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