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