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