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