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