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