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