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