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