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