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