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