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.completion_tasks.clear();
3600 this.discard_copilot_suggestion(cx);
3601 cx.notify();
3602 } else if this.completion_tasks.is_empty() {
3603 // If there are no more completion tasks and the last menu was
3604 // empty, we should hide it. If it was already hidden, we should
3605 // also show the copilot suggestion when available.
3606 drop(context_menu);
3607 if this.hide_context_menu(cx).is_none() {
3608 this.update_visible_copilot_suggestion(cx);
3609 }
3610 }
3611 })?;
3612
3613 Ok::<_, anyhow::Error>(())
3614 }
3615 .log_err()
3616 });
3617 self.completion_tasks.push((id, task));
3618 }
3619
3620 pub fn confirm_completion(
3621 &mut self,
3622 action: &ConfirmCompletion,
3623 cx: &mut ViewContext<Self>,
3624 ) -> Option<Task<Result<()>>> {
3625 use language::ToOffset as _;
3626
3627 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3628 menu
3629 } else {
3630 return None;
3631 };
3632
3633 let mat = completions_menu
3634 .matches
3635 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3636 let buffer_handle = completions_menu.buffer;
3637 let completions = completions_menu.completions.read();
3638 let completion = completions.get(mat.candidate_id)?;
3639
3640 let snippet;
3641 let text;
3642 if completion.is_snippet() {
3643 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3644 text = snippet.as_ref().unwrap().text.clone();
3645 } else {
3646 snippet = None;
3647 text = completion.new_text.clone();
3648 };
3649 let selections = self.selections.all::<usize>(cx);
3650 let buffer = buffer_handle.read(cx);
3651 let old_range = completion.old_range.to_offset(buffer);
3652 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3653
3654 let newest_selection = self.selections.newest_anchor();
3655 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3656 return None;
3657 }
3658
3659 let lookbehind = newest_selection
3660 .start
3661 .text_anchor
3662 .to_offset(buffer)
3663 .saturating_sub(old_range.start);
3664 let lookahead = old_range
3665 .end
3666 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3667 let mut common_prefix_len = old_text
3668 .bytes()
3669 .zip(text.bytes())
3670 .take_while(|(a, b)| a == b)
3671 .count();
3672
3673 let snapshot = self.buffer.read(cx).snapshot(cx);
3674 let mut range_to_replace: Option<Range<isize>> = None;
3675 let mut ranges = Vec::new();
3676 for selection in &selections {
3677 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3678 let start = selection.start.saturating_sub(lookbehind);
3679 let end = selection.end + lookahead;
3680 if selection.id == newest_selection.id {
3681 range_to_replace = Some(
3682 ((start + common_prefix_len) as isize - selection.start as isize)
3683 ..(end as isize - selection.start as isize),
3684 );
3685 }
3686 ranges.push(start + common_prefix_len..end);
3687 } else {
3688 common_prefix_len = 0;
3689 ranges.clear();
3690 ranges.extend(selections.iter().map(|s| {
3691 if s.id == newest_selection.id {
3692 range_to_replace = Some(
3693 old_range.start.to_offset_utf16(&snapshot).0 as isize
3694 - selection.start as isize
3695 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3696 - selection.start as isize,
3697 );
3698 old_range.clone()
3699 } else {
3700 s.start..s.end
3701 }
3702 }));
3703 break;
3704 }
3705 }
3706 let text = &text[common_prefix_len..];
3707
3708 cx.emit(Event::InputHandled {
3709 utf16_range_to_replace: range_to_replace,
3710 text: text.into(),
3711 });
3712
3713 self.transact(cx, |this, cx| {
3714 if let Some(mut snippet) = snippet {
3715 snippet.text = text.to_string();
3716 for tabstop in snippet.tabstops.iter_mut().flatten() {
3717 tabstop.start -= common_prefix_len as isize;
3718 tabstop.end -= common_prefix_len as isize;
3719 }
3720
3721 this.insert_snippet(&ranges, snippet, cx).log_err();
3722 } else {
3723 this.buffer.update(cx, |buffer, cx| {
3724 buffer.edit(
3725 ranges.iter().map(|range| (range.clone(), text)),
3726 this.autoindent_mode.clone(),
3727 cx,
3728 );
3729 });
3730 }
3731
3732 this.refresh_copilot_suggestions(true, cx);
3733 });
3734
3735 let project = self.project.clone()?;
3736 let apply_edits = project.update(cx, |project, cx| {
3737 project.apply_additional_edits_for_completion(
3738 buffer_handle,
3739 completion.clone(),
3740 true,
3741 cx,
3742 )
3743 });
3744 Some(cx.foreground().spawn(async move {
3745 apply_edits.await?;
3746 Ok(())
3747 }))
3748 }
3749
3750 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3751 let mut context_menu = self.context_menu.write();
3752 if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
3753 *context_menu = None;
3754 cx.notify();
3755 return;
3756 }
3757 drop(context_menu);
3758
3759 let deployed_from_indicator = action.deployed_from_indicator;
3760 let mut task = self.code_actions_task.take();
3761 cx.spawn(|this, mut cx| async move {
3762 while let Some(prev_task) = task {
3763 prev_task.await;
3764 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3765 }
3766
3767 this.update(&mut cx, |this, cx| {
3768 if this.focused {
3769 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3770 this.completion_tasks.clear();
3771 this.discard_copilot_suggestion(cx);
3772 *this.context_menu.write() =
3773 Some(ContextMenu::CodeActions(CodeActionsMenu {
3774 buffer,
3775 actions,
3776 selected_item: Default::default(),
3777 list: Default::default(),
3778 deployed_from_indicator,
3779 }));
3780 }
3781 }
3782 })?;
3783
3784 Ok::<_, anyhow::Error>(())
3785 })
3786 .detach_and_log_err(cx);
3787 }
3788
3789 pub fn confirm_code_action(
3790 workspace: &mut Workspace,
3791 action: &ConfirmCodeAction,
3792 cx: &mut ViewContext<Workspace>,
3793 ) -> Option<Task<Result<()>>> {
3794 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3795 let actions_menu = if let ContextMenu::CodeActions(menu) =
3796 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3797 {
3798 menu
3799 } else {
3800 return None;
3801 };
3802 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3803 let action = actions_menu.actions.get(action_ix)?.clone();
3804 let title = action.lsp_action.title.clone();
3805 let buffer = actions_menu.buffer;
3806
3807 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3808 project.apply_code_action(buffer, action, true, cx)
3809 });
3810 let editor = editor.downgrade();
3811 Some(cx.spawn(|workspace, cx| async move {
3812 let project_transaction = apply_code_actions.await?;
3813 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3814 }))
3815 }
3816
3817 async fn open_project_transaction(
3818 this: &WeakViewHandle<Editor>,
3819 workspace: WeakViewHandle<Workspace>,
3820 transaction: ProjectTransaction,
3821 title: String,
3822 mut cx: AsyncAppContext,
3823 ) -> Result<()> {
3824 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3825
3826 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3827 entries.sort_unstable_by_key(|(buffer, _)| {
3828 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3829 });
3830
3831 // If the project transaction's edits are all contained within this editor, then
3832 // avoid opening a new editor to display them.
3833
3834 if let Some((buffer, transaction)) = entries.first() {
3835 if entries.len() == 1 {
3836 let excerpt = this.read_with(&cx, |editor, cx| {
3837 editor
3838 .buffer()
3839 .read(cx)
3840 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3841 })?;
3842 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3843 if excerpted_buffer == *buffer {
3844 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3845 let excerpt_range = excerpt_range.to_offset(buffer);
3846 buffer
3847 .edited_ranges_for_transaction::<usize>(transaction)
3848 .all(|range| {
3849 excerpt_range.start <= range.start
3850 && excerpt_range.end >= range.end
3851 })
3852 });
3853
3854 if all_edits_within_excerpt {
3855 return Ok(());
3856 }
3857 }
3858 }
3859 }
3860 } else {
3861 return Ok(());
3862 }
3863
3864 let mut ranges_to_highlight = Vec::new();
3865 let excerpt_buffer = cx.add_model(|cx| {
3866 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3867 for (buffer_handle, transaction) in &entries {
3868 let buffer = buffer_handle.read(cx);
3869 ranges_to_highlight.extend(
3870 multibuffer.push_excerpts_with_context_lines(
3871 buffer_handle.clone(),
3872 buffer
3873 .edited_ranges_for_transaction::<usize>(transaction)
3874 .collect(),
3875 1,
3876 cx,
3877 ),
3878 );
3879 }
3880 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3881 multibuffer
3882 });
3883
3884 workspace.update(&mut cx, |workspace, cx| {
3885 let project = workspace.project().clone();
3886 let editor =
3887 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3888 workspace.add_item(Box::new(editor.clone()), cx);
3889 editor.update(cx, |editor, cx| {
3890 editor.highlight_background::<Self>(
3891 ranges_to_highlight,
3892 |theme| theme.editor.highlighted_line_background,
3893 cx,
3894 );
3895 });
3896 })?;
3897
3898 Ok(())
3899 }
3900
3901 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3902 let project = self.project.clone()?;
3903 let buffer = self.buffer.read(cx);
3904 let newest_selection = self.selections.newest_anchor().clone();
3905 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3906 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3907 if start_buffer != end_buffer {
3908 return None;
3909 }
3910
3911 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3912 cx.background().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await;
3913
3914 let actions = project
3915 .update(&mut cx, |project, cx| {
3916 project.code_actions(&start_buffer, start..end, cx)
3917 })
3918 .await;
3919
3920 this.update(&mut cx, |this, cx| {
3921 this.available_code_actions = actions.log_err().and_then(|actions| {
3922 if actions.is_empty() {
3923 None
3924 } else {
3925 Some((start_buffer, actions.into()))
3926 }
3927 });
3928 cx.notify();
3929 })
3930 .log_err();
3931 }));
3932 None
3933 }
3934
3935 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3936 if self.pending_rename.is_some() {
3937 return None;
3938 }
3939
3940 let project = self.project.clone()?;
3941 let buffer = self.buffer.read(cx);
3942 let newest_selection = self.selections.newest_anchor().clone();
3943 let cursor_position = newest_selection.head();
3944 let (cursor_buffer, cursor_buffer_position) =
3945 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3946 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3947 if cursor_buffer != tail_buffer {
3948 return None;
3949 }
3950
3951 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3952 cx.background()
3953 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3954 .await;
3955
3956 let highlights = project
3957 .update(&mut cx, |project, cx| {
3958 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3959 })
3960 .await
3961 .log_err();
3962
3963 if let Some(highlights) = highlights {
3964 this.update(&mut cx, |this, cx| {
3965 if this.pending_rename.is_some() {
3966 return;
3967 }
3968
3969 let buffer_id = cursor_position.buffer_id;
3970 let buffer = this.buffer.read(cx);
3971 if !buffer
3972 .text_anchor_for_position(cursor_position, cx)
3973 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3974 {
3975 return;
3976 }
3977
3978 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3979 let mut write_ranges = Vec::new();
3980 let mut read_ranges = Vec::new();
3981 for highlight in highlights {
3982 for (excerpt_id, excerpt_range) in
3983 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3984 {
3985 let start = highlight
3986 .range
3987 .start
3988 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3989 let end = highlight
3990 .range
3991 .end
3992 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3993 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3994 continue;
3995 }
3996
3997 let range = Anchor {
3998 buffer_id,
3999 excerpt_id: excerpt_id.clone(),
4000 text_anchor: start,
4001 }..Anchor {
4002 buffer_id,
4003 excerpt_id,
4004 text_anchor: end,
4005 };
4006 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4007 write_ranges.push(range);
4008 } else {
4009 read_ranges.push(range);
4010 }
4011 }
4012 }
4013
4014 this.highlight_background::<DocumentHighlightRead>(
4015 read_ranges,
4016 |theme| theme.editor.document_highlight_read_background,
4017 cx,
4018 );
4019 this.highlight_background::<DocumentHighlightWrite>(
4020 write_ranges,
4021 |theme| theme.editor.document_highlight_write_background,
4022 cx,
4023 );
4024 cx.notify();
4025 })
4026 .log_err();
4027 }
4028 }));
4029 None
4030 }
4031
4032 fn refresh_copilot_suggestions(
4033 &mut self,
4034 debounce: bool,
4035 cx: &mut ViewContext<Self>,
4036 ) -> Option<()> {
4037 let copilot = Copilot::global(cx)?;
4038 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4039 self.clear_copilot_suggestions(cx);
4040 return None;
4041 }
4042 self.update_visible_copilot_suggestion(cx);
4043
4044 let snapshot = self.buffer.read(cx).snapshot(cx);
4045 let cursor = self.selections.newest_anchor().head();
4046 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
4047 self.clear_copilot_suggestions(cx);
4048 return None;
4049 }
4050
4051 let (buffer, buffer_position) =
4052 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4053 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
4054 if debounce {
4055 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
4056 }
4057
4058 let completions = copilot
4059 .update(&mut cx, |copilot, cx| {
4060 copilot.completions(&buffer, buffer_position, cx)
4061 })
4062 .await
4063 .log_err()
4064 .into_iter()
4065 .flatten()
4066 .collect_vec();
4067
4068 this.update(&mut cx, |this, cx| {
4069 if !completions.is_empty() {
4070 this.copilot_state.cycled = false;
4071 this.copilot_state.pending_cycling_refresh = Task::ready(None);
4072 this.copilot_state.completions.clear();
4073 this.copilot_state.active_completion_index = 0;
4074 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
4075 for completion in completions {
4076 this.copilot_state.push_completion(completion);
4077 }
4078 this.update_visible_copilot_suggestion(cx);
4079 }
4080 })
4081 .log_err()?;
4082 Some(())
4083 });
4084
4085 Some(())
4086 }
4087
4088 fn cycle_copilot_suggestions(
4089 &mut self,
4090 direction: Direction,
4091 cx: &mut ViewContext<Self>,
4092 ) -> Option<()> {
4093 let copilot = Copilot::global(cx)?;
4094 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4095 return None;
4096 }
4097
4098 if self.copilot_state.cycled {
4099 self.copilot_state.cycle_completions(direction);
4100 self.update_visible_copilot_suggestion(cx);
4101 } else {
4102 let cursor = self.selections.newest_anchor().head();
4103 let (buffer, buffer_position) =
4104 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4105 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
4106 let completions = copilot
4107 .update(&mut cx, |copilot, cx| {
4108 copilot.completions_cycling(&buffer, buffer_position, cx)
4109 })
4110 .await;
4111
4112 this.update(&mut cx, |this, cx| {
4113 this.copilot_state.cycled = true;
4114 for completion in completions.log_err().into_iter().flatten() {
4115 this.copilot_state.push_completion(completion);
4116 }
4117 this.copilot_state.cycle_completions(direction);
4118 this.update_visible_copilot_suggestion(cx);
4119 })
4120 .log_err()?;
4121
4122 Some(())
4123 });
4124 }
4125
4126 Some(())
4127 }
4128
4129 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
4130 if !self.has_active_copilot_suggestion(cx) {
4131 self.refresh_copilot_suggestions(false, cx);
4132 return;
4133 }
4134
4135 self.update_visible_copilot_suggestion(cx);
4136 }
4137
4138 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
4139 if self.has_active_copilot_suggestion(cx) {
4140 self.cycle_copilot_suggestions(Direction::Next, cx);
4141 } else {
4142 self.refresh_copilot_suggestions(false, cx);
4143 }
4144 }
4145
4146 fn previous_copilot_suggestion(
4147 &mut self,
4148 _: &copilot::PreviousSuggestion,
4149 cx: &mut ViewContext<Self>,
4150 ) {
4151 if self.has_active_copilot_suggestion(cx) {
4152 self.cycle_copilot_suggestions(Direction::Prev, cx);
4153 } else {
4154 self.refresh_copilot_suggestions(false, cx);
4155 }
4156 }
4157
4158 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4159 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4160 if let Some((copilot, completion)) =
4161 Copilot::global(cx).zip(self.copilot_state.active_completion())
4162 {
4163 copilot
4164 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
4165 .detach_and_log_err(cx);
4166
4167 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
4168 }
4169 cx.emit(Event::InputHandled {
4170 utf16_range_to_replace: None,
4171 text: suggestion.text.to_string().into(),
4172 });
4173 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
4174 cx.notify();
4175 true
4176 } else {
4177 false
4178 }
4179 }
4180
4181 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4182 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4183 if let Some(copilot) = Copilot::global(cx) {
4184 copilot
4185 .update(cx, |copilot, cx| {
4186 copilot.discard_completions(&self.copilot_state.completions, cx)
4187 })
4188 .detach_and_log_err(cx);
4189
4190 self.report_copilot_event(None, false, cx)
4191 }
4192
4193 self.display_map.update(cx, |map, cx| {
4194 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
4195 });
4196 cx.notify();
4197 true
4198 } else {
4199 false
4200 }
4201 }
4202
4203 fn is_copilot_enabled_at(
4204 &self,
4205 location: Anchor,
4206 snapshot: &MultiBufferSnapshot,
4207 cx: &mut ViewContext<Self>,
4208 ) -> bool {
4209 let file = snapshot.file_at(location);
4210 let language = snapshot.language_at(location);
4211 let settings = all_language_settings(file, cx);
4212 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
4213 }
4214
4215 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
4216 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
4217 let buffer = self.buffer.read(cx).read(cx);
4218 suggestion.position.is_valid(&buffer)
4219 } else {
4220 false
4221 }
4222 }
4223
4224 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4225 let suggestion = self.copilot_state.suggestion.take()?;
4226 self.display_map.update(cx, |map, cx| {
4227 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
4228 });
4229 let buffer = self.buffer.read(cx).read(cx);
4230
4231 if suggestion.position.is_valid(&buffer) {
4232 Some(suggestion)
4233 } else {
4234 None
4235 }
4236 }
4237
4238 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
4239 let snapshot = self.buffer.read(cx).snapshot(cx);
4240 let selection = self.selections.newest_anchor();
4241 let cursor = selection.head();
4242
4243 if self.context_menu.read().is_some()
4244 || !self.completion_tasks.is_empty()
4245 || selection.start != selection.end
4246 {
4247 self.discard_copilot_suggestion(cx);
4248 } else if let Some(text) = self
4249 .copilot_state
4250 .text_for_active_completion(cursor, &snapshot)
4251 {
4252 let text = Rope::from(text);
4253 let mut to_remove = Vec::new();
4254 if let Some(suggestion) = self.copilot_state.suggestion.take() {
4255 to_remove.push(suggestion.id);
4256 }
4257
4258 let suggestion_inlay =
4259 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4260 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
4261 self.display_map.update(cx, move |map, cx| {
4262 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
4263 });
4264 cx.notify();
4265 } else {
4266 self.discard_copilot_suggestion(cx);
4267 }
4268 }
4269
4270 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
4271 self.copilot_state = Default::default();
4272 self.discard_copilot_suggestion(cx);
4273 }
4274
4275 pub fn render_code_actions_indicator(
4276 &self,
4277 style: &EditorStyle,
4278 is_active: bool,
4279 cx: &mut ViewContext<Self>,
4280 ) -> Option<AnyElement<Self>> {
4281 if self.available_code_actions.is_some() {
4282 enum CodeActions {}
4283 Some(
4284 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
4285 Svg::new("icons/bolt.svg").with_color(
4286 style
4287 .code_actions
4288 .indicator
4289 .in_state(is_active)
4290 .style_for(state)
4291 .color,
4292 )
4293 })
4294 .with_cursor_style(CursorStyle::PointingHand)
4295 .with_padding(Padding::uniform(3.))
4296 .on_down(MouseButton::Left, |_, this, cx| {
4297 this.toggle_code_actions(
4298 &ToggleCodeActions {
4299 deployed_from_indicator: true,
4300 },
4301 cx,
4302 );
4303 })
4304 .into_any(),
4305 )
4306 } else {
4307 None
4308 }
4309 }
4310
4311 pub fn render_fold_indicators(
4312 &self,
4313 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
4314 style: &EditorStyle,
4315 gutter_hovered: bool,
4316 line_height: f32,
4317 gutter_margin: f32,
4318 cx: &mut ViewContext<Self>,
4319 ) -> Vec<Option<AnyElement<Self>>> {
4320 enum FoldIndicators {}
4321
4322 let style = style.folds.clone();
4323
4324 fold_data
4325 .iter()
4326 .enumerate()
4327 .map(|(ix, fold_data)| {
4328 fold_data
4329 .map(|(fold_status, buffer_row, active)| {
4330 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4331 MouseEventHandler::new::<FoldIndicators, _>(
4332 ix as usize,
4333 cx,
4334 |mouse_state, _| {
4335 Svg::new(match fold_status {
4336 FoldStatus::Folded => style.folded_icon.clone(),
4337 FoldStatus::Foldable => style.foldable_icon.clone(),
4338 })
4339 .with_color(
4340 style
4341 .indicator
4342 .in_state(fold_status == FoldStatus::Folded)
4343 .style_for(mouse_state)
4344 .color,
4345 )
4346 .constrained()
4347 .with_width(gutter_margin * style.icon_margin_scale)
4348 .aligned()
4349 .constrained()
4350 .with_height(line_height)
4351 .with_width(gutter_margin)
4352 .aligned()
4353 },
4354 )
4355 .with_cursor_style(CursorStyle::PointingHand)
4356 .with_padding(Padding::uniform(3.))
4357 .on_click(MouseButton::Left, {
4358 move |_, editor, cx| match fold_status {
4359 FoldStatus::Folded => {
4360 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
4361 }
4362 FoldStatus::Foldable => {
4363 editor.fold_at(&FoldAt { buffer_row }, cx);
4364 }
4365 }
4366 })
4367 .into_any()
4368 })
4369 })
4370 .flatten()
4371 })
4372 .collect()
4373 }
4374
4375 pub fn context_menu_visible(&self) -> bool {
4376 self.context_menu
4377 .read()
4378 .as_ref()
4379 .map_or(false, |menu| menu.visible())
4380 }
4381
4382 pub fn render_context_menu(
4383 &self,
4384 cursor_position: DisplayPoint,
4385 style: EditorStyle,
4386 cx: &mut ViewContext<Editor>,
4387 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
4388 self.context_menu.read().as_ref().map(|menu| {
4389 menu.render(
4390 cursor_position,
4391 style,
4392 self.workspace.as_ref().map(|(w, _)| w.clone()),
4393 cx,
4394 )
4395 })
4396 }
4397
4398 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4399 cx.notify();
4400 self.completion_tasks.clear();
4401 let context_menu = self.context_menu.write().take();
4402 if context_menu.is_some() {
4403 self.update_visible_copilot_suggestion(cx);
4404 }
4405 context_menu
4406 }
4407
4408 pub fn insert_snippet(
4409 &mut self,
4410 insertion_ranges: &[Range<usize>],
4411 snippet: Snippet,
4412 cx: &mut ViewContext<Self>,
4413 ) -> Result<()> {
4414 let tabstops = self.buffer.update(cx, |buffer, cx| {
4415 let snippet_text: Arc<str> = snippet.text.clone().into();
4416 buffer.edit(
4417 insertion_ranges
4418 .iter()
4419 .cloned()
4420 .map(|range| (range, snippet_text.clone())),
4421 Some(AutoindentMode::EachLine),
4422 cx,
4423 );
4424
4425 let snapshot = &*buffer.read(cx);
4426 let snippet = &snippet;
4427 snippet
4428 .tabstops
4429 .iter()
4430 .map(|tabstop| {
4431 let mut tabstop_ranges = tabstop
4432 .iter()
4433 .flat_map(|tabstop_range| {
4434 let mut delta = 0_isize;
4435 insertion_ranges.iter().map(move |insertion_range| {
4436 let insertion_start = insertion_range.start as isize + delta;
4437 delta +=
4438 snippet.text.len() as isize - insertion_range.len() as isize;
4439
4440 let start = snapshot.anchor_before(
4441 (insertion_start + tabstop_range.start) as usize,
4442 );
4443 let end = snapshot
4444 .anchor_after((insertion_start + tabstop_range.end) as usize);
4445 start..end
4446 })
4447 })
4448 .collect::<Vec<_>>();
4449 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4450 tabstop_ranges
4451 })
4452 .collect::<Vec<_>>()
4453 });
4454
4455 if let Some(tabstop) = tabstops.first() {
4456 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4457 s.select_ranges(tabstop.iter().cloned());
4458 });
4459 self.snippet_stack.push(SnippetState {
4460 active_index: 0,
4461 ranges: tabstops,
4462 });
4463 }
4464
4465 Ok(())
4466 }
4467
4468 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4469 self.move_to_snippet_tabstop(Bias::Right, cx)
4470 }
4471
4472 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4473 self.move_to_snippet_tabstop(Bias::Left, cx)
4474 }
4475
4476 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4477 if let Some(mut snippet) = self.snippet_stack.pop() {
4478 match bias {
4479 Bias::Left => {
4480 if snippet.active_index > 0 {
4481 snippet.active_index -= 1;
4482 } else {
4483 self.snippet_stack.push(snippet);
4484 return false;
4485 }
4486 }
4487 Bias::Right => {
4488 if snippet.active_index + 1 < snippet.ranges.len() {
4489 snippet.active_index += 1;
4490 } else {
4491 self.snippet_stack.push(snippet);
4492 return false;
4493 }
4494 }
4495 }
4496 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4497 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4498 s.select_anchor_ranges(current_ranges.iter().cloned())
4499 });
4500 // If snippet state is not at the last tabstop, push it back on the stack
4501 if snippet.active_index + 1 < snippet.ranges.len() {
4502 self.snippet_stack.push(snippet);
4503 }
4504 return true;
4505 }
4506 }
4507
4508 false
4509 }
4510
4511 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4512 self.transact(cx, |this, cx| {
4513 this.select_all(&SelectAll, cx);
4514 this.insert("", cx);
4515 });
4516 }
4517
4518 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4519 self.transact(cx, |this, cx| {
4520 this.select_autoclose_pair(cx);
4521 let mut selections = this.selections.all::<Point>(cx);
4522 if !this.selections.line_mode {
4523 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4524 for selection in &mut selections {
4525 if selection.is_empty() {
4526 let old_head = selection.head();
4527 let mut new_head =
4528 movement::left(&display_map, old_head.to_display_point(&display_map))
4529 .to_point(&display_map);
4530 if let Some((buffer, line_buffer_range)) = display_map
4531 .buffer_snapshot
4532 .buffer_line_for_row(old_head.row)
4533 {
4534 let indent_size =
4535 buffer.indent_size_for_line(line_buffer_range.start.row);
4536 let indent_len = match indent_size.kind {
4537 IndentKind::Space => {
4538 buffer.settings_at(line_buffer_range.start, cx).tab_size
4539 }
4540 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4541 };
4542 if old_head.column <= indent_size.len && old_head.column > 0 {
4543 let indent_len = indent_len.get();
4544 new_head = cmp::min(
4545 new_head,
4546 Point::new(
4547 old_head.row,
4548 ((old_head.column - 1) / indent_len) * indent_len,
4549 ),
4550 );
4551 }
4552 }
4553
4554 selection.set_head(new_head, SelectionGoal::None);
4555 }
4556 }
4557 }
4558
4559 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4560 this.insert("", cx);
4561 this.refresh_copilot_suggestions(true, cx);
4562 });
4563 }
4564
4565 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4566 self.transact(cx, |this, cx| {
4567 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4568 let line_mode = s.line_mode;
4569 s.move_with(|map, selection| {
4570 if selection.is_empty() && !line_mode {
4571 let cursor = movement::right(map, selection.head());
4572 selection.end = cursor;
4573 selection.reversed = true;
4574 selection.goal = SelectionGoal::None;
4575 }
4576 })
4577 });
4578 this.insert("", cx);
4579 this.refresh_copilot_suggestions(true, cx);
4580 });
4581 }
4582
4583 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4584 if self.move_to_prev_snippet_tabstop(cx) {
4585 return;
4586 }
4587
4588 self.outdent(&Outdent, cx);
4589 }
4590
4591 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4592 if self.move_to_next_snippet_tabstop(cx) {
4593 return;
4594 }
4595
4596 let mut selections = self.selections.all_adjusted(cx);
4597 let buffer = self.buffer.read(cx);
4598 let snapshot = buffer.snapshot(cx);
4599 let rows_iter = selections.iter().map(|s| s.head().row);
4600 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4601
4602 let mut edits = Vec::new();
4603 let mut prev_edited_row = 0;
4604 let mut row_delta = 0;
4605 for selection in &mut selections {
4606 if selection.start.row != prev_edited_row {
4607 row_delta = 0;
4608 }
4609 prev_edited_row = selection.end.row;
4610
4611 // If the selection is non-empty, then increase the indentation of the selected lines.
4612 if !selection.is_empty() {
4613 row_delta =
4614 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4615 continue;
4616 }
4617
4618 // If the selection is empty and the cursor is in the leading whitespace before the
4619 // suggested indentation, then auto-indent the line.
4620 let cursor = selection.head();
4621 let current_indent = snapshot.indent_size_for_line(cursor.row);
4622 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4623 if cursor.column < suggested_indent.len
4624 && cursor.column <= current_indent.len
4625 && current_indent.len <= suggested_indent.len
4626 {
4627 selection.start = Point::new(cursor.row, suggested_indent.len);
4628 selection.end = selection.start;
4629 if row_delta == 0 {
4630 edits.extend(Buffer::edit_for_indent_size_adjustment(
4631 cursor.row,
4632 current_indent,
4633 suggested_indent,
4634 ));
4635 row_delta = suggested_indent.len - current_indent.len;
4636 }
4637 continue;
4638 }
4639 }
4640
4641 // Accept copilot suggestion if there is only one selection and the cursor is not
4642 // in the leading whitespace.
4643 if self.selections.count() == 1
4644 && cursor.column >= current_indent.len
4645 && self.has_active_copilot_suggestion(cx)
4646 {
4647 self.accept_copilot_suggestion(cx);
4648 return;
4649 }
4650
4651 // Otherwise, insert a hard or soft tab.
4652 let settings = buffer.settings_at(cursor, cx);
4653 let tab_size = if settings.hard_tabs {
4654 IndentSize::tab()
4655 } else {
4656 let tab_size = settings.tab_size.get();
4657 let char_column = snapshot
4658 .text_for_range(Point::new(cursor.row, 0)..cursor)
4659 .flat_map(str::chars)
4660 .count()
4661 + row_delta as usize;
4662 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4663 IndentSize::spaces(chars_to_next_tab_stop)
4664 };
4665 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4666 selection.end = selection.start;
4667 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4668 row_delta += tab_size.len;
4669 }
4670
4671 self.transact(cx, |this, cx| {
4672 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4673 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4674 this.refresh_copilot_suggestions(true, cx);
4675 });
4676 }
4677
4678 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4679 let mut selections = self.selections.all::<Point>(cx);
4680 let mut prev_edited_row = 0;
4681 let mut row_delta = 0;
4682 let mut edits = Vec::new();
4683 let buffer = self.buffer.read(cx);
4684 let snapshot = buffer.snapshot(cx);
4685 for selection in &mut selections {
4686 if selection.start.row != prev_edited_row {
4687 row_delta = 0;
4688 }
4689 prev_edited_row = selection.end.row;
4690
4691 row_delta =
4692 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4693 }
4694
4695 self.transact(cx, |this, cx| {
4696 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4697 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4698 });
4699 }
4700
4701 fn indent_selection(
4702 buffer: &MultiBuffer,
4703 snapshot: &MultiBufferSnapshot,
4704 selection: &mut Selection<Point>,
4705 edits: &mut Vec<(Range<Point>, String)>,
4706 delta_for_start_row: u32,
4707 cx: &AppContext,
4708 ) -> u32 {
4709 let settings = buffer.settings_at(selection.start, cx);
4710 let tab_size = settings.tab_size.get();
4711 let indent_kind = if settings.hard_tabs {
4712 IndentKind::Tab
4713 } else {
4714 IndentKind::Space
4715 };
4716 let mut start_row = selection.start.row;
4717 let mut end_row = selection.end.row + 1;
4718
4719 // If a selection ends at the beginning of a line, don't indent
4720 // that last line.
4721 if selection.end.column == 0 {
4722 end_row -= 1;
4723 }
4724
4725 // Avoid re-indenting a row that has already been indented by a
4726 // previous selection, but still update this selection's column
4727 // to reflect that indentation.
4728 if delta_for_start_row > 0 {
4729 start_row += 1;
4730 selection.start.column += delta_for_start_row;
4731 if selection.end.row == selection.start.row {
4732 selection.end.column += delta_for_start_row;
4733 }
4734 }
4735
4736 let mut delta_for_end_row = 0;
4737 for row in start_row..end_row {
4738 let current_indent = snapshot.indent_size_for_line(row);
4739 let indent_delta = match (current_indent.kind, indent_kind) {
4740 (IndentKind::Space, IndentKind::Space) => {
4741 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4742 IndentSize::spaces(columns_to_next_tab_stop)
4743 }
4744 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4745 (_, IndentKind::Tab) => IndentSize::tab(),
4746 };
4747
4748 let row_start = Point::new(row, 0);
4749 edits.push((
4750 row_start..row_start,
4751 indent_delta.chars().collect::<String>(),
4752 ));
4753
4754 // Update this selection's endpoints to reflect the indentation.
4755 if row == selection.start.row {
4756 selection.start.column += indent_delta.len;
4757 }
4758 if row == selection.end.row {
4759 selection.end.column += indent_delta.len;
4760 delta_for_end_row = indent_delta.len;
4761 }
4762 }
4763
4764 if selection.start.row == selection.end.row {
4765 delta_for_start_row + delta_for_end_row
4766 } else {
4767 delta_for_end_row
4768 }
4769 }
4770
4771 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4772 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4773 let selections = self.selections.all::<Point>(cx);
4774 let mut deletion_ranges = Vec::new();
4775 let mut last_outdent = None;
4776 {
4777 let buffer = self.buffer.read(cx);
4778 let snapshot = buffer.snapshot(cx);
4779 for selection in &selections {
4780 let settings = buffer.settings_at(selection.start, cx);
4781 let tab_size = settings.tab_size.get();
4782 let mut rows = selection.spanned_rows(false, &display_map);
4783
4784 // Avoid re-outdenting a row that has already been outdented by a
4785 // previous selection.
4786 if let Some(last_row) = last_outdent {
4787 if last_row == rows.start {
4788 rows.start += 1;
4789 }
4790 }
4791
4792 for row in rows {
4793 let indent_size = snapshot.indent_size_for_line(row);
4794 if indent_size.len > 0 {
4795 let deletion_len = match indent_size.kind {
4796 IndentKind::Space => {
4797 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4798 if columns_to_prev_tab_stop == 0 {
4799 tab_size
4800 } else {
4801 columns_to_prev_tab_stop
4802 }
4803 }
4804 IndentKind::Tab => 1,
4805 };
4806 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4807 last_outdent = Some(row);
4808 }
4809 }
4810 }
4811 }
4812
4813 self.transact(cx, |this, cx| {
4814 this.buffer.update(cx, |buffer, cx| {
4815 let empty_str: Arc<str> = "".into();
4816 buffer.edit(
4817 deletion_ranges
4818 .into_iter()
4819 .map(|range| (range, empty_str.clone())),
4820 None,
4821 cx,
4822 );
4823 });
4824 let selections = this.selections.all::<usize>(cx);
4825 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4826 });
4827 }
4828
4829 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4830 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4831 let selections = self.selections.all::<Point>(cx);
4832
4833 let mut new_cursors = Vec::new();
4834 let mut edit_ranges = Vec::new();
4835 let mut selections = selections.iter().peekable();
4836 while let Some(selection) = selections.next() {
4837 let mut rows = selection.spanned_rows(false, &display_map);
4838 let goal_display_column = selection.head().to_display_point(&display_map).column();
4839
4840 // Accumulate contiguous regions of rows that we want to delete.
4841 while let Some(next_selection) = selections.peek() {
4842 let next_rows = next_selection.spanned_rows(false, &display_map);
4843 if next_rows.start <= rows.end {
4844 rows.end = next_rows.end;
4845 selections.next().unwrap();
4846 } else {
4847 break;
4848 }
4849 }
4850
4851 let buffer = &display_map.buffer_snapshot;
4852 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4853 let edit_end;
4854 let cursor_buffer_row;
4855 if buffer.max_point().row >= rows.end {
4856 // If there's a line after the range, delete the \n from the end of the row range
4857 // and position the cursor on the next line.
4858 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4859 cursor_buffer_row = rows.end;
4860 } else {
4861 // If there isn't a line after the range, delete the \n from the line before the
4862 // start of the row range and position the cursor there.
4863 edit_start = edit_start.saturating_sub(1);
4864 edit_end = buffer.len();
4865 cursor_buffer_row = rows.start.saturating_sub(1);
4866 }
4867
4868 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4869 *cursor.column_mut() =
4870 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4871
4872 new_cursors.push((
4873 selection.id,
4874 buffer.anchor_after(cursor.to_point(&display_map)),
4875 ));
4876 edit_ranges.push(edit_start..edit_end);
4877 }
4878
4879 self.transact(cx, |this, cx| {
4880 let buffer = this.buffer.update(cx, |buffer, cx| {
4881 let empty_str: Arc<str> = "".into();
4882 buffer.edit(
4883 edit_ranges
4884 .into_iter()
4885 .map(|range| (range, empty_str.clone())),
4886 None,
4887 cx,
4888 );
4889 buffer.snapshot(cx)
4890 });
4891 let new_selections = new_cursors
4892 .into_iter()
4893 .map(|(id, cursor)| {
4894 let cursor = cursor.to_point(&buffer);
4895 Selection {
4896 id,
4897 start: cursor,
4898 end: cursor,
4899 reversed: false,
4900 goal: SelectionGoal::None,
4901 }
4902 })
4903 .collect();
4904
4905 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4906 s.select(new_selections);
4907 });
4908 });
4909 }
4910
4911 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4912 let mut row_ranges = Vec::<Range<u32>>::new();
4913 for selection in self.selections.all::<Point>(cx) {
4914 let start = selection.start.row;
4915 let end = if selection.start.row == selection.end.row {
4916 selection.start.row + 1
4917 } else {
4918 selection.end.row
4919 };
4920
4921 if let Some(last_row_range) = row_ranges.last_mut() {
4922 if start <= last_row_range.end {
4923 last_row_range.end = end;
4924 continue;
4925 }
4926 }
4927 row_ranges.push(start..end);
4928 }
4929
4930 let snapshot = self.buffer.read(cx).snapshot(cx);
4931 let mut cursor_positions = Vec::new();
4932 for row_range in &row_ranges {
4933 let anchor = snapshot.anchor_before(Point::new(
4934 row_range.end - 1,
4935 snapshot.line_len(row_range.end - 1),
4936 ));
4937 cursor_positions.push(anchor.clone()..anchor);
4938 }
4939
4940 self.transact(cx, |this, cx| {
4941 for row_range in row_ranges.into_iter().rev() {
4942 for row in row_range.rev() {
4943 let end_of_line = Point::new(row, snapshot.line_len(row));
4944 let indent = snapshot.indent_size_for_line(row + 1);
4945 let start_of_next_line = Point::new(row + 1, indent.len);
4946
4947 let replace = if snapshot.line_len(row + 1) > indent.len {
4948 " "
4949 } else {
4950 ""
4951 };
4952
4953 this.buffer.update(cx, |buffer, cx| {
4954 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4955 });
4956 }
4957 }
4958
4959 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4960 s.select_anchor_ranges(cursor_positions)
4961 });
4962 });
4963 }
4964
4965 pub fn sort_lines_case_sensitive(
4966 &mut self,
4967 _: &SortLinesCaseSensitive,
4968 cx: &mut ViewContext<Self>,
4969 ) {
4970 self.manipulate_lines(cx, |lines| lines.sort())
4971 }
4972
4973 pub fn sort_lines_case_insensitive(
4974 &mut self,
4975 _: &SortLinesCaseInsensitive,
4976 cx: &mut ViewContext<Self>,
4977 ) {
4978 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4979 }
4980
4981 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4982 self.manipulate_lines(cx, |lines| lines.reverse())
4983 }
4984
4985 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4986 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4987 }
4988
4989 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4990 where
4991 Fn: FnMut(&mut [&str]),
4992 {
4993 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4994 let buffer = self.buffer.read(cx).snapshot(cx);
4995
4996 let mut edits = Vec::new();
4997
4998 let selections = self.selections.all::<Point>(cx);
4999 let mut selections = selections.iter().peekable();
5000 let mut contiguous_row_selections = Vec::new();
5001 let mut new_selections = Vec::new();
5002
5003 while let Some(selection) = selections.next() {
5004 let (start_row, end_row) = consume_contiguous_rows(
5005 &mut contiguous_row_selections,
5006 selection,
5007 &display_map,
5008 &mut selections,
5009 );
5010
5011 let start_point = Point::new(start_row, 0);
5012 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
5013 let text = buffer
5014 .text_for_range(start_point..end_point)
5015 .collect::<String>();
5016 let mut lines = text.split("\n").collect_vec();
5017
5018 let lines_len = lines.len();
5019 callback(&mut lines);
5020
5021 // This is a current limitation with selections.
5022 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
5023 debug_assert!(
5024 lines.len() == lines_len,
5025 "callback should not change the number of lines"
5026 );
5027
5028 edits.push((start_point..end_point, lines.join("\n")));
5029 let start_anchor = buffer.anchor_after(start_point);
5030 let end_anchor = buffer.anchor_before(end_point);
5031
5032 // Make selection and push
5033 new_selections.push(Selection {
5034 id: selection.id,
5035 start: start_anchor.to_offset(&buffer),
5036 end: end_anchor.to_offset(&buffer),
5037 goal: SelectionGoal::None,
5038 reversed: selection.reversed,
5039 });
5040 }
5041
5042 self.transact(cx, |this, cx| {
5043 this.buffer.update(cx, |buffer, cx| {
5044 buffer.edit(edits, None, cx);
5045 });
5046
5047 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5048 s.select(new_selections);
5049 });
5050
5051 this.request_autoscroll(Autoscroll::fit(), cx);
5052 });
5053 }
5054
5055 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5056 self.manipulate_text(cx, |text| text.to_uppercase())
5057 }
5058
5059 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5060 self.manipulate_text(cx, |text| text.to_lowercase())
5061 }
5062
5063 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5064 self.manipulate_text(cx, |text| {
5065 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5066 // https://github.com/rutrum/convert-case/issues/16
5067 text.split("\n")
5068 .map(|line| line.to_case(Case::Title))
5069 .join("\n")
5070 })
5071 }
5072
5073 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5074 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5075 }
5076
5077 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5078 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5079 }
5080
5081 pub fn convert_to_upper_camel_case(
5082 &mut self,
5083 _: &ConvertToUpperCamelCase,
5084 cx: &mut ViewContext<Self>,
5085 ) {
5086 self.manipulate_text(cx, |text| {
5087 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5088 // https://github.com/rutrum/convert-case/issues/16
5089 text.split("\n")
5090 .map(|line| line.to_case(Case::UpperCamel))
5091 .join("\n")
5092 })
5093 }
5094
5095 pub fn convert_to_lower_camel_case(
5096 &mut self,
5097 _: &ConvertToLowerCamelCase,
5098 cx: &mut ViewContext<Self>,
5099 ) {
5100 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5101 }
5102
5103 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5104 where
5105 Fn: FnMut(&str) -> String,
5106 {
5107 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5108 let buffer = self.buffer.read(cx).snapshot(cx);
5109
5110 let mut new_selections = Vec::new();
5111 let mut edits = Vec::new();
5112 let mut selection_adjustment = 0i32;
5113
5114 for selection in self.selections.all::<usize>(cx) {
5115 let selection_is_empty = selection.is_empty();
5116
5117 let (start, end) = if selection_is_empty {
5118 let word_range = movement::surrounding_word(
5119 &display_map,
5120 selection.start.to_display_point(&display_map),
5121 );
5122 let start = word_range.start.to_offset(&display_map, Bias::Left);
5123 let end = word_range.end.to_offset(&display_map, Bias::Left);
5124 (start, end)
5125 } else {
5126 (selection.start, selection.end)
5127 };
5128
5129 let text = buffer.text_for_range(start..end).collect::<String>();
5130 let old_length = text.len() as i32;
5131 let text = callback(&text);
5132
5133 new_selections.push(Selection {
5134 start: (start as i32 - selection_adjustment) as usize,
5135 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
5136 goal: SelectionGoal::None,
5137 ..selection
5138 });
5139
5140 selection_adjustment += old_length - text.len() as i32;
5141
5142 edits.push((start..end, text));
5143 }
5144
5145 self.transact(cx, |this, cx| {
5146 this.buffer.update(cx, |buffer, cx| {
5147 buffer.edit(edits, None, cx);
5148 });
5149
5150 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5151 s.select(new_selections);
5152 });
5153
5154 this.request_autoscroll(Autoscroll::fit(), cx);
5155 });
5156 }
5157
5158 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
5159 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5160 let buffer = &display_map.buffer_snapshot;
5161 let selections = self.selections.all::<Point>(cx);
5162
5163 let mut edits = Vec::new();
5164 let mut selections_iter = selections.iter().peekable();
5165 while let Some(selection) = selections_iter.next() {
5166 // Avoid duplicating the same lines twice.
5167 let mut rows = selection.spanned_rows(false, &display_map);
5168
5169 while let Some(next_selection) = selections_iter.peek() {
5170 let next_rows = next_selection.spanned_rows(false, &display_map);
5171 if next_rows.start < rows.end {
5172 rows.end = next_rows.end;
5173 selections_iter.next().unwrap();
5174 } else {
5175 break;
5176 }
5177 }
5178
5179 // Copy the text from the selected row region and splice it at the start of the region.
5180 let start = Point::new(rows.start, 0);
5181 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
5182 let text = buffer
5183 .text_for_range(start..end)
5184 .chain(Some("\n"))
5185 .collect::<String>();
5186 edits.push((start..start, text));
5187 }
5188
5189 self.transact(cx, |this, cx| {
5190 this.buffer.update(cx, |buffer, cx| {
5191 buffer.edit(edits, None, cx);
5192 });
5193
5194 this.request_autoscroll(Autoscroll::fit(), cx);
5195 });
5196 }
5197
5198 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5199 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5200 let buffer = self.buffer.read(cx).snapshot(cx);
5201
5202 let mut edits = Vec::new();
5203 let mut unfold_ranges = Vec::new();
5204 let mut refold_ranges = Vec::new();
5205
5206 let selections = self.selections.all::<Point>(cx);
5207 let mut selections = selections.iter().peekable();
5208 let mut contiguous_row_selections = Vec::new();
5209 let mut new_selections = Vec::new();
5210
5211 while let Some(selection) = selections.next() {
5212 // Find all the selections that span a contiguous row range
5213 let (start_row, end_row) = consume_contiguous_rows(
5214 &mut contiguous_row_selections,
5215 selection,
5216 &display_map,
5217 &mut selections,
5218 );
5219
5220 // Move the text spanned by the row range to be before the line preceding the row range
5221 if start_row > 0 {
5222 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
5223 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
5224 let insertion_point = display_map
5225 .prev_line_boundary(Point::new(start_row - 1, 0))
5226 .0;
5227
5228 // Don't move lines across excerpts
5229 if buffer
5230 .excerpt_boundaries_in_range((
5231 Bound::Excluded(insertion_point),
5232 Bound::Included(range_to_move.end),
5233 ))
5234 .next()
5235 .is_none()
5236 {
5237 let text = buffer
5238 .text_for_range(range_to_move.clone())
5239 .flat_map(|s| s.chars())
5240 .skip(1)
5241 .chain(['\n'])
5242 .collect::<String>();
5243
5244 edits.push((
5245 buffer.anchor_after(range_to_move.start)
5246 ..buffer.anchor_before(range_to_move.end),
5247 String::new(),
5248 ));
5249 let insertion_anchor = buffer.anchor_after(insertion_point);
5250 edits.push((insertion_anchor..insertion_anchor, text));
5251
5252 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5253
5254 // Move selections up
5255 new_selections.extend(contiguous_row_selections.drain(..).map(
5256 |mut selection| {
5257 selection.start.row -= row_delta;
5258 selection.end.row -= row_delta;
5259 selection
5260 },
5261 ));
5262
5263 // Move folds up
5264 unfold_ranges.push(range_to_move.clone());
5265 for fold in display_map.folds_in_range(
5266 buffer.anchor_before(range_to_move.start)
5267 ..buffer.anchor_after(range_to_move.end),
5268 ) {
5269 let mut start = fold.start.to_point(&buffer);
5270 let mut end = fold.end.to_point(&buffer);
5271 start.row -= row_delta;
5272 end.row -= row_delta;
5273 refold_ranges.push(start..end);
5274 }
5275 }
5276 }
5277
5278 // If we didn't move line(s), preserve the existing selections
5279 new_selections.append(&mut contiguous_row_selections);
5280 }
5281
5282 self.transact(cx, |this, cx| {
5283 this.unfold_ranges(unfold_ranges, true, true, cx);
5284 this.buffer.update(cx, |buffer, cx| {
5285 for (range, text) in edits {
5286 buffer.edit([(range, text)], None, cx);
5287 }
5288 });
5289 this.fold_ranges(refold_ranges, true, cx);
5290 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5291 s.select(new_selections);
5292 })
5293 });
5294 }
5295
5296 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5297 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5298 let buffer = self.buffer.read(cx).snapshot(cx);
5299
5300 let mut edits = Vec::new();
5301 let mut unfold_ranges = Vec::new();
5302 let mut refold_ranges = Vec::new();
5303
5304 let selections = self.selections.all::<Point>(cx);
5305 let mut selections = selections.iter().peekable();
5306 let mut contiguous_row_selections = Vec::new();
5307 let mut new_selections = Vec::new();
5308
5309 while let Some(selection) = selections.next() {
5310 // Find all the selections that span a contiguous row range
5311 let (start_row, end_row) = consume_contiguous_rows(
5312 &mut contiguous_row_selections,
5313 selection,
5314 &display_map,
5315 &mut selections,
5316 );
5317
5318 // Move the text spanned by the row range to be after the last line of the row range
5319 if end_row <= buffer.max_point().row {
5320 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
5321 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
5322
5323 // Don't move lines across excerpt boundaries
5324 if buffer
5325 .excerpt_boundaries_in_range((
5326 Bound::Excluded(range_to_move.start),
5327 Bound::Included(insertion_point),
5328 ))
5329 .next()
5330 .is_none()
5331 {
5332 let mut text = String::from("\n");
5333 text.extend(buffer.text_for_range(range_to_move.clone()));
5334 text.pop(); // Drop trailing newline
5335 edits.push((
5336 buffer.anchor_after(range_to_move.start)
5337 ..buffer.anchor_before(range_to_move.end),
5338 String::new(),
5339 ));
5340 let insertion_anchor = buffer.anchor_after(insertion_point);
5341 edits.push((insertion_anchor..insertion_anchor, text));
5342
5343 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5344
5345 // Move selections down
5346 new_selections.extend(contiguous_row_selections.drain(..).map(
5347 |mut selection| {
5348 selection.start.row += row_delta;
5349 selection.end.row += row_delta;
5350 selection
5351 },
5352 ));
5353
5354 // Move folds down
5355 unfold_ranges.push(range_to_move.clone());
5356 for fold in display_map.folds_in_range(
5357 buffer.anchor_before(range_to_move.start)
5358 ..buffer.anchor_after(range_to_move.end),
5359 ) {
5360 let mut start = fold.start.to_point(&buffer);
5361 let mut end = fold.end.to_point(&buffer);
5362 start.row += row_delta;
5363 end.row += row_delta;
5364 refold_ranges.push(start..end);
5365 }
5366 }
5367 }
5368
5369 // If we didn't move line(s), preserve the existing selections
5370 new_selections.append(&mut contiguous_row_selections);
5371 }
5372
5373 self.transact(cx, |this, cx| {
5374 this.unfold_ranges(unfold_ranges, true, true, cx);
5375 this.buffer.update(cx, |buffer, cx| {
5376 for (range, text) in edits {
5377 buffer.edit([(range, text)], None, cx);
5378 }
5379 });
5380 this.fold_ranges(refold_ranges, true, cx);
5381 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5382 });
5383 }
5384
5385 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5386 self.transact(cx, |this, cx| {
5387 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5388 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5389 let line_mode = s.line_mode;
5390 s.move_with(|display_map, selection| {
5391 if !selection.is_empty() || line_mode {
5392 return;
5393 }
5394
5395 let mut head = selection.head();
5396 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5397 if head.column() == display_map.line_len(head.row()) {
5398 transpose_offset = display_map
5399 .buffer_snapshot
5400 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5401 }
5402
5403 if transpose_offset == 0 {
5404 return;
5405 }
5406
5407 *head.column_mut() += 1;
5408 head = display_map.clip_point(head, Bias::Right);
5409 selection.collapse_to(head, SelectionGoal::Column(head.column()));
5410
5411 let transpose_start = display_map
5412 .buffer_snapshot
5413 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5414 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5415 let transpose_end = display_map
5416 .buffer_snapshot
5417 .clip_offset(transpose_offset + 1, Bias::Right);
5418 if let Some(ch) =
5419 display_map.buffer_snapshot.chars_at(transpose_start).next()
5420 {
5421 edits.push((transpose_start..transpose_offset, String::new()));
5422 edits.push((transpose_end..transpose_end, ch.to_string()));
5423 }
5424 }
5425 });
5426 edits
5427 });
5428 this.buffer
5429 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5430 let selections = this.selections.all::<usize>(cx);
5431 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5432 s.select(selections);
5433 });
5434 });
5435 }
5436
5437 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5438 let mut text = String::new();
5439 let buffer = self.buffer.read(cx).snapshot(cx);
5440 let mut selections = self.selections.all::<Point>(cx);
5441 let mut clipboard_selections = Vec::with_capacity(selections.len());
5442 {
5443 let max_point = buffer.max_point();
5444 let mut is_first = true;
5445 for selection in &mut selections {
5446 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5447 if is_entire_line {
5448 selection.start = Point::new(selection.start.row, 0);
5449 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5450 selection.goal = SelectionGoal::None;
5451 }
5452 if is_first {
5453 is_first = false;
5454 } else {
5455 text += "\n";
5456 }
5457 let mut len = 0;
5458 for chunk in buffer.text_for_range(selection.start..selection.end) {
5459 text.push_str(chunk);
5460 len += chunk.len();
5461 }
5462 clipboard_selections.push(ClipboardSelection {
5463 len,
5464 is_entire_line,
5465 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5466 });
5467 }
5468 }
5469
5470 self.transact(cx, |this, cx| {
5471 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5472 s.select(selections);
5473 });
5474 this.insert("", cx);
5475 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5476 });
5477 }
5478
5479 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5480 let selections = self.selections.all::<Point>(cx);
5481 let buffer = self.buffer.read(cx).read(cx);
5482 let mut text = String::new();
5483
5484 let mut clipboard_selections = Vec::with_capacity(selections.len());
5485 {
5486 let max_point = buffer.max_point();
5487 let mut is_first = true;
5488 for selection in selections.iter() {
5489 let mut start = selection.start;
5490 let mut end = selection.end;
5491 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5492 if is_entire_line {
5493 start = Point::new(start.row, 0);
5494 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5495 }
5496 if is_first {
5497 is_first = false;
5498 } else {
5499 text += "\n";
5500 }
5501 let mut len = 0;
5502 for chunk in buffer.text_for_range(start..end) {
5503 text.push_str(chunk);
5504 len += chunk.len();
5505 }
5506 clipboard_selections.push(ClipboardSelection {
5507 len,
5508 is_entire_line,
5509 first_line_indent: buffer.indent_size_for_line(start.row).len,
5510 });
5511 }
5512 }
5513
5514 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5515 }
5516
5517 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5518 self.transact(cx, |this, cx| {
5519 if let Some(item) = cx.read_from_clipboard() {
5520 let clipboard_text = Cow::Borrowed(item.text());
5521 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5522 let old_selections = this.selections.all::<usize>(cx);
5523 let all_selections_were_entire_line =
5524 clipboard_selections.iter().all(|s| s.is_entire_line);
5525 let first_selection_indent_column =
5526 clipboard_selections.first().map(|s| s.first_line_indent);
5527 if clipboard_selections.len() != old_selections.len() {
5528 clipboard_selections.drain(..);
5529 }
5530
5531 this.buffer.update(cx, |buffer, cx| {
5532 let snapshot = buffer.read(cx);
5533 let mut start_offset = 0;
5534 let mut edits = Vec::new();
5535 let mut original_indent_columns = Vec::new();
5536 let line_mode = this.selections.line_mode;
5537 for (ix, selection) in old_selections.iter().enumerate() {
5538 let to_insert;
5539 let entire_line;
5540 let original_indent_column;
5541 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5542 let end_offset = start_offset + clipboard_selection.len;
5543 to_insert = &clipboard_text[start_offset..end_offset];
5544 entire_line = clipboard_selection.is_entire_line;
5545 start_offset = end_offset + 1;
5546 original_indent_column =
5547 Some(clipboard_selection.first_line_indent);
5548 } else {
5549 to_insert = clipboard_text.as_str();
5550 entire_line = all_selections_were_entire_line;
5551 original_indent_column = first_selection_indent_column
5552 }
5553
5554 // If the corresponding selection was empty when this slice of the
5555 // clipboard text was written, then the entire line containing the
5556 // selection was copied. If this selection is also currently empty,
5557 // then paste the line before the current line of the buffer.
5558 let range = if selection.is_empty() && !line_mode && entire_line {
5559 let column = selection.start.to_point(&snapshot).column as usize;
5560 let line_start = selection.start - column;
5561 line_start..line_start
5562 } else {
5563 selection.range()
5564 };
5565
5566 edits.push((range, to_insert));
5567 original_indent_columns.extend(original_indent_column);
5568 }
5569 drop(snapshot);
5570
5571 buffer.edit(
5572 edits,
5573 Some(AutoindentMode::Block {
5574 original_indent_columns,
5575 }),
5576 cx,
5577 );
5578 });
5579
5580 let selections = this.selections.all::<usize>(cx);
5581 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5582 } else {
5583 this.insert(&clipboard_text, cx);
5584 }
5585 }
5586 });
5587 }
5588
5589 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5590 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5591 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5592 self.change_selections(None, cx, |s| {
5593 s.select_anchors(selections.to_vec());
5594 });
5595 }
5596 self.request_autoscroll(Autoscroll::fit(), cx);
5597 self.unmark_text(cx);
5598 self.refresh_copilot_suggestions(true, cx);
5599 cx.emit(Event::Edited);
5600 }
5601 }
5602
5603 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5604 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5605 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5606 {
5607 self.change_selections(None, cx, |s| {
5608 s.select_anchors(selections.to_vec());
5609 });
5610 }
5611 self.request_autoscroll(Autoscroll::fit(), cx);
5612 self.unmark_text(cx);
5613 self.refresh_copilot_suggestions(true, cx);
5614 cx.emit(Event::Edited);
5615 }
5616 }
5617
5618 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5619 self.buffer
5620 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5621 }
5622
5623 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5624 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5625 let line_mode = s.line_mode;
5626 s.move_with(|map, selection| {
5627 let cursor = if selection.is_empty() && !line_mode {
5628 movement::left(map, selection.start)
5629 } else {
5630 selection.start
5631 };
5632 selection.collapse_to(cursor, SelectionGoal::None);
5633 });
5634 })
5635 }
5636
5637 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5638 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5639 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5640 })
5641 }
5642
5643 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5644 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5645 let line_mode = s.line_mode;
5646 s.move_with(|map, selection| {
5647 let cursor = if selection.is_empty() && !line_mode {
5648 movement::right(map, selection.end)
5649 } else {
5650 selection.end
5651 };
5652 selection.collapse_to(cursor, SelectionGoal::None)
5653 });
5654 })
5655 }
5656
5657 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5658 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5659 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5660 })
5661 }
5662
5663 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5664 if self.take_rename(true, cx).is_some() {
5665 return;
5666 }
5667
5668 if matches!(self.mode, EditorMode::SingleLine) {
5669 cx.propagate_action();
5670 return;
5671 }
5672
5673 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5674 let line_mode = s.line_mode;
5675 s.move_with(|map, selection| {
5676 if !selection.is_empty() && !line_mode {
5677 selection.goal = SelectionGoal::None;
5678 }
5679 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
5680 selection.collapse_to(cursor, goal);
5681 });
5682 })
5683 }
5684
5685 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5686 if self.take_rename(true, cx).is_some() {
5687 return;
5688 }
5689
5690 if matches!(self.mode, EditorMode::SingleLine) {
5691 cx.propagate_action();
5692 return;
5693 }
5694
5695 let row_count = if let Some(row_count) = self.visible_line_count() {
5696 row_count as u32 - 1
5697 } else {
5698 return;
5699 };
5700
5701 let autoscroll = if action.center_cursor {
5702 Autoscroll::center()
5703 } else {
5704 Autoscroll::fit()
5705 };
5706
5707 self.change_selections(Some(autoscroll), cx, |s| {
5708 let line_mode = s.line_mode;
5709 s.move_with(|map, selection| {
5710 if !selection.is_empty() && !line_mode {
5711 selection.goal = SelectionGoal::None;
5712 }
5713 let (cursor, goal) =
5714 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5715 selection.collapse_to(cursor, goal);
5716 });
5717 });
5718 }
5719
5720 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5721 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5722 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5723 })
5724 }
5725
5726 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5727 self.take_rename(true, cx);
5728
5729 if self.mode == EditorMode::SingleLine {
5730 cx.propagate_action();
5731 return;
5732 }
5733
5734 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5735 let line_mode = s.line_mode;
5736 s.move_with(|map, selection| {
5737 if !selection.is_empty() && !line_mode {
5738 selection.goal = SelectionGoal::None;
5739 }
5740 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5741 selection.collapse_to(cursor, goal);
5742 });
5743 });
5744 }
5745
5746 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5747 if self.take_rename(true, cx).is_some() {
5748 return;
5749 }
5750
5751 if self
5752 .context_menu
5753 .write()
5754 .as_mut()
5755 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5756 .unwrap_or(false)
5757 {
5758 return;
5759 }
5760
5761 if matches!(self.mode, EditorMode::SingleLine) {
5762 cx.propagate_action();
5763 return;
5764 }
5765
5766 let row_count = if let Some(row_count) = self.visible_line_count() {
5767 row_count as u32 - 1
5768 } else {
5769 return;
5770 };
5771
5772 let autoscroll = if action.center_cursor {
5773 Autoscroll::center()
5774 } else {
5775 Autoscroll::fit()
5776 };
5777
5778 self.change_selections(Some(autoscroll), cx, |s| {
5779 let line_mode = s.line_mode;
5780 s.move_with(|map, selection| {
5781 if !selection.is_empty() && !line_mode {
5782 selection.goal = SelectionGoal::None;
5783 }
5784 let (cursor, goal) =
5785 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5786 selection.collapse_to(cursor, goal);
5787 });
5788 });
5789 }
5790
5791 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5792 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5793 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5794 });
5795 }
5796
5797 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5798 if let Some(context_menu) = self.context_menu.write().as_mut() {
5799 context_menu.select_first(self.project.as_ref(), cx);
5800 }
5801 }
5802
5803 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5804 if let Some(context_menu) = self.context_menu.write().as_mut() {
5805 context_menu.select_prev(self.project.as_ref(), cx);
5806 }
5807 }
5808
5809 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5810 if let Some(context_menu) = self.context_menu.write().as_mut() {
5811 context_menu.select_next(self.project.as_ref(), cx);
5812 }
5813 }
5814
5815 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5816 if let Some(context_menu) = self.context_menu.write().as_mut() {
5817 context_menu.select_last(self.project.as_ref(), cx);
5818 }
5819 }
5820
5821 pub fn move_to_previous_word_start(
5822 &mut self,
5823 _: &MoveToPreviousWordStart,
5824 cx: &mut ViewContext<Self>,
5825 ) {
5826 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5827 s.move_cursors_with(|map, head, _| {
5828 (
5829 movement::previous_word_start(map, head),
5830 SelectionGoal::None,
5831 )
5832 });
5833 })
5834 }
5835
5836 pub fn move_to_previous_subword_start(
5837 &mut self,
5838 _: &MoveToPreviousSubwordStart,
5839 cx: &mut ViewContext<Self>,
5840 ) {
5841 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5842 s.move_cursors_with(|map, head, _| {
5843 (
5844 movement::previous_subword_start(map, head),
5845 SelectionGoal::None,
5846 )
5847 });
5848 })
5849 }
5850
5851 pub fn select_to_previous_word_start(
5852 &mut self,
5853 _: &SelectToPreviousWordStart,
5854 cx: &mut ViewContext<Self>,
5855 ) {
5856 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5857 s.move_heads_with(|map, head, _| {
5858 (
5859 movement::previous_word_start(map, head),
5860 SelectionGoal::None,
5861 )
5862 });
5863 })
5864 }
5865
5866 pub fn select_to_previous_subword_start(
5867 &mut self,
5868 _: &SelectToPreviousSubwordStart,
5869 cx: &mut ViewContext<Self>,
5870 ) {
5871 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5872 s.move_heads_with(|map, head, _| {
5873 (
5874 movement::previous_subword_start(map, head),
5875 SelectionGoal::None,
5876 )
5877 });
5878 })
5879 }
5880
5881 pub fn delete_to_previous_word_start(
5882 &mut self,
5883 _: &DeleteToPreviousWordStart,
5884 cx: &mut ViewContext<Self>,
5885 ) {
5886 self.transact(cx, |this, cx| {
5887 this.select_autoclose_pair(cx);
5888 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5889 let line_mode = s.line_mode;
5890 s.move_with(|map, selection| {
5891 if selection.is_empty() && !line_mode {
5892 let cursor = movement::previous_word_start(map, selection.head());
5893 selection.set_head(cursor, SelectionGoal::None);
5894 }
5895 });
5896 });
5897 this.insert("", cx);
5898 });
5899 }
5900
5901 pub fn delete_to_previous_subword_start(
5902 &mut self,
5903 _: &DeleteToPreviousSubwordStart,
5904 cx: &mut ViewContext<Self>,
5905 ) {
5906 self.transact(cx, |this, cx| {
5907 this.select_autoclose_pair(cx);
5908 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5909 let line_mode = s.line_mode;
5910 s.move_with(|map, selection| {
5911 if selection.is_empty() && !line_mode {
5912 let cursor = movement::previous_subword_start(map, selection.head());
5913 selection.set_head(cursor, SelectionGoal::None);
5914 }
5915 });
5916 });
5917 this.insert("", cx);
5918 });
5919 }
5920
5921 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5922 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5923 s.move_cursors_with(|map, head, _| {
5924 (movement::next_word_end(map, head), SelectionGoal::None)
5925 });
5926 })
5927 }
5928
5929 pub fn move_to_next_subword_end(
5930 &mut self,
5931 _: &MoveToNextSubwordEnd,
5932 cx: &mut ViewContext<Self>,
5933 ) {
5934 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5935 s.move_cursors_with(|map, head, _| {
5936 (movement::next_subword_end(map, head), SelectionGoal::None)
5937 });
5938 })
5939 }
5940
5941 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5942 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5943 s.move_heads_with(|map, head, _| {
5944 (movement::next_word_end(map, head), SelectionGoal::None)
5945 });
5946 })
5947 }
5948
5949 pub fn select_to_next_subword_end(
5950 &mut self,
5951 _: &SelectToNextSubwordEnd,
5952 cx: &mut ViewContext<Self>,
5953 ) {
5954 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5955 s.move_heads_with(|map, head, _| {
5956 (movement::next_subword_end(map, head), SelectionGoal::None)
5957 });
5958 })
5959 }
5960
5961 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5962 self.transact(cx, |this, cx| {
5963 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5964 let line_mode = s.line_mode;
5965 s.move_with(|map, selection| {
5966 if selection.is_empty() && !line_mode {
5967 let cursor = movement::next_word_end(map, selection.head());
5968 selection.set_head(cursor, SelectionGoal::None);
5969 }
5970 });
5971 });
5972 this.insert("", cx);
5973 });
5974 }
5975
5976 pub fn delete_to_next_subword_end(
5977 &mut self,
5978 _: &DeleteToNextSubwordEnd,
5979 cx: &mut ViewContext<Self>,
5980 ) {
5981 self.transact(cx, |this, cx| {
5982 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5983 s.move_with(|map, selection| {
5984 if selection.is_empty() {
5985 let cursor = movement::next_subword_end(map, selection.head());
5986 selection.set_head(cursor, SelectionGoal::None);
5987 }
5988 });
5989 });
5990 this.insert("", cx);
5991 });
5992 }
5993
5994 pub fn move_to_beginning_of_line(
5995 &mut self,
5996 _: &MoveToBeginningOfLine,
5997 cx: &mut ViewContext<Self>,
5998 ) {
5999 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6000 s.move_cursors_with(|map, head, _| {
6001 (
6002 movement::indented_line_beginning(map, head, true),
6003 SelectionGoal::None,
6004 )
6005 });
6006 })
6007 }
6008
6009 pub fn select_to_beginning_of_line(
6010 &mut self,
6011 action: &SelectToBeginningOfLine,
6012 cx: &mut ViewContext<Self>,
6013 ) {
6014 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6015 s.move_heads_with(|map, head, _| {
6016 (
6017 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6018 SelectionGoal::None,
6019 )
6020 });
6021 });
6022 }
6023
6024 pub fn delete_to_beginning_of_line(
6025 &mut self,
6026 _: &DeleteToBeginningOfLine,
6027 cx: &mut ViewContext<Self>,
6028 ) {
6029 self.transact(cx, |this, cx| {
6030 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6031 s.move_with(|_, selection| {
6032 selection.reversed = true;
6033 });
6034 });
6035
6036 this.select_to_beginning_of_line(
6037 &SelectToBeginningOfLine {
6038 stop_at_soft_wraps: false,
6039 },
6040 cx,
6041 );
6042 this.backspace(&Backspace, cx);
6043 });
6044 }
6045
6046 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6047 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6048 s.move_cursors_with(|map, head, _| {
6049 (movement::line_end(map, head, true), SelectionGoal::None)
6050 });
6051 })
6052 }
6053
6054 pub fn select_to_end_of_line(
6055 &mut self,
6056 action: &SelectToEndOfLine,
6057 cx: &mut ViewContext<Self>,
6058 ) {
6059 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6060 s.move_heads_with(|map, head, _| {
6061 (
6062 movement::line_end(map, head, action.stop_at_soft_wraps),
6063 SelectionGoal::None,
6064 )
6065 });
6066 })
6067 }
6068
6069 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6070 self.transact(cx, |this, cx| {
6071 this.select_to_end_of_line(
6072 &SelectToEndOfLine {
6073 stop_at_soft_wraps: false,
6074 },
6075 cx,
6076 );
6077 this.delete(&Delete, cx);
6078 });
6079 }
6080
6081 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6082 self.transact(cx, |this, cx| {
6083 this.select_to_end_of_line(
6084 &SelectToEndOfLine {
6085 stop_at_soft_wraps: false,
6086 },
6087 cx,
6088 );
6089 this.cut(&Cut, cx);
6090 });
6091 }
6092
6093 pub fn move_to_start_of_paragraph(
6094 &mut self,
6095 _: &MoveToStartOfParagraph,
6096 cx: &mut ViewContext<Self>,
6097 ) {
6098 if matches!(self.mode, EditorMode::SingleLine) {
6099 cx.propagate_action();
6100 return;
6101 }
6102
6103 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6104 s.move_with(|map, selection| {
6105 selection.collapse_to(
6106 movement::start_of_paragraph(map, selection.head(), 1),
6107 SelectionGoal::None,
6108 )
6109 });
6110 })
6111 }
6112
6113 pub fn move_to_end_of_paragraph(
6114 &mut self,
6115 _: &MoveToEndOfParagraph,
6116 cx: &mut ViewContext<Self>,
6117 ) {
6118 if matches!(self.mode, EditorMode::SingleLine) {
6119 cx.propagate_action();
6120 return;
6121 }
6122
6123 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6124 s.move_with(|map, selection| {
6125 selection.collapse_to(
6126 movement::end_of_paragraph(map, selection.head(), 1),
6127 SelectionGoal::None,
6128 )
6129 });
6130 })
6131 }
6132
6133 pub fn select_to_start_of_paragraph(
6134 &mut self,
6135 _: &SelectToStartOfParagraph,
6136 cx: &mut ViewContext<Self>,
6137 ) {
6138 if matches!(self.mode, EditorMode::SingleLine) {
6139 cx.propagate_action();
6140 return;
6141 }
6142
6143 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6144 s.move_heads_with(|map, head, _| {
6145 (
6146 movement::start_of_paragraph(map, head, 1),
6147 SelectionGoal::None,
6148 )
6149 });
6150 })
6151 }
6152
6153 pub fn select_to_end_of_paragraph(
6154 &mut self,
6155 _: &SelectToEndOfParagraph,
6156 cx: &mut ViewContext<Self>,
6157 ) {
6158 if matches!(self.mode, EditorMode::SingleLine) {
6159 cx.propagate_action();
6160 return;
6161 }
6162
6163 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6164 s.move_heads_with(|map, head, _| {
6165 (
6166 movement::end_of_paragraph(map, head, 1),
6167 SelectionGoal::None,
6168 )
6169 });
6170 })
6171 }
6172
6173 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6174 if matches!(self.mode, EditorMode::SingleLine) {
6175 cx.propagate_action();
6176 return;
6177 }
6178
6179 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6180 s.select_ranges(vec![0..0]);
6181 });
6182 }
6183
6184 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6185 let mut selection = self.selections.last::<Point>(cx);
6186 selection.set_head(Point::zero(), SelectionGoal::None);
6187
6188 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6189 s.select(vec![selection]);
6190 });
6191 }
6192
6193 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6194 if matches!(self.mode, EditorMode::SingleLine) {
6195 cx.propagate_action();
6196 return;
6197 }
6198
6199 let cursor = self.buffer.read(cx).read(cx).len();
6200 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6201 s.select_ranges(vec![cursor..cursor])
6202 });
6203 }
6204
6205 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6206 self.nav_history = nav_history;
6207 }
6208
6209 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6210 self.nav_history.as_ref()
6211 }
6212
6213 fn push_to_nav_history(
6214 &mut self,
6215 cursor_anchor: Anchor,
6216 new_position: Option<Point>,
6217 cx: &mut ViewContext<Self>,
6218 ) {
6219 if let Some(nav_history) = self.nav_history.as_mut() {
6220 let buffer = self.buffer.read(cx).read(cx);
6221 let cursor_position = cursor_anchor.to_point(&buffer);
6222 let scroll_state = self.scroll_manager.anchor();
6223 let scroll_top_row = scroll_state.top_row(&buffer);
6224 drop(buffer);
6225
6226 if let Some(new_position) = new_position {
6227 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6228 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6229 return;
6230 }
6231 }
6232
6233 nav_history.push(
6234 Some(NavigationData {
6235 cursor_anchor,
6236 cursor_position,
6237 scroll_anchor: scroll_state,
6238 scroll_top_row,
6239 }),
6240 cx,
6241 );
6242 }
6243 }
6244
6245 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6246 let buffer = self.buffer.read(cx).snapshot(cx);
6247 let mut selection = self.selections.first::<usize>(cx);
6248 selection.set_head(buffer.len(), SelectionGoal::None);
6249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6250 s.select(vec![selection]);
6251 });
6252 }
6253
6254 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6255 let end = self.buffer.read(cx).read(cx).len();
6256 self.change_selections(None, cx, |s| {
6257 s.select_ranges(vec![0..end]);
6258 });
6259 }
6260
6261 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6263 let mut selections = self.selections.all::<Point>(cx);
6264 let max_point = display_map.buffer_snapshot.max_point();
6265 for selection in &mut selections {
6266 let rows = selection.spanned_rows(true, &display_map);
6267 selection.start = Point::new(rows.start, 0);
6268 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
6269 selection.reversed = false;
6270 }
6271 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6272 s.select(selections);
6273 });
6274 }
6275
6276 pub fn split_selection_into_lines(
6277 &mut self,
6278 _: &SplitSelectionIntoLines,
6279 cx: &mut ViewContext<Self>,
6280 ) {
6281 let mut to_unfold = Vec::new();
6282 let mut new_selection_ranges = Vec::new();
6283 {
6284 let selections = self.selections.all::<Point>(cx);
6285 let buffer = self.buffer.read(cx).read(cx);
6286 for selection in selections {
6287 for row in selection.start.row..selection.end.row {
6288 let cursor = Point::new(row, buffer.line_len(row));
6289 new_selection_ranges.push(cursor..cursor);
6290 }
6291 new_selection_ranges.push(selection.end..selection.end);
6292 to_unfold.push(selection.start..selection.end);
6293 }
6294 }
6295 self.unfold_ranges(to_unfold, true, true, cx);
6296 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6297 s.select_ranges(new_selection_ranges);
6298 });
6299 }
6300
6301 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6302 self.add_selection(true, cx);
6303 }
6304
6305 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6306 self.add_selection(false, cx);
6307 }
6308
6309 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6310 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6311 let mut selections = self.selections.all::<Point>(cx);
6312 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6313 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6314 let range = oldest_selection.display_range(&display_map).sorted();
6315 let columns = cmp::min(range.start.column(), range.end.column())
6316 ..cmp::max(range.start.column(), range.end.column());
6317
6318 selections.clear();
6319 let mut stack = Vec::new();
6320 for row in range.start.row()..=range.end.row() {
6321 if let Some(selection) = self.selections.build_columnar_selection(
6322 &display_map,
6323 row,
6324 &columns,
6325 oldest_selection.reversed,
6326 ) {
6327 stack.push(selection.id);
6328 selections.push(selection);
6329 }
6330 }
6331
6332 if above {
6333 stack.reverse();
6334 }
6335
6336 AddSelectionsState { above, stack }
6337 });
6338
6339 let last_added_selection = *state.stack.last().unwrap();
6340 let mut new_selections = Vec::new();
6341 if above == state.above {
6342 let end_row = if above {
6343 0
6344 } else {
6345 display_map.max_point().row()
6346 };
6347
6348 'outer: for selection in selections {
6349 if selection.id == last_added_selection {
6350 let range = selection.display_range(&display_map).sorted();
6351 debug_assert_eq!(range.start.row(), range.end.row());
6352 let mut row = range.start.row();
6353 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
6354 {
6355 start..end
6356 } else {
6357 cmp::min(range.start.column(), range.end.column())
6358 ..cmp::max(range.start.column(), range.end.column())
6359 };
6360
6361 while row != end_row {
6362 if above {
6363 row -= 1;
6364 } else {
6365 row += 1;
6366 }
6367
6368 if let Some(new_selection) = self.selections.build_columnar_selection(
6369 &display_map,
6370 row,
6371 &columns,
6372 selection.reversed,
6373 ) {
6374 state.stack.push(new_selection.id);
6375 if above {
6376 new_selections.push(new_selection);
6377 new_selections.push(selection);
6378 } else {
6379 new_selections.push(selection);
6380 new_selections.push(new_selection);
6381 }
6382
6383 continue 'outer;
6384 }
6385 }
6386 }
6387
6388 new_selections.push(selection);
6389 }
6390 } else {
6391 new_selections = selections;
6392 new_selections.retain(|s| s.id != last_added_selection);
6393 state.stack.pop();
6394 }
6395
6396 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6397 s.select(new_selections);
6398 });
6399 if state.stack.len() > 1 {
6400 self.add_selections_state = Some(state);
6401 }
6402 }
6403
6404 pub fn select_next_match_internal(
6405 &mut self,
6406 display_map: &DisplaySnapshot,
6407 replace_newest: bool,
6408 autoscroll: Option<Autoscroll>,
6409 cx: &mut ViewContext<Self>,
6410 ) -> Result<()> {
6411 fn select_next_match_ranges(
6412 this: &mut Editor,
6413 range: Range<usize>,
6414 replace_newest: bool,
6415 auto_scroll: Option<Autoscroll>,
6416 cx: &mut ViewContext<Editor>,
6417 ) {
6418 this.unfold_ranges([range.clone()], false, true, cx);
6419 this.change_selections(auto_scroll, cx, |s| {
6420 if replace_newest {
6421 s.delete(s.newest_anchor().id);
6422 }
6423 s.insert_range(range.clone());
6424 });
6425 }
6426
6427 let buffer = &display_map.buffer_snapshot;
6428 let mut selections = self.selections.all::<usize>(cx);
6429 if let Some(mut select_next_state) = self.select_next_state.take() {
6430 let query = &select_next_state.query;
6431 if !select_next_state.done {
6432 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6433 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6434 let mut next_selected_range = None;
6435
6436 let bytes_after_last_selection =
6437 buffer.bytes_in_range(last_selection.end..buffer.len());
6438 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6439 let query_matches = query
6440 .stream_find_iter(bytes_after_last_selection)
6441 .map(|result| (last_selection.end, result))
6442 .chain(
6443 query
6444 .stream_find_iter(bytes_before_first_selection)
6445 .map(|result| (0, result)),
6446 );
6447
6448 for (start_offset, query_match) in query_matches {
6449 let query_match = query_match.unwrap(); // can only fail due to I/O
6450 let offset_range =
6451 start_offset + query_match.start()..start_offset + query_match.end();
6452 let display_range = offset_range.start.to_display_point(&display_map)
6453 ..offset_range.end.to_display_point(&display_map);
6454
6455 if !select_next_state.wordwise
6456 || (!movement::is_inside_word(&display_map, display_range.start)
6457 && !movement::is_inside_word(&display_map, display_range.end))
6458 {
6459 if selections
6460 .iter()
6461 .find(|selection| selection.equals(&offset_range))
6462 .is_none()
6463 {
6464 next_selected_range = Some(offset_range);
6465 break;
6466 }
6467 }
6468 }
6469
6470 if let Some(next_selected_range) = next_selected_range {
6471 select_next_match_ranges(
6472 self,
6473 next_selected_range,
6474 replace_newest,
6475 autoscroll,
6476 cx,
6477 );
6478 } else {
6479 select_next_state.done = true;
6480 }
6481 }
6482
6483 self.select_next_state = Some(select_next_state);
6484 } else if selections.len() == 1 {
6485 let selection = selections.last_mut().unwrap();
6486 if selection.start == selection.end {
6487 let word_range = movement::surrounding_word(
6488 &display_map,
6489 selection.start.to_display_point(&display_map),
6490 );
6491 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6492 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6493 selection.goal = SelectionGoal::None;
6494 selection.reversed = false;
6495
6496 let query = buffer
6497 .text_for_range(selection.start..selection.end)
6498 .collect::<String>();
6499
6500 let is_empty = query.is_empty();
6501 let select_state = SelectNextState {
6502 query: AhoCorasick::new(&[query])?,
6503 wordwise: true,
6504 done: is_empty,
6505 };
6506 select_next_match_ranges(
6507 self,
6508 selection.start..selection.end,
6509 replace_newest,
6510 autoscroll,
6511 cx,
6512 );
6513 self.select_next_state = Some(select_state);
6514 } else {
6515 let query = buffer
6516 .text_for_range(selection.start..selection.end)
6517 .collect::<String>();
6518 self.select_next_state = Some(SelectNextState {
6519 query: AhoCorasick::new(&[query])?,
6520 wordwise: false,
6521 done: false,
6522 });
6523 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6524 }
6525 }
6526 Ok(())
6527 }
6528
6529 pub fn select_all_matches(
6530 &mut self,
6531 action: &SelectAllMatches,
6532 cx: &mut ViewContext<Self>,
6533 ) -> Result<()> {
6534 self.push_to_selection_history();
6535 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6536
6537 loop {
6538 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6539
6540 if self
6541 .select_next_state
6542 .as_ref()
6543 .map(|selection_state| selection_state.done)
6544 .unwrap_or(true)
6545 {
6546 break;
6547 }
6548 }
6549
6550 Ok(())
6551 }
6552
6553 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6554 self.push_to_selection_history();
6555 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6556 self.select_next_match_internal(
6557 &display_map,
6558 action.replace_newest,
6559 Some(Autoscroll::newest()),
6560 cx,
6561 )?;
6562 Ok(())
6563 }
6564
6565 pub fn select_previous(
6566 &mut self,
6567 action: &SelectPrevious,
6568 cx: &mut ViewContext<Self>,
6569 ) -> Result<()> {
6570 self.push_to_selection_history();
6571 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6572 let buffer = &display_map.buffer_snapshot;
6573 let mut selections = self.selections.all::<usize>(cx);
6574 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6575 let query = &select_prev_state.query;
6576 if !select_prev_state.done {
6577 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6578 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6579 let mut next_selected_range = None;
6580 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6581 let bytes_before_last_selection =
6582 buffer.reversed_bytes_in_range(0..last_selection.start);
6583 let bytes_after_first_selection =
6584 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6585 let query_matches = query
6586 .stream_find_iter(bytes_before_last_selection)
6587 .map(|result| (last_selection.start, result))
6588 .chain(
6589 query
6590 .stream_find_iter(bytes_after_first_selection)
6591 .map(|result| (buffer.len(), result)),
6592 );
6593 for (end_offset, query_match) in query_matches {
6594 let query_match = query_match.unwrap(); // can only fail due to I/O
6595 let offset_range =
6596 end_offset - query_match.end()..end_offset - query_match.start();
6597 let display_range = offset_range.start.to_display_point(&display_map)
6598 ..offset_range.end.to_display_point(&display_map);
6599
6600 if !select_prev_state.wordwise
6601 || (!movement::is_inside_word(&display_map, display_range.start)
6602 && !movement::is_inside_word(&display_map, display_range.end))
6603 {
6604 next_selected_range = Some(offset_range);
6605 break;
6606 }
6607 }
6608
6609 if let Some(next_selected_range) = next_selected_range {
6610 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6611 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6612 if action.replace_newest {
6613 s.delete(s.newest_anchor().id);
6614 }
6615 s.insert_range(next_selected_range);
6616 });
6617 } else {
6618 select_prev_state.done = true;
6619 }
6620 }
6621
6622 self.select_prev_state = Some(select_prev_state);
6623 } else if selections.len() == 1 {
6624 let selection = selections.last_mut().unwrap();
6625 if selection.start == selection.end {
6626 let word_range = movement::surrounding_word(
6627 &display_map,
6628 selection.start.to_display_point(&display_map),
6629 );
6630 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6631 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6632 selection.goal = SelectionGoal::None;
6633 selection.reversed = false;
6634
6635 let query = buffer
6636 .text_for_range(selection.start..selection.end)
6637 .collect::<String>();
6638 let query = query.chars().rev().collect::<String>();
6639 let select_state = SelectNextState {
6640 query: AhoCorasick::new(&[query])?,
6641 wordwise: true,
6642 done: false,
6643 };
6644 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6645 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6646 s.select(selections);
6647 });
6648 self.select_prev_state = Some(select_state);
6649 } else {
6650 let query = buffer
6651 .text_for_range(selection.start..selection.end)
6652 .collect::<String>();
6653 let query = query.chars().rev().collect::<String>();
6654 self.select_prev_state = Some(SelectNextState {
6655 query: AhoCorasick::new(&[query])?,
6656 wordwise: false,
6657 done: false,
6658 });
6659 self.select_previous(action, cx)?;
6660 }
6661 }
6662 Ok(())
6663 }
6664
6665 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6666 self.transact(cx, |this, cx| {
6667 let mut selections = this.selections.all::<Point>(cx);
6668 let mut edits = Vec::new();
6669 let mut selection_edit_ranges = Vec::new();
6670 let mut last_toggled_row = None;
6671 let snapshot = this.buffer.read(cx).read(cx);
6672 let empty_str: Arc<str> = "".into();
6673 let mut suffixes_inserted = Vec::new();
6674
6675 fn comment_prefix_range(
6676 snapshot: &MultiBufferSnapshot,
6677 row: u32,
6678 comment_prefix: &str,
6679 comment_prefix_whitespace: &str,
6680 ) -> Range<Point> {
6681 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6682
6683 let mut line_bytes = snapshot
6684 .bytes_in_range(start..snapshot.max_point())
6685 .flatten()
6686 .copied();
6687
6688 // If this line currently begins with the line comment prefix, then record
6689 // the range containing the prefix.
6690 if line_bytes
6691 .by_ref()
6692 .take(comment_prefix.len())
6693 .eq(comment_prefix.bytes())
6694 {
6695 // Include any whitespace that matches the comment prefix.
6696 let matching_whitespace_len = line_bytes
6697 .zip(comment_prefix_whitespace.bytes())
6698 .take_while(|(a, b)| a == b)
6699 .count() as u32;
6700 let end = Point::new(
6701 start.row,
6702 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6703 );
6704 start..end
6705 } else {
6706 start..start
6707 }
6708 }
6709
6710 fn comment_suffix_range(
6711 snapshot: &MultiBufferSnapshot,
6712 row: u32,
6713 comment_suffix: &str,
6714 comment_suffix_has_leading_space: bool,
6715 ) -> Range<Point> {
6716 let end = Point::new(row, snapshot.line_len(row));
6717 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6718
6719 let mut line_end_bytes = snapshot
6720 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6721 .flatten()
6722 .copied();
6723
6724 let leading_space_len = if suffix_start_column > 0
6725 && line_end_bytes.next() == Some(b' ')
6726 && comment_suffix_has_leading_space
6727 {
6728 1
6729 } else {
6730 0
6731 };
6732
6733 // If this line currently begins with the line comment prefix, then record
6734 // the range containing the prefix.
6735 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6736 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6737 start..end
6738 } else {
6739 end..end
6740 }
6741 }
6742
6743 // TODO: Handle selections that cross excerpts
6744 for selection in &mut selections {
6745 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6746 let language = if let Some(language) =
6747 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6748 {
6749 language
6750 } else {
6751 continue;
6752 };
6753
6754 selection_edit_ranges.clear();
6755
6756 // If multiple selections contain a given row, avoid processing that
6757 // row more than once.
6758 let mut start_row = selection.start.row;
6759 if last_toggled_row == Some(start_row) {
6760 start_row += 1;
6761 }
6762 let end_row =
6763 if selection.end.row > selection.start.row && selection.end.column == 0 {
6764 selection.end.row - 1
6765 } else {
6766 selection.end.row
6767 };
6768 last_toggled_row = Some(end_row);
6769
6770 if start_row > end_row {
6771 continue;
6772 }
6773
6774 // If the language has line comments, toggle those.
6775 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6776 // Split the comment prefix's trailing whitespace into a separate string,
6777 // as that portion won't be used for detecting if a line is a comment.
6778 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6779 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6780 let mut all_selection_lines_are_comments = true;
6781
6782 for row in start_row..=end_row {
6783 if snapshot.is_line_blank(row) && start_row < end_row {
6784 continue;
6785 }
6786
6787 let prefix_range = comment_prefix_range(
6788 snapshot.deref(),
6789 row,
6790 comment_prefix,
6791 comment_prefix_whitespace,
6792 );
6793 if prefix_range.is_empty() {
6794 all_selection_lines_are_comments = false;
6795 }
6796 selection_edit_ranges.push(prefix_range);
6797 }
6798
6799 if all_selection_lines_are_comments {
6800 edits.extend(
6801 selection_edit_ranges
6802 .iter()
6803 .cloned()
6804 .map(|range| (range, empty_str.clone())),
6805 );
6806 } else {
6807 let min_column = selection_edit_ranges
6808 .iter()
6809 .map(|r| r.start.column)
6810 .min()
6811 .unwrap_or(0);
6812 edits.extend(selection_edit_ranges.iter().map(|range| {
6813 let position = Point::new(range.start.row, min_column);
6814 (position..position, full_comment_prefix.clone())
6815 }));
6816 }
6817 } else if let Some((full_comment_prefix, comment_suffix)) =
6818 language.block_comment_delimiters()
6819 {
6820 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6821 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6822 let prefix_range = comment_prefix_range(
6823 snapshot.deref(),
6824 start_row,
6825 comment_prefix,
6826 comment_prefix_whitespace,
6827 );
6828 let suffix_range = comment_suffix_range(
6829 snapshot.deref(),
6830 end_row,
6831 comment_suffix.trim_start_matches(' '),
6832 comment_suffix.starts_with(' '),
6833 );
6834
6835 if prefix_range.is_empty() || suffix_range.is_empty() {
6836 edits.push((
6837 prefix_range.start..prefix_range.start,
6838 full_comment_prefix.clone(),
6839 ));
6840 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6841 suffixes_inserted.push((end_row, comment_suffix.len()));
6842 } else {
6843 edits.push((prefix_range, empty_str.clone()));
6844 edits.push((suffix_range, empty_str.clone()));
6845 }
6846 } else {
6847 continue;
6848 }
6849 }
6850
6851 drop(snapshot);
6852 this.buffer.update(cx, |buffer, cx| {
6853 buffer.edit(edits, None, cx);
6854 });
6855
6856 // Adjust selections so that they end before any comment suffixes that
6857 // were inserted.
6858 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6859 let mut selections = this.selections.all::<Point>(cx);
6860 let snapshot = this.buffer.read(cx).read(cx);
6861 for selection in &mut selections {
6862 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6863 match row.cmp(&selection.end.row) {
6864 Ordering::Less => {
6865 suffixes_inserted.next();
6866 continue;
6867 }
6868 Ordering::Greater => break,
6869 Ordering::Equal => {
6870 if selection.end.column == snapshot.line_len(row) {
6871 if selection.is_empty() {
6872 selection.start.column -= suffix_len as u32;
6873 }
6874 selection.end.column -= suffix_len as u32;
6875 }
6876 break;
6877 }
6878 }
6879 }
6880 }
6881
6882 drop(snapshot);
6883 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6884
6885 let selections = this.selections.all::<Point>(cx);
6886 let selections_on_single_row = selections.windows(2).all(|selections| {
6887 selections[0].start.row == selections[1].start.row
6888 && selections[0].end.row == selections[1].end.row
6889 && selections[0].start.row == selections[0].end.row
6890 });
6891 let selections_selecting = selections
6892 .iter()
6893 .any(|selection| selection.start != selection.end);
6894 let advance_downwards = action.advance_downwards
6895 && selections_on_single_row
6896 && !selections_selecting
6897 && this.mode != EditorMode::SingleLine;
6898
6899 if advance_downwards {
6900 let snapshot = this.buffer.read(cx).snapshot(cx);
6901
6902 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6903 s.move_cursors_with(|display_snapshot, display_point, _| {
6904 let mut point = display_point.to_point(display_snapshot);
6905 point.row += 1;
6906 point = snapshot.clip_point(point, Bias::Left);
6907 let display_point = point.to_display_point(display_snapshot);
6908 (display_point, SelectionGoal::Column(display_point.column()))
6909 })
6910 });
6911 }
6912 });
6913 }
6914
6915 pub fn select_larger_syntax_node(
6916 &mut self,
6917 _: &SelectLargerSyntaxNode,
6918 cx: &mut ViewContext<Self>,
6919 ) {
6920 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6921 let buffer = self.buffer.read(cx).snapshot(cx);
6922 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6923
6924 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6925 let mut selected_larger_node = false;
6926 let new_selections = old_selections
6927 .iter()
6928 .map(|selection| {
6929 let old_range = selection.start..selection.end;
6930 let mut new_range = old_range.clone();
6931 while let Some(containing_range) =
6932 buffer.range_for_syntax_ancestor(new_range.clone())
6933 {
6934 new_range = containing_range;
6935 if !display_map.intersects_fold(new_range.start)
6936 && !display_map.intersects_fold(new_range.end)
6937 {
6938 break;
6939 }
6940 }
6941
6942 selected_larger_node |= new_range != old_range;
6943 Selection {
6944 id: selection.id,
6945 start: new_range.start,
6946 end: new_range.end,
6947 goal: SelectionGoal::None,
6948 reversed: selection.reversed,
6949 }
6950 })
6951 .collect::<Vec<_>>();
6952
6953 if selected_larger_node {
6954 stack.push(old_selections);
6955 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6956 s.select(new_selections);
6957 });
6958 }
6959 self.select_larger_syntax_node_stack = stack;
6960 }
6961
6962 pub fn select_smaller_syntax_node(
6963 &mut self,
6964 _: &SelectSmallerSyntaxNode,
6965 cx: &mut ViewContext<Self>,
6966 ) {
6967 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6968 if let Some(selections) = stack.pop() {
6969 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6970 s.select(selections.to_vec());
6971 });
6972 }
6973 self.select_larger_syntax_node_stack = stack;
6974 }
6975
6976 pub fn move_to_enclosing_bracket(
6977 &mut self,
6978 _: &MoveToEnclosingBracket,
6979 cx: &mut ViewContext<Self>,
6980 ) {
6981 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6982 s.move_offsets_with(|snapshot, selection| {
6983 let Some(enclosing_bracket_ranges) =
6984 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6985 else {
6986 return;
6987 };
6988
6989 let mut best_length = usize::MAX;
6990 let mut best_inside = false;
6991 let mut best_in_bracket_range = false;
6992 let mut best_destination = None;
6993 for (open, close) in enclosing_bracket_ranges {
6994 let close = close.to_inclusive();
6995 let length = close.end() - open.start;
6996 let inside = selection.start >= open.end && selection.end <= *close.start();
6997 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6998 || close.contains(&selection.head());
6999
7000 // If best is next to a bracket and current isn't, skip
7001 if !in_bracket_range && best_in_bracket_range {
7002 continue;
7003 }
7004
7005 // Prefer smaller lengths unless best is inside and current isn't
7006 if length > best_length && (best_inside || !inside) {
7007 continue;
7008 }
7009
7010 best_length = length;
7011 best_inside = inside;
7012 best_in_bracket_range = in_bracket_range;
7013 best_destination = Some(
7014 if close.contains(&selection.start) && close.contains(&selection.end) {
7015 if inside {
7016 open.end
7017 } else {
7018 open.start
7019 }
7020 } else {
7021 if inside {
7022 *close.start()
7023 } else {
7024 *close.end()
7025 }
7026 },
7027 );
7028 }
7029
7030 if let Some(destination) = best_destination {
7031 selection.collapse_to(destination, SelectionGoal::None);
7032 }
7033 })
7034 });
7035 }
7036
7037 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7038 self.end_selection(cx);
7039 self.selection_history.mode = SelectionHistoryMode::Undoing;
7040 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
7041 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7042 self.select_next_state = entry.select_next_state;
7043 self.select_prev_state = entry.select_prev_state;
7044 self.add_selections_state = entry.add_selections_state;
7045 self.request_autoscroll(Autoscroll::newest(), cx);
7046 }
7047 self.selection_history.mode = SelectionHistoryMode::Normal;
7048 }
7049
7050 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7051 self.end_selection(cx);
7052 self.selection_history.mode = SelectionHistoryMode::Redoing;
7053 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7054 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7055 self.select_next_state = entry.select_next_state;
7056 self.select_prev_state = entry.select_prev_state;
7057 self.add_selections_state = entry.add_selections_state;
7058 self.request_autoscroll(Autoscroll::newest(), cx);
7059 }
7060 self.selection_history.mode = SelectionHistoryMode::Normal;
7061 }
7062
7063 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
7064 self.go_to_diagnostic_impl(Direction::Next, cx)
7065 }
7066
7067 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
7068 self.go_to_diagnostic_impl(Direction::Prev, cx)
7069 }
7070
7071 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
7072 let buffer = self.buffer.read(cx).snapshot(cx);
7073 let selection = self.selections.newest::<usize>(cx);
7074
7075 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
7076 if direction == Direction::Next {
7077 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
7078 let (group_id, jump_to) = popover.activation_info();
7079 if self.activate_diagnostics(group_id, cx) {
7080 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7081 let mut new_selection = s.newest_anchor().clone();
7082 new_selection.collapse_to(jump_to, SelectionGoal::None);
7083 s.select_anchors(vec![new_selection.clone()]);
7084 });
7085 }
7086 return;
7087 }
7088 }
7089
7090 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
7091 active_diagnostics
7092 .primary_range
7093 .to_offset(&buffer)
7094 .to_inclusive()
7095 });
7096 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
7097 if active_primary_range.contains(&selection.head()) {
7098 *active_primary_range.end()
7099 } else {
7100 selection.head()
7101 }
7102 } else {
7103 selection.head()
7104 };
7105
7106 loop {
7107 let mut diagnostics = if direction == Direction::Prev {
7108 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
7109 } else {
7110 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
7111 };
7112 let group = diagnostics.find_map(|entry| {
7113 if entry.diagnostic.is_primary
7114 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
7115 && !entry.range.is_empty()
7116 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
7117 {
7118 Some((entry.range, entry.diagnostic.group_id))
7119 } else {
7120 None
7121 }
7122 });
7123
7124 if let Some((primary_range, group_id)) = group {
7125 if self.activate_diagnostics(group_id, cx) {
7126 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7127 s.select(vec![Selection {
7128 id: selection.id,
7129 start: primary_range.start,
7130 end: primary_range.start,
7131 reversed: false,
7132 goal: SelectionGoal::None,
7133 }]);
7134 });
7135 }
7136 break;
7137 } else {
7138 // Cycle around to the start of the buffer, potentially moving back to the start of
7139 // the currently active diagnostic.
7140 active_primary_range.take();
7141 if direction == Direction::Prev {
7142 if search_start == buffer.len() {
7143 break;
7144 } else {
7145 search_start = buffer.len();
7146 }
7147 } else if search_start == 0 {
7148 break;
7149 } else {
7150 search_start = 0;
7151 }
7152 }
7153 }
7154 }
7155
7156 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7157 let snapshot = self
7158 .display_map
7159 .update(cx, |display_map, cx| display_map.snapshot(cx));
7160 let selection = self.selections.newest::<Point>(cx);
7161
7162 if !self.seek_in_direction(
7163 &snapshot,
7164 selection.head(),
7165 false,
7166 snapshot
7167 .buffer_snapshot
7168 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7169 cx,
7170 ) {
7171 let wrapped_point = Point::zero();
7172 self.seek_in_direction(
7173 &snapshot,
7174 wrapped_point,
7175 true,
7176 snapshot
7177 .buffer_snapshot
7178 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7179 cx,
7180 );
7181 }
7182 }
7183
7184 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7185 let snapshot = self
7186 .display_map
7187 .update(cx, |display_map, cx| display_map.snapshot(cx));
7188 let selection = self.selections.newest::<Point>(cx);
7189
7190 if !self.seek_in_direction(
7191 &snapshot,
7192 selection.head(),
7193 false,
7194 snapshot
7195 .buffer_snapshot
7196 .git_diff_hunks_in_range_rev(0..selection.head().row),
7197 cx,
7198 ) {
7199 let wrapped_point = snapshot.buffer_snapshot.max_point();
7200 self.seek_in_direction(
7201 &snapshot,
7202 wrapped_point,
7203 true,
7204 snapshot
7205 .buffer_snapshot
7206 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7207 cx,
7208 );
7209 }
7210 }
7211
7212 fn seek_in_direction(
7213 &mut self,
7214 snapshot: &DisplaySnapshot,
7215 initial_point: Point,
7216 is_wrapped: bool,
7217 hunks: impl Iterator<Item = DiffHunk<u32>>,
7218 cx: &mut ViewContext<Editor>,
7219 ) -> bool {
7220 let display_point = initial_point.to_display_point(snapshot);
7221 let mut hunks = hunks
7222 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7223 .skip_while(|hunk| {
7224 if is_wrapped {
7225 false
7226 } else {
7227 hunk.contains_display_row(display_point.row())
7228 }
7229 })
7230 .dedup();
7231
7232 if let Some(hunk) = hunks.next() {
7233 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7234 let row = hunk.start_display_row();
7235 let point = DisplayPoint::new(row, 0);
7236 s.select_display_ranges([point..point]);
7237 });
7238
7239 true
7240 } else {
7241 false
7242 }
7243 }
7244
7245 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7246 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7247 }
7248
7249 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7250 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7251 }
7252
7253 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7254 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7255 }
7256
7257 pub fn go_to_type_definition_split(
7258 &mut self,
7259 _: &GoToTypeDefinitionSplit,
7260 cx: &mut ViewContext<Self>,
7261 ) {
7262 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7263 }
7264
7265 fn go_to_definition_of_kind(
7266 &mut self,
7267 kind: GotoDefinitionKind,
7268 split: bool,
7269 cx: &mut ViewContext<Self>,
7270 ) {
7271 let Some(workspace) = self.workspace(cx) else {
7272 return;
7273 };
7274 let buffer = self.buffer.read(cx);
7275 let head = self.selections.newest::<usize>(cx).head();
7276 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7277 text_anchor
7278 } else {
7279 return;
7280 };
7281
7282 let project = workspace.read(cx).project().clone();
7283 let definitions = project.update(cx, |project, cx| match kind {
7284 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7285 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7286 });
7287
7288 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
7289 let definitions = definitions.await?;
7290 editor.update(&mut cx, |editor, cx| {
7291 editor.navigate_to_definitions(
7292 definitions
7293 .into_iter()
7294 .map(GoToDefinitionLink::Text)
7295 .collect(),
7296 split,
7297 cx,
7298 );
7299 })?;
7300 Ok::<(), anyhow::Error>(())
7301 })
7302 .detach_and_log_err(cx);
7303 }
7304
7305 pub fn navigate_to_definitions(
7306 &mut self,
7307 mut definitions: Vec<GoToDefinitionLink>,
7308 split: bool,
7309 cx: &mut ViewContext<Editor>,
7310 ) {
7311 let Some(workspace) = self.workspace(cx) else {
7312 return;
7313 };
7314 let pane = workspace.read(cx).active_pane().clone();
7315 // If there is one definition, just open it directly
7316 if definitions.len() == 1 {
7317 let definition = definitions.pop().unwrap();
7318 let target_task = match definition {
7319 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7320 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7321 self.compute_target_location(lsp_location, server_id, cx)
7322 }
7323 };
7324 cx.spawn(|editor, mut cx| async move {
7325 let target = target_task.await.context("target resolution task")?;
7326 if let Some(target) = target {
7327 editor.update(&mut cx, |editor, cx| {
7328 let range = target.range.to_offset(target.buffer.read(cx));
7329 let range = editor.range_for_match(&range);
7330 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7331 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7332 s.select_ranges([range]);
7333 });
7334 } else {
7335 cx.window_context().defer(move |cx| {
7336 let target_editor: ViewHandle<Self> =
7337 workspace.update(cx, |workspace, cx| {
7338 if split {
7339 workspace.split_project_item(target.buffer.clone(), cx)
7340 } else {
7341 workspace.open_project_item(target.buffer.clone(), cx)
7342 }
7343 });
7344 target_editor.update(cx, |target_editor, cx| {
7345 // When selecting a definition in a different buffer, disable the nav history
7346 // to avoid creating a history entry at the previous cursor location.
7347 pane.update(cx, |pane, _| pane.disable_history());
7348 target_editor.change_selections(
7349 Some(Autoscroll::fit()),
7350 cx,
7351 |s| {
7352 s.select_ranges([range]);
7353 },
7354 );
7355 pane.update(cx, |pane, _| pane.enable_history());
7356 });
7357 });
7358 }
7359 })
7360 } else {
7361 Ok(())
7362 }
7363 })
7364 .detach_and_log_err(cx);
7365 } else if !definitions.is_empty() {
7366 let replica_id = self.replica_id(cx);
7367 cx.spawn(|editor, mut cx| async move {
7368 let (title, location_tasks) = editor
7369 .update(&mut cx, |editor, cx| {
7370 let title = definitions
7371 .iter()
7372 .find_map(|definition| match definition {
7373 GoToDefinitionLink::Text(link) => {
7374 link.origin.as_ref().map(|origin| {
7375 let buffer = origin.buffer.read(cx);
7376 format!(
7377 "Definitions for {}",
7378 buffer
7379 .text_for_range(origin.range.clone())
7380 .collect::<String>()
7381 )
7382 })
7383 }
7384 GoToDefinitionLink::InlayHint(_, _) => None,
7385 })
7386 .unwrap_or("Definitions".to_string());
7387 let location_tasks = definitions
7388 .into_iter()
7389 .map(|definition| match definition {
7390 GoToDefinitionLink::Text(link) => {
7391 Task::Ready(Some(Ok(Some(link.target))))
7392 }
7393 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7394 editor.compute_target_location(lsp_location, server_id, cx)
7395 }
7396 })
7397 .collect::<Vec<_>>();
7398 (title, location_tasks)
7399 })
7400 .context("location tasks preparation")?;
7401
7402 let locations = futures::future::join_all(location_tasks)
7403 .await
7404 .into_iter()
7405 .filter_map(|location| location.transpose())
7406 .collect::<Result<_>>()
7407 .context("location tasks")?;
7408 workspace.update(&mut cx, |workspace, cx| {
7409 Self::open_locations_in_multibuffer(
7410 workspace, locations, replica_id, title, split, cx,
7411 )
7412 });
7413
7414 anyhow::Ok(())
7415 })
7416 .detach_and_log_err(cx);
7417 }
7418 }
7419
7420 fn compute_target_location(
7421 &self,
7422 lsp_location: lsp::Location,
7423 server_id: LanguageServerId,
7424 cx: &mut ViewContext<Editor>,
7425 ) -> Task<anyhow::Result<Option<Location>>> {
7426 let Some(project) = self.project.clone() else {
7427 return Task::Ready(Some(Ok(None)));
7428 };
7429
7430 cx.spawn(move |editor, mut cx| async move {
7431 let location_task = editor.update(&mut cx, |editor, cx| {
7432 project.update(cx, |project, cx| {
7433 let language_server_name =
7434 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7435 project
7436 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7437 .map(|(_, lsp_adapter)| {
7438 LanguageServerName(Arc::from(lsp_adapter.name()))
7439 })
7440 });
7441 language_server_name.map(|language_server_name| {
7442 project.open_local_buffer_via_lsp(
7443 lsp_location.uri.clone(),
7444 server_id,
7445 language_server_name,
7446 cx,
7447 )
7448 })
7449 })
7450 })?;
7451 let location = match location_task {
7452 Some(task) => Some({
7453 let target_buffer_handle = task.await.context("open local buffer")?;
7454 let range = {
7455 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7456 let target_start = target_buffer.clip_point_utf16(
7457 point_from_lsp(lsp_location.range.start),
7458 Bias::Left,
7459 );
7460 let target_end = target_buffer.clip_point_utf16(
7461 point_from_lsp(lsp_location.range.end),
7462 Bias::Left,
7463 );
7464 target_buffer.anchor_after(target_start)
7465 ..target_buffer.anchor_before(target_end)
7466 })
7467 };
7468 Location {
7469 buffer: target_buffer_handle,
7470 range,
7471 }
7472 }),
7473 None => None,
7474 };
7475 Ok(location)
7476 })
7477 }
7478
7479 pub fn find_all_references(
7480 workspace: &mut Workspace,
7481 _: &FindAllReferences,
7482 cx: &mut ViewContext<Workspace>,
7483 ) -> Option<Task<Result<()>>> {
7484 let active_item = workspace.active_item(cx)?;
7485 let editor_handle = active_item.act_as::<Self>(cx)?;
7486
7487 let editor = editor_handle.read(cx);
7488 let buffer = editor.buffer.read(cx);
7489 let head = editor.selections.newest::<usize>(cx).head();
7490 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7491 let replica_id = editor.replica_id(cx);
7492
7493 let project = workspace.project().clone();
7494 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7495 Some(cx.spawn_labeled(
7496 "Finding All References...",
7497 |workspace, mut cx| async move {
7498 let locations = references.await?;
7499 if locations.is_empty() {
7500 return Ok(());
7501 }
7502
7503 workspace.update(&mut cx, |workspace, cx| {
7504 let title = locations
7505 .first()
7506 .as_ref()
7507 .map(|location| {
7508 let buffer = location.buffer.read(cx);
7509 format!(
7510 "References to `{}`",
7511 buffer
7512 .text_for_range(location.range.clone())
7513 .collect::<String>()
7514 )
7515 })
7516 .unwrap();
7517 Self::open_locations_in_multibuffer(
7518 workspace, locations, replica_id, title, false, cx,
7519 );
7520 })?;
7521
7522 Ok(())
7523 },
7524 ))
7525 }
7526
7527 /// Opens a multibuffer with the given project locations in it
7528 pub fn open_locations_in_multibuffer(
7529 workspace: &mut Workspace,
7530 mut locations: Vec<Location>,
7531 replica_id: ReplicaId,
7532 title: String,
7533 split: bool,
7534 cx: &mut ViewContext<Workspace>,
7535 ) {
7536 // If there are multiple definitions, open them in a multibuffer
7537 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7538 let mut locations = locations.into_iter().peekable();
7539 let mut ranges_to_highlight = Vec::new();
7540
7541 let excerpt_buffer = cx.add_model(|cx| {
7542 let mut multibuffer = MultiBuffer::new(replica_id);
7543 while let Some(location) = locations.next() {
7544 let buffer = location.buffer.read(cx);
7545 let mut ranges_for_buffer = Vec::new();
7546 let range = location.range.to_offset(buffer);
7547 ranges_for_buffer.push(range.clone());
7548
7549 while let Some(next_location) = locations.peek() {
7550 if next_location.buffer == location.buffer {
7551 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7552 locations.next();
7553 } else {
7554 break;
7555 }
7556 }
7557
7558 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7559 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7560 location.buffer.clone(),
7561 ranges_for_buffer,
7562 1,
7563 cx,
7564 ))
7565 }
7566
7567 multibuffer.with_title(title)
7568 });
7569
7570 let editor = cx.add_view(|cx| {
7571 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7572 });
7573 editor.update(cx, |editor, cx| {
7574 editor.highlight_background::<Self>(
7575 ranges_to_highlight,
7576 |theme| theme.editor.highlighted_line_background,
7577 cx,
7578 );
7579 });
7580 if split {
7581 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7582 } else {
7583 workspace.add_item(Box::new(editor), cx);
7584 }
7585 }
7586
7587 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7588 use language::ToOffset as _;
7589
7590 let project = self.project.clone()?;
7591 let selection = self.selections.newest_anchor().clone();
7592 let (cursor_buffer, cursor_buffer_position) = self
7593 .buffer
7594 .read(cx)
7595 .text_anchor_for_position(selection.head(), cx)?;
7596 let (tail_buffer, _) = self
7597 .buffer
7598 .read(cx)
7599 .text_anchor_for_position(selection.tail(), cx)?;
7600 if tail_buffer != cursor_buffer {
7601 return None;
7602 }
7603
7604 let snapshot = cursor_buffer.read(cx).snapshot();
7605 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7606 let prepare_rename = project.update(cx, |project, cx| {
7607 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7608 });
7609
7610 Some(cx.spawn(|this, mut cx| async move {
7611 let rename_range = if let Some(range) = prepare_rename.await? {
7612 Some(range)
7613 } else {
7614 this.update(&mut cx, |this, cx| {
7615 let buffer = this.buffer.read(cx).snapshot(cx);
7616 let mut buffer_highlights = this
7617 .document_highlights_for_position(selection.head(), &buffer)
7618 .filter(|highlight| {
7619 highlight.start.excerpt_id() == selection.head().excerpt_id()
7620 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7621 });
7622 buffer_highlights
7623 .next()
7624 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7625 })?
7626 };
7627 if let Some(rename_range) = rename_range {
7628 let rename_buffer_range = rename_range.to_offset(&snapshot);
7629 let cursor_offset_in_rename_range =
7630 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7631
7632 this.update(&mut cx, |this, cx| {
7633 this.take_rename(false, cx);
7634 let style = this.style(cx);
7635 let buffer = this.buffer.read(cx).read(cx);
7636 let cursor_offset = selection.head().to_offset(&buffer);
7637 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7638 let rename_end = rename_start + rename_buffer_range.len();
7639 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7640 let mut old_highlight_id = None;
7641 let old_name: Arc<str> = buffer
7642 .chunks(rename_start..rename_end, true)
7643 .map(|chunk| {
7644 if old_highlight_id.is_none() {
7645 old_highlight_id = chunk.syntax_highlight_id;
7646 }
7647 chunk.text
7648 })
7649 .collect::<String>()
7650 .into();
7651
7652 drop(buffer);
7653
7654 // Position the selection in the rename editor so that it matches the current selection.
7655 this.show_local_selections = false;
7656 let rename_editor = cx.add_view(|cx| {
7657 let mut editor = Editor::single_line(None, cx);
7658 if let Some(old_highlight_id) = old_highlight_id {
7659 editor.override_text_style =
7660 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7661 }
7662 editor.buffer.update(cx, |buffer, cx| {
7663 buffer.edit([(0..0, old_name.clone())], None, cx)
7664 });
7665 editor.select_all(&SelectAll, cx);
7666 editor
7667 });
7668
7669 let ranges = this
7670 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7671 .into_iter()
7672 .flat_map(|(_, ranges)| ranges.into_iter())
7673 .chain(
7674 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7675 .into_iter()
7676 .flat_map(|(_, ranges)| ranges.into_iter()),
7677 )
7678 .collect();
7679
7680 this.highlight_text::<Rename>(
7681 ranges,
7682 HighlightStyle {
7683 fade_out: Some(style.rename_fade),
7684 ..Default::default()
7685 },
7686 cx,
7687 );
7688 cx.focus(&rename_editor);
7689 let block_id = this.insert_blocks(
7690 [BlockProperties {
7691 style: BlockStyle::Flex,
7692 position: range.start.clone(),
7693 height: 1,
7694 render: Arc::new({
7695 let editor = rename_editor.clone();
7696 move |cx: &mut BlockContext| {
7697 ChildView::new(&editor, cx)
7698 .contained()
7699 .with_padding_left(cx.anchor_x)
7700 .into_any()
7701 }
7702 }),
7703 disposition: BlockDisposition::Below,
7704 }],
7705 Some(Autoscroll::fit()),
7706 cx,
7707 )[0];
7708 this.pending_rename = Some(RenameState {
7709 range,
7710 old_name,
7711 editor: rename_editor,
7712 block_id,
7713 });
7714 })?;
7715 }
7716
7717 Ok(())
7718 }))
7719 }
7720
7721 pub fn confirm_rename(
7722 workspace: &mut Workspace,
7723 _: &ConfirmRename,
7724 cx: &mut ViewContext<Workspace>,
7725 ) -> Option<Task<Result<()>>> {
7726 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7727
7728 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7729 let rename = editor.take_rename(false, cx)?;
7730 let buffer = editor.buffer.read(cx);
7731 let (start_buffer, start) =
7732 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7733 let (end_buffer, end) =
7734 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7735 if start_buffer == end_buffer {
7736 let new_name = rename.editor.read(cx).text(cx);
7737 Some((start_buffer, start..end, rename.old_name, new_name))
7738 } else {
7739 None
7740 }
7741 })?;
7742
7743 let rename = workspace.project().clone().update(cx, |project, cx| {
7744 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7745 });
7746
7747 let editor = editor.downgrade();
7748 Some(cx.spawn(|workspace, mut cx| async move {
7749 let project_transaction = rename.await?;
7750 Self::open_project_transaction(
7751 &editor,
7752 workspace,
7753 project_transaction,
7754 format!("Rename: {} → {}", old_name, new_name),
7755 cx.clone(),
7756 )
7757 .await?;
7758
7759 editor.update(&mut cx, |editor, cx| {
7760 editor.refresh_document_highlights(cx);
7761 })?;
7762 Ok(())
7763 }))
7764 }
7765
7766 fn take_rename(
7767 &mut self,
7768 moving_cursor: bool,
7769 cx: &mut ViewContext<Self>,
7770 ) -> Option<RenameState> {
7771 let rename = self.pending_rename.take()?;
7772 self.remove_blocks(
7773 [rename.block_id].into_iter().collect(),
7774 Some(Autoscroll::fit()),
7775 cx,
7776 );
7777 self.clear_highlights::<Rename>(cx);
7778 self.show_local_selections = true;
7779
7780 if moving_cursor {
7781 let rename_editor = rename.editor.read(cx);
7782 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7783
7784 // Update the selection to match the position of the selection inside
7785 // the rename editor.
7786 let snapshot = self.buffer.read(cx).read(cx);
7787 let rename_range = rename.range.to_offset(&snapshot);
7788 let cursor_in_editor = snapshot
7789 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7790 .min(rename_range.end);
7791 drop(snapshot);
7792
7793 self.change_selections(None, cx, |s| {
7794 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7795 });
7796 } else {
7797 self.refresh_document_highlights(cx);
7798 }
7799
7800 Some(rename)
7801 }
7802
7803 #[cfg(any(test, feature = "test-support"))]
7804 pub fn pending_rename(&self) -> Option<&RenameState> {
7805 self.pending_rename.as_ref()
7806 }
7807
7808 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7809 let project = match &self.project {
7810 Some(project) => project.clone(),
7811 None => return None,
7812 };
7813
7814 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7815 }
7816
7817 fn perform_format(
7818 &mut self,
7819 project: ModelHandle<Project>,
7820 trigger: FormatTrigger,
7821 cx: &mut ViewContext<Self>,
7822 ) -> Task<Result<()>> {
7823 let buffer = self.buffer().clone();
7824 let buffers = buffer.read(cx).all_buffers();
7825
7826 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7827 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7828
7829 cx.spawn(|_, mut cx| async move {
7830 let transaction = futures::select_biased! {
7831 _ = timeout => {
7832 log::warn!("timed out waiting for formatting");
7833 None
7834 }
7835 transaction = format.log_err().fuse() => transaction,
7836 };
7837
7838 buffer.update(&mut cx, |buffer, cx| {
7839 if let Some(transaction) = transaction {
7840 if !buffer.is_singleton() {
7841 buffer.push_transaction(&transaction.0, cx);
7842 }
7843 }
7844
7845 cx.notify();
7846 });
7847
7848 Ok(())
7849 })
7850 }
7851
7852 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7853 if let Some(project) = self.project.clone() {
7854 self.buffer.update(cx, |multi_buffer, cx| {
7855 project.update(cx, |project, cx| {
7856 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7857 });
7858 })
7859 }
7860 }
7861
7862 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7863 cx.show_character_palette();
7864 }
7865
7866 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7867 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7868 let buffer = self.buffer.read(cx).snapshot(cx);
7869 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7870 let is_valid = buffer
7871 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7872 .any(|entry| {
7873 entry.diagnostic.is_primary
7874 && !entry.range.is_empty()
7875 && entry.range.start == primary_range_start
7876 && entry.diagnostic.message == active_diagnostics.primary_message
7877 });
7878
7879 if is_valid != active_diagnostics.is_valid {
7880 active_diagnostics.is_valid = is_valid;
7881 let mut new_styles = HashMap::default();
7882 for (block_id, diagnostic) in &active_diagnostics.blocks {
7883 new_styles.insert(
7884 *block_id,
7885 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7886 );
7887 }
7888 self.display_map
7889 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7890 }
7891 }
7892 }
7893
7894 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7895 self.dismiss_diagnostics(cx);
7896 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7897 let buffer = self.buffer.read(cx).snapshot(cx);
7898
7899 let mut primary_range = None;
7900 let mut primary_message = None;
7901 let mut group_end = Point::zero();
7902 let diagnostic_group = buffer
7903 .diagnostic_group::<Point>(group_id)
7904 .map(|entry| {
7905 if entry.range.end > group_end {
7906 group_end = entry.range.end;
7907 }
7908 if entry.diagnostic.is_primary {
7909 primary_range = Some(entry.range.clone());
7910 primary_message = Some(entry.diagnostic.message.clone());
7911 }
7912 entry
7913 })
7914 .collect::<Vec<_>>();
7915 let primary_range = primary_range?;
7916 let primary_message = primary_message?;
7917 let primary_range =
7918 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7919
7920 let blocks = display_map
7921 .insert_blocks(
7922 diagnostic_group.iter().map(|entry| {
7923 let diagnostic = entry.diagnostic.clone();
7924 let message_height = diagnostic.message.lines().count() as u8;
7925 BlockProperties {
7926 style: BlockStyle::Fixed,
7927 position: buffer.anchor_after(entry.range.start),
7928 height: message_height,
7929 render: diagnostic_block_renderer(diagnostic, true),
7930 disposition: BlockDisposition::Below,
7931 }
7932 }),
7933 cx,
7934 )
7935 .into_iter()
7936 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7937 .collect();
7938
7939 Some(ActiveDiagnosticGroup {
7940 primary_range,
7941 primary_message,
7942 blocks,
7943 is_valid: true,
7944 })
7945 });
7946 self.active_diagnostics.is_some()
7947 }
7948
7949 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7950 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7951 self.display_map.update(cx, |display_map, cx| {
7952 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7953 });
7954 cx.notify();
7955 }
7956 }
7957
7958 pub fn set_selections_from_remote(
7959 &mut self,
7960 selections: Vec<Selection<Anchor>>,
7961 pending_selection: Option<Selection<Anchor>>,
7962 cx: &mut ViewContext<Self>,
7963 ) {
7964 let old_cursor_position = self.selections.newest_anchor().head();
7965 self.selections.change_with(cx, |s| {
7966 s.select_anchors(selections);
7967 if let Some(pending_selection) = pending_selection {
7968 s.set_pending(pending_selection, SelectMode::Character);
7969 } else {
7970 s.clear_pending();
7971 }
7972 });
7973 self.selections_did_change(false, &old_cursor_position, cx);
7974 }
7975
7976 fn push_to_selection_history(&mut self) {
7977 self.selection_history.push(SelectionHistoryEntry {
7978 selections: self.selections.disjoint_anchors(),
7979 select_next_state: self.select_next_state.clone(),
7980 select_prev_state: self.select_prev_state.clone(),
7981 add_selections_state: self.add_selections_state.clone(),
7982 });
7983 }
7984
7985 pub fn transact(
7986 &mut self,
7987 cx: &mut ViewContext<Self>,
7988 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7989 ) -> Option<TransactionId> {
7990 self.start_transaction_at(Instant::now(), cx);
7991 update(self, cx);
7992 self.end_transaction_at(Instant::now(), cx)
7993 }
7994
7995 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7996 self.end_selection(cx);
7997 if let Some(tx_id) = self
7998 .buffer
7999 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8000 {
8001 self.selection_history
8002 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8003 }
8004 }
8005
8006 fn end_transaction_at(
8007 &mut self,
8008 now: Instant,
8009 cx: &mut ViewContext<Self>,
8010 ) -> Option<TransactionId> {
8011 if let Some(tx_id) = self
8012 .buffer
8013 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8014 {
8015 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8016 *end_selections = Some(self.selections.disjoint_anchors());
8017 } else {
8018 error!("unexpectedly ended a transaction that wasn't started by this editor");
8019 }
8020
8021 cx.emit(Event::Edited);
8022 Some(tx_id)
8023 } else {
8024 None
8025 }
8026 }
8027
8028 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8029 let mut fold_ranges = Vec::new();
8030
8031 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8032
8033 let selections = self.selections.all_adjusted(cx);
8034 for selection in selections {
8035 let range = selection.range().sorted();
8036 let buffer_start_row = range.start.row;
8037
8038 for row in (0..=range.end.row).rev() {
8039 let fold_range = display_map.foldable_range(row);
8040
8041 if let Some(fold_range) = fold_range {
8042 if fold_range.end.row >= buffer_start_row {
8043 fold_ranges.push(fold_range);
8044 if row <= range.start.row {
8045 break;
8046 }
8047 }
8048 }
8049 }
8050 }
8051
8052 self.fold_ranges(fold_ranges, true, cx);
8053 }
8054
8055 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8056 let buffer_row = fold_at.buffer_row;
8057 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8058
8059 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8060 let autoscroll = self
8061 .selections
8062 .all::<Point>(cx)
8063 .iter()
8064 .any(|selection| fold_range.overlaps(&selection.range()));
8065
8066 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8067 }
8068 }
8069
8070 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8072 let buffer = &display_map.buffer_snapshot;
8073 let selections = self.selections.all::<Point>(cx);
8074 let ranges = selections
8075 .iter()
8076 .map(|s| {
8077 let range = s.display_range(&display_map).sorted();
8078 let mut start = range.start.to_point(&display_map);
8079 let mut end = range.end.to_point(&display_map);
8080 start.column = 0;
8081 end.column = buffer.line_len(end.row);
8082 start..end
8083 })
8084 .collect::<Vec<_>>();
8085
8086 self.unfold_ranges(ranges, true, true, cx);
8087 }
8088
8089 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8090 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8091
8092 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8093 ..Point::new(
8094 unfold_at.buffer_row,
8095 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8096 );
8097
8098 let autoscroll = self
8099 .selections
8100 .all::<Point>(cx)
8101 .iter()
8102 .any(|selection| selection.range().overlaps(&intersection_range));
8103
8104 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8105 }
8106
8107 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8108 let selections = self.selections.all::<Point>(cx);
8109 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8110 let line_mode = self.selections.line_mode;
8111 let ranges = selections.into_iter().map(|s| {
8112 if line_mode {
8113 let start = Point::new(s.start.row, 0);
8114 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8115 start..end
8116 } else {
8117 s.start..s.end
8118 }
8119 });
8120 self.fold_ranges(ranges, true, cx);
8121 }
8122
8123 pub fn fold_ranges<T: ToOffset + Clone>(
8124 &mut self,
8125 ranges: impl IntoIterator<Item = Range<T>>,
8126 auto_scroll: bool,
8127 cx: &mut ViewContext<Self>,
8128 ) {
8129 let mut ranges = ranges.into_iter().peekable();
8130 if ranges.peek().is_some() {
8131 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8132
8133 if auto_scroll {
8134 self.request_autoscroll(Autoscroll::fit(), cx);
8135 }
8136
8137 cx.notify();
8138 }
8139 }
8140
8141 pub fn unfold_ranges<T: ToOffset + Clone>(
8142 &mut self,
8143 ranges: impl IntoIterator<Item = Range<T>>,
8144 inclusive: bool,
8145 auto_scroll: bool,
8146 cx: &mut ViewContext<Self>,
8147 ) {
8148 let mut ranges = ranges.into_iter().peekable();
8149 if ranges.peek().is_some() {
8150 self.display_map
8151 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8152 if auto_scroll {
8153 self.request_autoscroll(Autoscroll::fit(), cx);
8154 }
8155
8156 cx.notify();
8157 }
8158 }
8159
8160 pub fn gutter_hover(
8161 &mut self,
8162 GutterHover { hovered }: &GutterHover,
8163 cx: &mut ViewContext<Self>,
8164 ) {
8165 self.gutter_hovered = *hovered;
8166 cx.notify();
8167 }
8168
8169 pub fn insert_blocks(
8170 &mut self,
8171 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8172 autoscroll: Option<Autoscroll>,
8173 cx: &mut ViewContext<Self>,
8174 ) -> Vec<BlockId> {
8175 let blocks = self
8176 .display_map
8177 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8178 if let Some(autoscroll) = autoscroll {
8179 self.request_autoscroll(autoscroll, cx);
8180 }
8181 blocks
8182 }
8183
8184 pub fn replace_blocks(
8185 &mut self,
8186 blocks: HashMap<BlockId, RenderBlock>,
8187 autoscroll: Option<Autoscroll>,
8188 cx: &mut ViewContext<Self>,
8189 ) {
8190 self.display_map
8191 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8192 if let Some(autoscroll) = autoscroll {
8193 self.request_autoscroll(autoscroll, cx);
8194 }
8195 }
8196
8197 pub fn remove_blocks(
8198 &mut self,
8199 block_ids: HashSet<BlockId>,
8200 autoscroll: Option<Autoscroll>,
8201 cx: &mut ViewContext<Self>,
8202 ) {
8203 self.display_map.update(cx, |display_map, cx| {
8204 display_map.remove_blocks(block_ids, cx)
8205 });
8206 if let Some(autoscroll) = autoscroll {
8207 self.request_autoscroll(autoscroll, cx);
8208 }
8209 }
8210
8211 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8212 self.display_map
8213 .update(cx, |map, cx| map.snapshot(cx))
8214 .longest_row()
8215 }
8216
8217 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8218 self.display_map
8219 .update(cx, |map, cx| map.snapshot(cx))
8220 .max_point()
8221 }
8222
8223 pub fn text(&self, cx: &AppContext) -> String {
8224 self.buffer.read(cx).read(cx).text()
8225 }
8226
8227 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8228 self.transact(cx, |this, cx| {
8229 this.buffer
8230 .read(cx)
8231 .as_singleton()
8232 .expect("you can only call set_text on editors for singleton buffers")
8233 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8234 });
8235 }
8236
8237 pub fn display_text(&self, cx: &mut AppContext) -> String {
8238 self.display_map
8239 .update(cx, |map, cx| map.snapshot(cx))
8240 .text()
8241 }
8242
8243 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8244 let mut wrap_guides = smallvec::smallvec![];
8245
8246 if self.show_wrap_guides == Some(false) {
8247 return wrap_guides;
8248 }
8249
8250 let settings = self.buffer.read(cx).settings_at(0, cx);
8251 if settings.show_wrap_guides {
8252 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8253 wrap_guides.push((soft_wrap as usize, true));
8254 }
8255 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8256 }
8257
8258 wrap_guides
8259 }
8260
8261 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8262 let settings = self.buffer.read(cx).settings_at(0, cx);
8263 let mode = self
8264 .soft_wrap_mode_override
8265 .unwrap_or_else(|| settings.soft_wrap);
8266 match mode {
8267 language_settings::SoftWrap::None => SoftWrap::None,
8268 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8269 language_settings::SoftWrap::PreferredLineLength => {
8270 SoftWrap::Column(settings.preferred_line_length)
8271 }
8272 }
8273 }
8274
8275 pub fn set_soft_wrap_mode(
8276 &mut self,
8277 mode: language_settings::SoftWrap,
8278 cx: &mut ViewContext<Self>,
8279 ) {
8280 self.soft_wrap_mode_override = Some(mode);
8281 cx.notify();
8282 }
8283
8284 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
8285 self.display_map
8286 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8287 }
8288
8289 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8290 if self.soft_wrap_mode_override.is_some() {
8291 self.soft_wrap_mode_override.take();
8292 } else {
8293 let soft_wrap = match self.soft_wrap_mode(cx) {
8294 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8295 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8296 };
8297 self.soft_wrap_mode_override = Some(soft_wrap);
8298 }
8299 cx.notify();
8300 }
8301
8302 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8303 self.show_gutter = show_gutter;
8304 cx.notify();
8305 }
8306
8307 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8308 self.show_wrap_guides = Some(show_gutter);
8309 cx.notify();
8310 }
8311
8312 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8313 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8314 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8315 cx.reveal_path(&file.abs_path(cx));
8316 }
8317 }
8318 }
8319
8320 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8321 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8322 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8323 if let Some(path) = file.abs_path(cx).to_str() {
8324 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8325 }
8326 }
8327 }
8328 }
8329
8330 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8331 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8332 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8333 if let Some(path) = file.path().to_str() {
8334 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8335 }
8336 }
8337 }
8338 }
8339
8340 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8341 self.highlighted_rows = rows;
8342 }
8343
8344 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8345 self.highlighted_rows.clone()
8346 }
8347
8348 pub fn highlight_background<T: 'static>(
8349 &mut self,
8350 ranges: Vec<Range<Anchor>>,
8351 color_fetcher: fn(&Theme) -> Color,
8352 cx: &mut ViewContext<Self>,
8353 ) {
8354 self.background_highlights
8355 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8356 cx.notify();
8357 }
8358
8359 pub fn highlight_inlay_background<T: 'static>(
8360 &mut self,
8361 ranges: Vec<InlayHighlight>,
8362 color_fetcher: fn(&Theme) -> Color,
8363 cx: &mut ViewContext<Self>,
8364 ) {
8365 // TODO: no actual highlights happen for inlays currently, find a way to do that
8366 self.inlay_background_highlights
8367 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8368 cx.notify();
8369 }
8370
8371 pub fn clear_background_highlights<T: 'static>(
8372 &mut self,
8373 cx: &mut ViewContext<Self>,
8374 ) -> Option<BackgroundHighlight> {
8375 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8376 let inlay_highlights = self
8377 .inlay_background_highlights
8378 .remove(&Some(TypeId::of::<T>()));
8379 if text_highlights.is_some() || inlay_highlights.is_some() {
8380 cx.notify();
8381 }
8382 text_highlights
8383 }
8384
8385 #[cfg(feature = "test-support")]
8386 pub fn all_text_background_highlights(
8387 &mut self,
8388 cx: &mut ViewContext<Self>,
8389 ) -> Vec<(Range<DisplayPoint>, Color)> {
8390 let snapshot = self.snapshot(cx);
8391 let buffer = &snapshot.buffer_snapshot;
8392 let start = buffer.anchor_before(0);
8393 let end = buffer.anchor_after(buffer.len());
8394 let theme = theme::current(cx);
8395 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
8396 }
8397
8398 fn document_highlights_for_position<'a>(
8399 &'a self,
8400 position: Anchor,
8401 buffer: &'a MultiBufferSnapshot,
8402 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8403 let read_highlights = self
8404 .background_highlights
8405 .get(&TypeId::of::<DocumentHighlightRead>())
8406 .map(|h| &h.1);
8407 let write_highlights = self
8408 .background_highlights
8409 .get(&TypeId::of::<DocumentHighlightWrite>())
8410 .map(|h| &h.1);
8411 let left_position = position.bias_left(buffer);
8412 let right_position = position.bias_right(buffer);
8413 read_highlights
8414 .into_iter()
8415 .chain(write_highlights)
8416 .flat_map(move |ranges| {
8417 let start_ix = match ranges.binary_search_by(|probe| {
8418 let cmp = probe.end.cmp(&left_position, buffer);
8419 if cmp.is_ge() {
8420 Ordering::Greater
8421 } else {
8422 Ordering::Less
8423 }
8424 }) {
8425 Ok(i) | Err(i) => i,
8426 };
8427
8428 let right_position = right_position.clone();
8429 ranges[start_ix..]
8430 .iter()
8431 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8432 })
8433 }
8434
8435 pub fn background_highlights_in_range(
8436 &self,
8437 search_range: Range<Anchor>,
8438 display_snapshot: &DisplaySnapshot,
8439 theme: &Theme,
8440 ) -> Vec<(Range<DisplayPoint>, Color)> {
8441 let mut results = Vec::new();
8442 for (color_fetcher, ranges) in self.background_highlights.values() {
8443 let color = color_fetcher(theme);
8444 let start_ix = match ranges.binary_search_by(|probe| {
8445 let cmp = probe
8446 .end
8447 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8448 if cmp.is_gt() {
8449 Ordering::Greater
8450 } else {
8451 Ordering::Less
8452 }
8453 }) {
8454 Ok(i) | Err(i) => i,
8455 };
8456 for range in &ranges[start_ix..] {
8457 if range
8458 .start
8459 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8460 .is_ge()
8461 {
8462 break;
8463 }
8464
8465 let start = range.start.to_display_point(&display_snapshot);
8466 let end = range.end.to_display_point(&display_snapshot);
8467 results.push((start..end, color))
8468 }
8469 }
8470 results
8471 }
8472
8473 pub fn background_highlight_row_ranges<T: 'static>(
8474 &self,
8475 search_range: Range<Anchor>,
8476 display_snapshot: &DisplaySnapshot,
8477 count: usize,
8478 ) -> Vec<RangeInclusive<DisplayPoint>> {
8479 let mut results = Vec::new();
8480 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8481 return vec![];
8482 };
8483
8484 let start_ix = match ranges.binary_search_by(|probe| {
8485 let cmp = probe
8486 .end
8487 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8488 if cmp.is_gt() {
8489 Ordering::Greater
8490 } else {
8491 Ordering::Less
8492 }
8493 }) {
8494 Ok(i) | Err(i) => i,
8495 };
8496 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8497 if let (Some(start_display), Some(end_display)) = (start, end) {
8498 results.push(
8499 start_display.to_display_point(display_snapshot)
8500 ..=end_display.to_display_point(display_snapshot),
8501 );
8502 }
8503 };
8504 let mut start_row: Option<Point> = None;
8505 let mut end_row: Option<Point> = None;
8506 if ranges.len() > count {
8507 return Vec::new();
8508 }
8509 for range in &ranges[start_ix..] {
8510 if range
8511 .start
8512 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8513 .is_ge()
8514 {
8515 break;
8516 }
8517 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8518 if let Some(current_row) = &end_row {
8519 if end.row == current_row.row {
8520 continue;
8521 }
8522 }
8523 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8524 if start_row.is_none() {
8525 assert_eq!(end_row, None);
8526 start_row = Some(start);
8527 end_row = Some(end);
8528 continue;
8529 }
8530 if let Some(current_end) = end_row.as_mut() {
8531 if start.row > current_end.row + 1 {
8532 push_region(start_row, end_row);
8533 start_row = Some(start);
8534 end_row = Some(end);
8535 } else {
8536 // Merge two hunks.
8537 *current_end = end;
8538 }
8539 } else {
8540 unreachable!();
8541 }
8542 }
8543 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8544 push_region(start_row, end_row);
8545 results
8546 }
8547
8548 pub fn highlight_text<T: 'static>(
8549 &mut self,
8550 ranges: Vec<Range<Anchor>>,
8551 style: HighlightStyle,
8552 cx: &mut ViewContext<Self>,
8553 ) {
8554 self.display_map.update(cx, |map, _| {
8555 map.highlight_text(TypeId::of::<T>(), ranges, style)
8556 });
8557 cx.notify();
8558 }
8559
8560 pub fn highlight_inlays<T: 'static>(
8561 &mut self,
8562 highlights: Vec<InlayHighlight>,
8563 style: HighlightStyle,
8564 cx: &mut ViewContext<Self>,
8565 ) {
8566 self.display_map.update(cx, |map, _| {
8567 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8568 });
8569 cx.notify();
8570 }
8571
8572 pub fn text_highlights<'a, T: 'static>(
8573 &'a self,
8574 cx: &'a AppContext,
8575 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8576 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8577 }
8578
8579 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8580 let cleared = self
8581 .display_map
8582 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8583 if cleared {
8584 cx.notify();
8585 }
8586 }
8587
8588 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8589 self.blink_manager.read(cx).visible() && self.focused
8590 }
8591
8592 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8593 cx.notify();
8594 }
8595
8596 fn on_buffer_event(
8597 &mut self,
8598 multibuffer: ModelHandle<MultiBuffer>,
8599 event: &multi_buffer::Event,
8600 cx: &mut ViewContext<Self>,
8601 ) {
8602 match event {
8603 multi_buffer::Event::Edited {
8604 sigleton_buffer_edited,
8605 } => {
8606 self.refresh_active_diagnostics(cx);
8607 self.refresh_code_actions(cx);
8608 if self.has_active_copilot_suggestion(cx) {
8609 self.update_visible_copilot_suggestion(cx);
8610 }
8611 cx.emit(Event::BufferEdited);
8612
8613 if *sigleton_buffer_edited {
8614 if let Some(project) = &self.project {
8615 let project = project.read(cx);
8616 let languages_affected = multibuffer
8617 .read(cx)
8618 .all_buffers()
8619 .into_iter()
8620 .filter_map(|buffer| {
8621 let buffer = buffer.read(cx);
8622 let language = buffer.language()?;
8623 if project.is_local()
8624 && project.language_servers_for_buffer(buffer, cx).count() == 0
8625 {
8626 None
8627 } else {
8628 Some(language)
8629 }
8630 })
8631 .cloned()
8632 .collect::<HashSet<_>>();
8633 if !languages_affected.is_empty() {
8634 self.refresh_inlay_hints(
8635 InlayHintRefreshReason::BufferEdited(languages_affected),
8636 cx,
8637 );
8638 }
8639 }
8640 }
8641 }
8642 multi_buffer::Event::ExcerptsAdded {
8643 buffer,
8644 predecessor,
8645 excerpts,
8646 } => {
8647 cx.emit(Event::ExcerptsAdded {
8648 buffer: buffer.clone(),
8649 predecessor: *predecessor,
8650 excerpts: excerpts.clone(),
8651 });
8652 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8653 }
8654 multi_buffer::Event::ExcerptsRemoved { ids } => {
8655 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8656 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8657 }
8658 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8659 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8660 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8661 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8662 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8663 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8664 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8665 multi_buffer::Event::DiagnosticsUpdated => {
8666 self.refresh_active_diagnostics(cx);
8667 }
8668 _ => {}
8669 };
8670 }
8671
8672 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8673 cx.notify();
8674 }
8675
8676 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8677 self.refresh_copilot_suggestions(true, cx);
8678 self.refresh_inlay_hints(
8679 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8680 self.selections.newest_anchor().head(),
8681 &self.buffer.read(cx).snapshot(cx),
8682 cx,
8683 )),
8684 cx,
8685 );
8686 }
8687
8688 pub fn set_searchable(&mut self, searchable: bool) {
8689 self.searchable = searchable;
8690 }
8691
8692 pub fn searchable(&self) -> bool {
8693 self.searchable
8694 }
8695
8696 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8697 let active_item = workspace.active_item(cx);
8698 let editor_handle = if let Some(editor) = active_item
8699 .as_ref()
8700 .and_then(|item| item.act_as::<Self>(cx))
8701 {
8702 editor
8703 } else {
8704 cx.propagate_action();
8705 return;
8706 };
8707
8708 let editor = editor_handle.read(cx);
8709 let buffer = editor.buffer.read(cx);
8710 if buffer.is_singleton() {
8711 cx.propagate_action();
8712 return;
8713 }
8714
8715 let mut new_selections_by_buffer = HashMap::default();
8716 for selection in editor.selections.all::<usize>(cx) {
8717 for (buffer, mut range, _) in
8718 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8719 {
8720 if selection.reversed {
8721 mem::swap(&mut range.start, &mut range.end);
8722 }
8723 new_selections_by_buffer
8724 .entry(buffer)
8725 .or_insert(Vec::new())
8726 .push(range)
8727 }
8728 }
8729
8730 editor_handle.update(cx, |editor, cx| {
8731 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8732 });
8733 let pane = workspace.active_pane().clone();
8734 pane.update(cx, |pane, _| pane.disable_history());
8735
8736 // We defer the pane interaction because we ourselves are a workspace item
8737 // and activating a new item causes the pane to call a method on us reentrantly,
8738 // which panics if we're on the stack.
8739 cx.defer(move |workspace, cx| {
8740 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8741 let editor = workspace.open_project_item::<Self>(buffer, cx);
8742 editor.update(cx, |editor, cx| {
8743 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8744 s.select_ranges(ranges);
8745 });
8746 });
8747 }
8748
8749 pane.update(cx, |pane, _| pane.enable_history());
8750 });
8751 }
8752
8753 fn jump(
8754 workspace: &mut Workspace,
8755 path: ProjectPath,
8756 position: Point,
8757 anchor: language::Anchor,
8758 cx: &mut ViewContext<Workspace>,
8759 ) {
8760 let editor = workspace.open_path(path, None, true, cx);
8761 cx.spawn(|_, mut cx| async move {
8762 let editor = editor
8763 .await?
8764 .downcast::<Editor>()
8765 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8766 .downgrade();
8767 editor.update(&mut cx, |editor, cx| {
8768 let buffer = editor
8769 .buffer()
8770 .read(cx)
8771 .as_singleton()
8772 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8773 let buffer = buffer.read(cx);
8774 let cursor = if buffer.can_resolve(&anchor) {
8775 language::ToPoint::to_point(&anchor, buffer)
8776 } else {
8777 buffer.clip_point(position, Bias::Left)
8778 };
8779
8780 let nav_history = editor.nav_history.take();
8781 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8782 s.select_ranges([cursor..cursor]);
8783 });
8784 editor.nav_history = nav_history;
8785
8786 anyhow::Ok(())
8787 })??;
8788
8789 anyhow::Ok(())
8790 })
8791 .detach_and_log_err(cx);
8792 }
8793
8794 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8795 let snapshot = self.buffer.read(cx).read(cx);
8796 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8797 Some(
8798 ranges
8799 .iter()
8800 .map(move |range| {
8801 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8802 })
8803 .collect(),
8804 )
8805 }
8806
8807 fn selection_replacement_ranges(
8808 &self,
8809 range: Range<OffsetUtf16>,
8810 cx: &AppContext,
8811 ) -> Vec<Range<OffsetUtf16>> {
8812 let selections = self.selections.all::<OffsetUtf16>(cx);
8813 let newest_selection = selections
8814 .iter()
8815 .max_by_key(|selection| selection.id)
8816 .unwrap();
8817 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8818 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8819 let snapshot = self.buffer.read(cx).read(cx);
8820 selections
8821 .into_iter()
8822 .map(|mut selection| {
8823 selection.start.0 =
8824 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8825 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8826 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8827 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8828 })
8829 .collect()
8830 }
8831
8832 fn report_copilot_event(
8833 &self,
8834 suggestion_id: Option<String>,
8835 suggestion_accepted: bool,
8836 cx: &AppContext,
8837 ) {
8838 let Some(project) = &self.project else { return };
8839
8840 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8841 let file_extension = self
8842 .buffer
8843 .read(cx)
8844 .as_singleton()
8845 .and_then(|b| b.read(cx).file())
8846 .and_then(|file| Path::new(file.file_name(cx)).extension())
8847 .and_then(|e| e.to_str())
8848 .map(|a| a.to_string());
8849
8850 let telemetry = project.read(cx).client().telemetry().clone();
8851 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8852
8853 let event = ClickhouseEvent::Copilot {
8854 suggestion_id,
8855 suggestion_accepted,
8856 file_extension,
8857 };
8858 telemetry.report_clickhouse_event(event, telemetry_settings);
8859 }
8860
8861 fn report_editor_event(
8862 &self,
8863 operation: &'static str,
8864 file_extension: Option<String>,
8865 cx: &AppContext,
8866 ) {
8867 let Some(project) = &self.project else { return };
8868
8869 // If None, we are in a file without an extension
8870 let file = self
8871 .buffer
8872 .read(cx)
8873 .as_singleton()
8874 .and_then(|b| b.read(cx).file());
8875 let file_extension = file_extension.or(file
8876 .as_ref()
8877 .and_then(|file| Path::new(file.file_name(cx)).extension())
8878 .and_then(|e| e.to_str())
8879 .map(|a| a.to_string()));
8880
8881 let vim_mode = cx
8882 .global::<SettingsStore>()
8883 .raw_user_settings()
8884 .get("vim_mode")
8885 == Some(&serde_json::Value::Bool(true));
8886 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8887 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8888 let copilot_enabled_for_language = self
8889 .buffer
8890 .read(cx)
8891 .settings_at(0, cx)
8892 .show_copilot_suggestions;
8893
8894 let telemetry = project.read(cx).client().telemetry().clone();
8895 let event = ClickhouseEvent::Editor {
8896 file_extension,
8897 vim_mode,
8898 operation,
8899 copilot_enabled,
8900 copilot_enabled_for_language,
8901 };
8902 telemetry.report_clickhouse_event(event, telemetry_settings)
8903 }
8904
8905 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8906 /// with each line being an array of {text, highlight} objects.
8907 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8908 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8909 return;
8910 };
8911
8912 #[derive(Serialize)]
8913 struct Chunk<'a> {
8914 text: String,
8915 highlight: Option<&'a str>,
8916 }
8917
8918 let snapshot = buffer.read(cx).snapshot();
8919 let range = self
8920 .selected_text_range(cx)
8921 .and_then(|selected_range| {
8922 if selected_range.is_empty() {
8923 None
8924 } else {
8925 Some(selected_range)
8926 }
8927 })
8928 .unwrap_or_else(|| 0..snapshot.len());
8929
8930 let chunks = snapshot.chunks(range, true);
8931 let mut lines = Vec::new();
8932 let mut line: VecDeque<Chunk> = VecDeque::new();
8933
8934 let theme = &theme::current(cx).editor.syntax;
8935
8936 for chunk in chunks {
8937 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8938 let mut chunk_lines = chunk.text.split("\n").peekable();
8939 while let Some(text) = chunk_lines.next() {
8940 let mut merged_with_last_token = false;
8941 if let Some(last_token) = line.back_mut() {
8942 if last_token.highlight == highlight {
8943 last_token.text.push_str(text);
8944 merged_with_last_token = true;
8945 }
8946 }
8947
8948 if !merged_with_last_token {
8949 line.push_back(Chunk {
8950 text: text.into(),
8951 highlight,
8952 });
8953 }
8954
8955 if chunk_lines.peek().is_some() {
8956 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8957 line.pop_front();
8958 }
8959 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8960 line.pop_back();
8961 }
8962
8963 lines.push(mem::take(&mut line));
8964 }
8965 }
8966 }
8967
8968 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8969 return;
8970 };
8971 cx.write_to_clipboard(ClipboardItem::new(lines));
8972 }
8973
8974 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8975 &self.inlay_hint_cache
8976 }
8977
8978 pub fn replay_insert_event(
8979 &mut self,
8980 text: &str,
8981 relative_utf16_range: Option<Range<isize>>,
8982 cx: &mut ViewContext<Self>,
8983 ) {
8984 if !self.input_enabled {
8985 cx.emit(Event::InputIgnored { text: text.into() });
8986 return;
8987 }
8988 if let Some(relative_utf16_range) = relative_utf16_range {
8989 let selections = self.selections.all::<OffsetUtf16>(cx);
8990 self.change_selections(None, cx, |s| {
8991 let new_ranges = selections.into_iter().map(|range| {
8992 let start = OffsetUtf16(
8993 range
8994 .head()
8995 .0
8996 .saturating_add_signed(relative_utf16_range.start),
8997 );
8998 let end = OffsetUtf16(
8999 range
9000 .head()
9001 .0
9002 .saturating_add_signed(relative_utf16_range.end),
9003 );
9004 start..end
9005 });
9006 s.select_ranges(new_ranges);
9007 });
9008 }
9009
9010 self.handle_input(text, cx);
9011 }
9012
9013 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9014 let Some(project) = self.project.as_ref() else {
9015 return false;
9016 };
9017 let project = project.read(cx);
9018
9019 let mut supports = false;
9020 self.buffer().read(cx).for_each_buffer(|buffer| {
9021 if !supports {
9022 supports = project
9023 .language_servers_for_buffer(buffer.read(cx), cx)
9024 .any(
9025 |(_, server)| match server.capabilities().inlay_hint_provider {
9026 Some(lsp::OneOf::Left(enabled)) => enabled,
9027 Some(lsp::OneOf::Right(_)) => true,
9028 None => false,
9029 },
9030 )
9031 }
9032 });
9033 supports
9034 }
9035}
9036
9037pub trait CollaborationHub {
9038 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9039 fn user_participant_indices<'a>(
9040 &self,
9041 cx: &'a AppContext,
9042 ) -> &'a HashMap<u64, ParticipantIndex>;
9043}
9044
9045impl CollaborationHub for ModelHandle<Project> {
9046 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9047 self.read(cx).collaborators()
9048 }
9049
9050 fn user_participant_indices<'a>(
9051 &self,
9052 cx: &'a AppContext,
9053 ) -> &'a HashMap<u64, ParticipantIndex> {
9054 self.read(cx).user_store().read(cx).participant_indices()
9055 }
9056}
9057
9058fn inlay_hint_settings(
9059 location: Anchor,
9060 snapshot: &MultiBufferSnapshot,
9061 cx: &mut ViewContext<'_, '_, Editor>,
9062) -> InlayHintSettings {
9063 let file = snapshot.file_at(location);
9064 let language = snapshot.language_at(location);
9065 let settings = all_language_settings(file, cx);
9066 settings
9067 .language(language.map(|l| l.name()).as_deref())
9068 .inlay_hints
9069}
9070
9071fn consume_contiguous_rows(
9072 contiguous_row_selections: &mut Vec<Selection<Point>>,
9073 selection: &Selection<Point>,
9074 display_map: &DisplaySnapshot,
9075 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9076) -> (u32, u32) {
9077 contiguous_row_selections.push(selection.clone());
9078 let start_row = selection.start.row;
9079 let mut end_row = ending_row(selection, display_map);
9080
9081 while let Some(next_selection) = selections.peek() {
9082 if next_selection.start.row <= end_row {
9083 end_row = ending_row(next_selection, display_map);
9084 contiguous_row_selections.push(selections.next().unwrap().clone());
9085 } else {
9086 break;
9087 }
9088 }
9089 (start_row, end_row)
9090}
9091
9092fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9093 if next_selection.end.column > 0 || next_selection.is_empty() {
9094 display_map.next_line_boundary(next_selection.end).0.row + 1
9095 } else {
9096 next_selection.end.row
9097 }
9098}
9099
9100impl EditorSnapshot {
9101 pub fn remote_selections_in_range<'a>(
9102 &'a self,
9103 range: &'a Range<Anchor>,
9104 collaboration_hub: &dyn CollaborationHub,
9105 cx: &'a AppContext,
9106 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9107 let participant_indices = collaboration_hub.user_participant_indices(cx);
9108 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9109 let collaborators_by_replica_id = collaborators_by_peer_id
9110 .iter()
9111 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9112 .collect::<HashMap<_, _>>();
9113 self.buffer_snapshot
9114 .remote_selections_in_range(range)
9115 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9116 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9117 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9118 Some(RemoteSelection {
9119 replica_id,
9120 selection,
9121 cursor_shape,
9122 line_mode,
9123 participant_index,
9124 peer_id: collaborator.peer_id,
9125 })
9126 })
9127 }
9128
9129 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9130 self.display_snapshot.buffer_snapshot.language_at(position)
9131 }
9132
9133 pub fn is_focused(&self) -> bool {
9134 self.is_focused
9135 }
9136
9137 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9138 self.placeholder_text.as_ref()
9139 }
9140
9141 pub fn scroll_position(&self) -> Vector2F {
9142 self.scroll_anchor.scroll_position(&self.display_snapshot)
9143 }
9144}
9145
9146impl Deref for EditorSnapshot {
9147 type Target = DisplaySnapshot;
9148
9149 fn deref(&self) -> &Self::Target {
9150 &self.display_snapshot
9151 }
9152}
9153
9154#[derive(Clone, Debug, PartialEq, Eq)]
9155pub enum Event {
9156 InputIgnored {
9157 text: Arc<str>,
9158 },
9159 InputHandled {
9160 utf16_range_to_replace: Option<Range<isize>>,
9161 text: Arc<str>,
9162 },
9163 ExcerptsAdded {
9164 buffer: ModelHandle<Buffer>,
9165 predecessor: ExcerptId,
9166 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9167 },
9168 ExcerptsRemoved {
9169 ids: Vec<ExcerptId>,
9170 },
9171 BufferEdited,
9172 Edited,
9173 Reparsed,
9174 Focused,
9175 Blurred,
9176 DirtyChanged,
9177 Saved,
9178 TitleChanged,
9179 DiffBaseChanged,
9180 SelectionsChanged {
9181 local: bool,
9182 },
9183 ScrollPositionChanged {
9184 local: bool,
9185 autoscroll: bool,
9186 },
9187 Closed,
9188}
9189
9190pub struct EditorFocused(pub ViewHandle<Editor>);
9191pub struct EditorBlurred(pub ViewHandle<Editor>);
9192pub struct EditorReleased(pub WeakViewHandle<Editor>);
9193
9194impl Entity for Editor {
9195 type Event = Event;
9196
9197 fn release(&mut self, cx: &mut AppContext) {
9198 cx.emit_global(EditorReleased(self.handle.clone()));
9199 }
9200}
9201
9202impl View for Editor {
9203 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9204 let style = self.style(cx);
9205 let font_changed = self.display_map.update(cx, |map, cx| {
9206 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9207 map.set_font(style.text.font_id, style.text.font_size, cx)
9208 });
9209
9210 if font_changed {
9211 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9212 hide_hover(editor, cx);
9213 hide_link_definition(editor, cx);
9214 });
9215 }
9216
9217 Stack::new()
9218 .with_child(EditorElement::new(style.clone()))
9219 .with_child(ChildView::new(&self.mouse_context_menu, cx))
9220 .into_any()
9221 }
9222
9223 fn ui_name() -> &'static str {
9224 "Editor"
9225 }
9226
9227 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
9228 if cx.is_self_focused() {
9229 let focused_event = EditorFocused(cx.handle());
9230 cx.emit(Event::Focused);
9231 cx.emit_global(focused_event);
9232 }
9233 if let Some(rename) = self.pending_rename.as_ref() {
9234 cx.focus(&rename.editor);
9235 } else if cx.is_self_focused() || !focused.is::<Editor>() {
9236 if !self.focused {
9237 self.blink_manager.update(cx, BlinkManager::enable);
9238 }
9239 self.focused = true;
9240 self.buffer.update(cx, |buffer, cx| {
9241 buffer.finalize_last_transaction(cx);
9242 if self.leader_peer_id.is_none() {
9243 buffer.set_active_selections(
9244 &self.selections.disjoint_anchors(),
9245 self.selections.line_mode,
9246 self.cursor_shape,
9247 cx,
9248 );
9249 }
9250 });
9251 }
9252 }
9253
9254 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
9255 let blurred_event = EditorBlurred(cx.handle());
9256 cx.emit_global(blurred_event);
9257 self.focused = false;
9258 self.blink_manager.update(cx, BlinkManager::disable);
9259 self.buffer
9260 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9261 self.hide_context_menu(cx);
9262 hide_hover(self, cx);
9263 cx.emit(Event::Blurred);
9264 cx.notify();
9265 }
9266
9267 fn modifiers_changed(
9268 &mut self,
9269 event: &gpui::platform::ModifiersChangedEvent,
9270 cx: &mut ViewContext<Self>,
9271 ) -> bool {
9272 let pending_selection = self.has_pending_selection();
9273
9274 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9275 if event.cmd && !pending_selection {
9276 let point = point.clone();
9277 let snapshot = self.snapshot(cx);
9278 let kind = point.definition_kind(event.shift);
9279
9280 show_link_definition(kind, self, point, snapshot, cx);
9281 return false;
9282 }
9283 }
9284
9285 {
9286 if self.link_go_to_definition_state.symbol_range.is_some()
9287 || !self.link_go_to_definition_state.definitions.is_empty()
9288 {
9289 self.link_go_to_definition_state.symbol_range.take();
9290 self.link_go_to_definition_state.definitions.clear();
9291 cx.notify();
9292 }
9293
9294 self.link_go_to_definition_state.task = None;
9295
9296 self.clear_highlights::<LinkGoToDefinitionState>(cx);
9297 }
9298
9299 false
9300 }
9301
9302 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
9303 Self::reset_to_default_keymap_context(keymap);
9304 let mode = match self.mode {
9305 EditorMode::SingleLine => "single_line",
9306 EditorMode::AutoHeight { .. } => "auto_height",
9307 EditorMode::Full => "full",
9308 };
9309 keymap.add_key("mode", mode);
9310 if self.pending_rename.is_some() {
9311 keymap.add_identifier("renaming");
9312 }
9313 if self.context_menu_visible() {
9314 match self.context_menu.read().as_ref() {
9315 Some(ContextMenu::Completions(_)) => {
9316 keymap.add_identifier("menu");
9317 keymap.add_identifier("showing_completions")
9318 }
9319 Some(ContextMenu::CodeActions(_)) => {
9320 keymap.add_identifier("menu");
9321 keymap.add_identifier("showing_code_actions")
9322 }
9323 None => {}
9324 }
9325 }
9326
9327 for layer in self.keymap_context_layers.values() {
9328 keymap.extend(layer);
9329 }
9330
9331 if let Some(extension) = self
9332 .buffer
9333 .read(cx)
9334 .as_singleton()
9335 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
9336 {
9337 keymap.add_key("extension", extension.to_string());
9338 }
9339 }
9340
9341 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
9342 Some(
9343 self.buffer
9344 .read(cx)
9345 .read(cx)
9346 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9347 .collect(),
9348 )
9349 }
9350
9351 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9352 // Prevent the IME menu from appearing when holding down an alphabetic key
9353 // while input is disabled.
9354 if !self.input_enabled {
9355 return None;
9356 }
9357
9358 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9359 Some(range.start.0..range.end.0)
9360 }
9361
9362 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9363 let snapshot = self.buffer.read(cx).read(cx);
9364 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9365 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9366 }
9367
9368 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9369 self.clear_highlights::<InputComposition>(cx);
9370 self.ime_transaction.take();
9371 }
9372
9373 fn replace_text_in_range(
9374 &mut self,
9375 range_utf16: Option<Range<usize>>,
9376 text: &str,
9377 cx: &mut ViewContext<Self>,
9378 ) {
9379 if !self.input_enabled {
9380 cx.emit(Event::InputIgnored { text: text.into() });
9381 return;
9382 }
9383
9384 self.transact(cx, |this, cx| {
9385 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9386 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9387 Some(this.selection_replacement_ranges(range_utf16, cx))
9388 } else {
9389 this.marked_text_ranges(cx)
9390 };
9391
9392 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9393 let newest_selection_id = this.selections.newest_anchor().id;
9394 this.selections
9395 .all::<OffsetUtf16>(cx)
9396 .iter()
9397 .zip(ranges_to_replace.iter())
9398 .find_map(|(selection, range)| {
9399 if selection.id == newest_selection_id {
9400 Some(
9401 (range.start.0 as isize - selection.head().0 as isize)
9402 ..(range.end.0 as isize - selection.head().0 as isize),
9403 )
9404 } else {
9405 None
9406 }
9407 })
9408 });
9409
9410 cx.emit(Event::InputHandled {
9411 utf16_range_to_replace: range_to_replace,
9412 text: text.into(),
9413 });
9414
9415 if let Some(new_selected_ranges) = new_selected_ranges {
9416 this.change_selections(None, cx, |selections| {
9417 selections.select_ranges(new_selected_ranges)
9418 });
9419 }
9420
9421 this.handle_input(text, cx);
9422 });
9423
9424 if let Some(transaction) = self.ime_transaction {
9425 self.buffer.update(cx, |buffer, cx| {
9426 buffer.group_until_transaction(transaction, cx);
9427 });
9428 }
9429
9430 self.unmark_text(cx);
9431 }
9432
9433 fn replace_and_mark_text_in_range(
9434 &mut self,
9435 range_utf16: Option<Range<usize>>,
9436 text: &str,
9437 new_selected_range_utf16: Option<Range<usize>>,
9438 cx: &mut ViewContext<Self>,
9439 ) {
9440 if !self.input_enabled {
9441 cx.emit(Event::InputIgnored { text: text.into() });
9442 return;
9443 }
9444
9445 let transaction = self.transact(cx, |this, cx| {
9446 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9447 let snapshot = this.buffer.read(cx).read(cx);
9448 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9449 for marked_range in &mut marked_ranges {
9450 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9451 marked_range.start.0 += relative_range_utf16.start;
9452 marked_range.start =
9453 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9454 marked_range.end =
9455 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9456 }
9457 }
9458 Some(marked_ranges)
9459 } else if let Some(range_utf16) = range_utf16 {
9460 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9461 Some(this.selection_replacement_ranges(range_utf16, cx))
9462 } else {
9463 None
9464 };
9465
9466 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9467 let newest_selection_id = this.selections.newest_anchor().id;
9468 this.selections
9469 .all::<OffsetUtf16>(cx)
9470 .iter()
9471 .zip(ranges_to_replace.iter())
9472 .find_map(|(selection, range)| {
9473 if selection.id == newest_selection_id {
9474 Some(
9475 (range.start.0 as isize - selection.head().0 as isize)
9476 ..(range.end.0 as isize - selection.head().0 as isize),
9477 )
9478 } else {
9479 None
9480 }
9481 })
9482 });
9483
9484 cx.emit(Event::InputHandled {
9485 utf16_range_to_replace: range_to_replace,
9486 text: text.into(),
9487 });
9488
9489 if let Some(ranges) = ranges_to_replace {
9490 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9491 }
9492
9493 let marked_ranges = {
9494 let snapshot = this.buffer.read(cx).read(cx);
9495 this.selections
9496 .disjoint_anchors()
9497 .iter()
9498 .map(|selection| {
9499 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9500 })
9501 .collect::<Vec<_>>()
9502 };
9503
9504 if text.is_empty() {
9505 this.unmark_text(cx);
9506 } else {
9507 this.highlight_text::<InputComposition>(
9508 marked_ranges.clone(),
9509 this.style(cx).composition_mark,
9510 cx,
9511 );
9512 }
9513
9514 this.handle_input(text, cx);
9515
9516 if let Some(new_selected_range) = new_selected_range_utf16 {
9517 let snapshot = this.buffer.read(cx).read(cx);
9518 let new_selected_ranges = marked_ranges
9519 .into_iter()
9520 .map(|marked_range| {
9521 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9522 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9523 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9524 snapshot.clip_offset_utf16(new_start, Bias::Left)
9525 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9526 })
9527 .collect::<Vec<_>>();
9528
9529 drop(snapshot);
9530 this.change_selections(None, cx, |selections| {
9531 selections.select_ranges(new_selected_ranges)
9532 });
9533 }
9534 });
9535
9536 self.ime_transaction = self.ime_transaction.or(transaction);
9537 if let Some(transaction) = self.ime_transaction {
9538 self.buffer.update(cx, |buffer, cx| {
9539 buffer.group_until_transaction(transaction, cx);
9540 });
9541 }
9542
9543 if self.text_highlights::<InputComposition>(cx).is_none() {
9544 self.ime_transaction.take();
9545 }
9546 }
9547}
9548
9549fn build_style(
9550 settings: &ThemeSettings,
9551 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9552 override_text_style: Option<&OverrideTextStyle>,
9553 cx: &AppContext,
9554) -> EditorStyle {
9555 let font_cache = cx.font_cache();
9556 let line_height_scalar = settings.line_height();
9557 let theme_id = settings.theme.meta.id;
9558 let mut theme = settings.theme.editor.clone();
9559 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9560 let field_editor_theme = get_field_editor_theme(&settings.theme);
9561 theme.text_color = field_editor_theme.text.color;
9562 theme.selection = field_editor_theme.selection;
9563 theme.background = field_editor_theme
9564 .container
9565 .background_color
9566 .unwrap_or_default();
9567 EditorStyle {
9568 text: field_editor_theme.text,
9569 placeholder_text: field_editor_theme.placeholder_text,
9570 line_height_scalar,
9571 theme,
9572 theme_id,
9573 }
9574 } else {
9575 let font_family_id = settings.buffer_font_family;
9576 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9577 let font_properties = Default::default();
9578 let font_id = font_cache
9579 .select_font(font_family_id, &font_properties)
9580 .unwrap();
9581 let font_size = settings.buffer_font_size(cx);
9582 EditorStyle {
9583 text: TextStyle {
9584 color: settings.theme.editor.text_color,
9585 font_family_name,
9586 font_family_id,
9587 font_id,
9588 font_size,
9589 font_properties,
9590 underline: Default::default(),
9591 soft_wrap: false,
9592 },
9593 placeholder_text: None,
9594 line_height_scalar,
9595 theme,
9596 theme_id,
9597 }
9598 };
9599
9600 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9601 if let Some(highlighted) = style
9602 .text
9603 .clone()
9604 .highlight(highlight_style, font_cache)
9605 .log_err()
9606 {
9607 style.text = highlighted;
9608 }
9609 }
9610
9611 style
9612}
9613
9614trait SelectionExt {
9615 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9616 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9617 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9618 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9619 -> Range<u32>;
9620}
9621
9622impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9623 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9624 let start = self.start.to_point(buffer);
9625 let end = self.end.to_point(buffer);
9626 if self.reversed {
9627 end..start
9628 } else {
9629 start..end
9630 }
9631 }
9632
9633 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9634 let start = self.start.to_offset(buffer);
9635 let end = self.end.to_offset(buffer);
9636 if self.reversed {
9637 end..start
9638 } else {
9639 start..end
9640 }
9641 }
9642
9643 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9644 let start = self
9645 .start
9646 .to_point(&map.buffer_snapshot)
9647 .to_display_point(map);
9648 let end = self
9649 .end
9650 .to_point(&map.buffer_snapshot)
9651 .to_display_point(map);
9652 if self.reversed {
9653 end..start
9654 } else {
9655 start..end
9656 }
9657 }
9658
9659 fn spanned_rows(
9660 &self,
9661 include_end_if_at_line_start: bool,
9662 map: &DisplaySnapshot,
9663 ) -> Range<u32> {
9664 let start = self.start.to_point(&map.buffer_snapshot);
9665 let mut end = self.end.to_point(&map.buffer_snapshot);
9666 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9667 end.row -= 1;
9668 }
9669
9670 let buffer_start = map.prev_line_boundary(start).0;
9671 let buffer_end = map.next_line_boundary(end).0;
9672 buffer_start.row..buffer_end.row + 1
9673 }
9674}
9675
9676impl<T: InvalidationRegion> InvalidationStack<T> {
9677 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9678 where
9679 S: Clone + ToOffset,
9680 {
9681 while let Some(region) = self.last() {
9682 let all_selections_inside_invalidation_ranges =
9683 if selections.len() == region.ranges().len() {
9684 selections
9685 .iter()
9686 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9687 .all(|(selection, invalidation_range)| {
9688 let head = selection.head().to_offset(buffer);
9689 invalidation_range.start <= head && invalidation_range.end >= head
9690 })
9691 } else {
9692 false
9693 };
9694
9695 if all_selections_inside_invalidation_ranges {
9696 break;
9697 } else {
9698 self.pop();
9699 }
9700 }
9701 }
9702}
9703
9704impl<T> Default for InvalidationStack<T> {
9705 fn default() -> Self {
9706 Self(Default::default())
9707 }
9708}
9709
9710impl<T> Deref for InvalidationStack<T> {
9711 type Target = Vec<T>;
9712
9713 fn deref(&self) -> &Self::Target {
9714 &self.0
9715 }
9716}
9717
9718impl<T> DerefMut for InvalidationStack<T> {
9719 fn deref_mut(&mut self) -> &mut Self::Target {
9720 &mut self.0
9721 }
9722}
9723
9724impl InvalidationRegion for SnippetState {
9725 fn ranges(&self) -> &[Range<Anchor>] {
9726 &self.ranges[self.active_index]
9727 }
9728}
9729
9730impl Deref for EditorStyle {
9731 type Target = theme::Editor;
9732
9733 fn deref(&self) -> &Self::Target {
9734 &self.theme
9735 }
9736}
9737
9738pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9739 let mut highlighted_lines = Vec::new();
9740
9741 for (index, line) in diagnostic.message.lines().enumerate() {
9742 let line = match &diagnostic.source {
9743 Some(source) if index == 0 => {
9744 let source_highlight = Vec::from_iter(0..source.len());
9745 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9746 }
9747
9748 _ => highlight_diagnostic_message(Vec::new(), line),
9749 };
9750 highlighted_lines.push(line);
9751 }
9752 let message = diagnostic.message;
9753 Arc::new(move |cx: &mut BlockContext| {
9754 let message = message.clone();
9755 let settings = settings::get::<ThemeSettings>(cx);
9756 let tooltip_style = settings.theme.tooltip.clone();
9757 let theme = &settings.theme.editor;
9758 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9759 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9760 let anchor_x = cx.anchor_x;
9761 enum BlockContextToolip {}
9762 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9763 Flex::column()
9764 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9765 Label::new(
9766 line.clone(),
9767 style.message.clone().with_font_size(font_size),
9768 )
9769 .with_highlights(highlights.clone())
9770 .contained()
9771 .with_margin_left(anchor_x)
9772 }))
9773 .aligned()
9774 .left()
9775 .into_any()
9776 })
9777 .with_cursor_style(CursorStyle::PointingHand)
9778 .on_click(MouseButton::Left, move |_, _, cx| {
9779 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9780 })
9781 // We really need to rethink this ID system...
9782 .with_tooltip::<BlockContextToolip>(
9783 cx.block_id,
9784 "Copy diagnostic message",
9785 None,
9786 tooltip_style,
9787 cx,
9788 )
9789 .into_any()
9790 })
9791}
9792
9793pub fn highlight_diagnostic_message(
9794 initial_highlights: Vec<usize>,
9795 message: &str,
9796) -> (String, Vec<usize>) {
9797 let mut message_without_backticks = String::new();
9798 let mut prev_offset = 0;
9799 let mut inside_block = false;
9800 let mut highlights = initial_highlights;
9801 for (match_ix, (offset, _)) in message
9802 .match_indices('`')
9803 .chain([(message.len(), "")])
9804 .enumerate()
9805 {
9806 message_without_backticks.push_str(&message[prev_offset..offset]);
9807 if inside_block {
9808 highlights.extend(prev_offset - match_ix..offset - match_ix);
9809 }
9810
9811 inside_block = !inside_block;
9812 prev_offset = offset + 1;
9813 }
9814
9815 (message_without_backticks, highlights)
9816}
9817
9818pub fn diagnostic_style(
9819 severity: DiagnosticSeverity,
9820 valid: bool,
9821 theme: &theme::Editor,
9822) -> DiagnosticStyle {
9823 match (severity, valid) {
9824 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9825 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9826 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9827 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9828 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9829 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9830 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9831 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9832 _ => theme.invalid_hint_diagnostic.clone(),
9833 }
9834}
9835
9836pub fn combine_syntax_and_fuzzy_match_highlights(
9837 text: &str,
9838 default_style: HighlightStyle,
9839 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9840 match_indices: &[usize],
9841) -> Vec<(Range<usize>, HighlightStyle)> {
9842 let mut result = Vec::new();
9843 let mut match_indices = match_indices.iter().copied().peekable();
9844
9845 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9846 {
9847 syntax_highlight.weight = None;
9848
9849 // Add highlights for any fuzzy match characters before the next
9850 // syntax highlight range.
9851 while let Some(&match_index) = match_indices.peek() {
9852 if match_index >= range.start {
9853 break;
9854 }
9855 match_indices.next();
9856 let end_index = char_ix_after(match_index, text);
9857 let mut match_style = default_style;
9858 match_style.weight = Some(fonts::Weight::BOLD);
9859 result.push((match_index..end_index, match_style));
9860 }
9861
9862 if range.start == usize::MAX {
9863 break;
9864 }
9865
9866 // Add highlights for any fuzzy match characters within the
9867 // syntax highlight range.
9868 let mut offset = range.start;
9869 while let Some(&match_index) = match_indices.peek() {
9870 if match_index >= range.end {
9871 break;
9872 }
9873
9874 match_indices.next();
9875 if match_index > offset {
9876 result.push((offset..match_index, syntax_highlight));
9877 }
9878
9879 let mut end_index = char_ix_after(match_index, text);
9880 while let Some(&next_match_index) = match_indices.peek() {
9881 if next_match_index == end_index && next_match_index < range.end {
9882 end_index = char_ix_after(next_match_index, text);
9883 match_indices.next();
9884 } else {
9885 break;
9886 }
9887 }
9888
9889 let mut match_style = syntax_highlight;
9890 match_style.weight = Some(fonts::Weight::BOLD);
9891 result.push((match_index..end_index, match_style));
9892 offset = end_index;
9893 }
9894
9895 if offset < range.end {
9896 result.push((offset..range.end, syntax_highlight));
9897 }
9898 }
9899
9900 fn char_ix_after(ix: usize, text: &str) -> usize {
9901 ix + text[ix..].chars().next().unwrap().len_utf8()
9902 }
9903
9904 result
9905}
9906
9907pub fn styled_runs_for_code_label<'a>(
9908 label: &'a CodeLabel,
9909 syntax_theme: &'a theme::SyntaxTheme,
9910) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9911 let fade_out = HighlightStyle {
9912 fade_out: Some(0.35),
9913 ..Default::default()
9914 };
9915
9916 let mut prev_end = label.filter_range.end;
9917 label
9918 .runs
9919 .iter()
9920 .enumerate()
9921 .flat_map(move |(ix, (range, highlight_id))| {
9922 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9923 style
9924 } else {
9925 return Default::default();
9926 };
9927 let mut muted_style = style;
9928 muted_style.highlight(fade_out);
9929
9930 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9931 if range.start >= label.filter_range.end {
9932 if range.start > prev_end {
9933 runs.push((prev_end..range.start, fade_out));
9934 }
9935 runs.push((range.clone(), muted_style));
9936 } else if range.end <= label.filter_range.end {
9937 runs.push((range.clone(), style));
9938 } else {
9939 runs.push((range.start..label.filter_range.end, style));
9940 runs.push((label.filter_range.end..range.end, muted_style));
9941 }
9942 prev_end = cmp::max(prev_end, range.end);
9943
9944 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9945 runs.push((prev_end..label.text.len(), fade_out));
9946 }
9947
9948 runs
9949 })
9950}
9951
9952pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9953 let mut index = 0;
9954 let mut codepoints = text.char_indices().peekable();
9955
9956 std::iter::from_fn(move || {
9957 let start_index = index;
9958 while let Some((new_index, codepoint)) = codepoints.next() {
9959 index = new_index + codepoint.len_utf8();
9960 let current_upper = codepoint.is_uppercase();
9961 let next_upper = codepoints
9962 .peek()
9963 .map(|(_, c)| c.is_uppercase())
9964 .unwrap_or(false);
9965
9966 if !current_upper && next_upper {
9967 return Some(&text[start_index..index]);
9968 }
9969 }
9970
9971 index = text.len();
9972 if start_index < text.len() {
9973 return Some(&text[start_index..]);
9974 }
9975 None
9976 })
9977 .flat_map(|word| word.split_inclusive('_'))
9978 .flat_map(|word| word.split_inclusive('-'))
9979}
9980
9981trait RangeToAnchorExt {
9982 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9983}
9984
9985impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9986 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9987 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9988 }
9989}