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