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