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, Component, Context, DispatchContext, EventEmitter, FocusHandle,
44 FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render,
45 Subscription, 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 ui2::IconButton;
99use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
100use workspace::{
101 item::ItemEvent, searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace,
102};
103
104const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
105const MAX_LINE_LEN: usize = 1024;
106const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
107const MAX_SELECTION_HISTORY_LEN: usize = 1024;
108const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
109pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
110pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
111
112pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
113
114// pub fn render_parsed_markdown<Tag: 'static>(
115// parsed: &language::ParsedMarkdown,
116// editor_style: &EditorStyle,
117// workspace: Option<WeakView<Workspace>>,
118// cx: &mut ViewContext<Editor>,
119// ) -> Text {
120// enum RenderedMarkdown {}
121
122// let parsed = parsed.clone();
123// let view_id = cx.view_id();
124// let code_span_background_color = editor_style.document_highlight_read_background;
125
126// let mut region_id = 0;
127
128// todo!()
129// // Text::new(parsed.text, editor_style.text.clone())
130// // .with_highlights(
131// // parsed
132// // .highlights
133// // .iter()
134// // .filter_map(|(range, highlight)| {
135// // let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
136// // Some((range.clone(), highlight))
137// // })
138// // .collect::<Vec<_>>(),
139// // )
140// // .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| {
141// // region_id += 1;
142// // let region = parsed.regions[ix].clone();
143
144// // if let Some(link) = region.link {
145// // cx.scene().push_cursor_region(CursorRegion {
146// // bounds,
147// // style: CursorStyle::PointingHand,
148// // });
149// // cx.scene().push_mouse_region(
150// // MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
151// // .on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
152// // markdown::Link::Web { url } => cx.platform().open_url(url),
153// // markdown::Link::Path { path } => {
154// // if let Some(workspace) = &workspace {
155// // _ = workspace.update(cx, |workspace, cx| {
156// // workspace.open_abs_path(path.clone(), false, cx).detach();
157// // });
158// // }
159// // }
160// // }),
161// // );
162// // }
163
164// // if region.code {
165// // cx.draw_quad(Quad {
166// // bounds,
167// // background: Some(code_span_background_color),
168// // corner_radii: (2.0).into(),
169// // order: todo!(),
170// // content_mask: todo!(),
171// // border_color: todo!(),
172// // border_widths: todo!(),
173// // });
174// // }
175// // })
176// // .with_soft_wrap(true)
177// }
178
179#[action]
180pub struct SelectNext {
181 #[serde(default)]
182 pub replace_newest: bool,
183}
184
185#[action]
186pub struct SelectPrevious {
187 #[serde(default)]
188 pub replace_newest: bool,
189}
190
191#[action]
192pub struct SelectAllMatches {
193 #[serde(default)]
194 pub replace_newest: bool,
195}
196
197#[action]
198pub struct SelectToBeginningOfLine {
199 #[serde(default)]
200 stop_at_soft_wraps: bool,
201}
202
203#[action]
204pub struct MovePageUp {
205 #[serde(default)]
206 center_cursor: bool,
207}
208
209#[action]
210pub struct MovePageDown {
211 #[serde(default)]
212 center_cursor: bool,
213}
214
215#[action]
216pub struct SelectToEndOfLine {
217 #[serde(default)]
218 stop_at_soft_wraps: bool,
219}
220
221#[action]
222pub struct ToggleCodeActions {
223 #[serde(default)]
224 pub deployed_from_indicator: bool,
225}
226
227#[action]
228pub struct ConfirmCompletion {
229 #[serde(default)]
230 pub item_ix: Option<usize>,
231}
232
233#[action]
234pub struct ConfirmCodeAction {
235 #[serde(default)]
236 pub item_ix: Option<usize>,
237}
238
239#[action]
240pub struct ToggleComments {
241 #[serde(default)]
242 pub advance_downwards: bool,
243}
244
245#[action]
246pub struct FoldAt {
247 pub buffer_row: u32,
248}
249
250#[action]
251pub struct UnfoldAt {
252 pub buffer_row: u32,
253}
254
255#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
256pub enum InlayId {
257 Suggestion(usize),
258 Hint(usize),
259}
260
261impl InlayId {
262 fn id(&self) -> usize {
263 match self {
264 Self::Suggestion(id) => *id,
265 Self::Hint(id) => *id,
266 }
267 }
268}
269
270actions!(
271 AddSelectionAbove,
272 AddSelectionBelow,
273 Backspace,
274 Cancel,
275 ConfirmRename,
276 ContextMenuFirst,
277 ContextMenuLast,
278 ContextMenuNext,
279 ContextMenuPrev,
280 ConvertToKebabCase,
281 ConvertToLowerCamelCase,
282 ConvertToLowerCase,
283 ConvertToSnakeCase,
284 ConvertToTitleCase,
285 ConvertToUpperCamelCase,
286 ConvertToUpperCase,
287 Copy,
288 CopyHighlightJson,
289 CopyPath,
290 CopyRelativePath,
291 Cut,
292 CutToEndOfLine,
293 Delete,
294 DeleteLine,
295 DeleteToBeginningOfLine,
296 DeleteToEndOfLine,
297 DeleteToNextSubwordEnd,
298 DeleteToNextWordEnd,
299 DeleteToPreviousSubwordStart,
300 DeleteToPreviousWordStart,
301 DuplicateLine,
302 FindAllReferences,
303 Fold,
304 FoldSelectedRanges,
305 Format,
306 GoToDefinition,
307 GoToDefinitionSplit,
308 GoToDiagnostic,
309 GoToHunk,
310 GoToPrevDiagnostic,
311 GoToPrevHunk,
312 GoToTypeDefinition,
313 GoToTypeDefinitionSplit,
314 HalfPageDown,
315 HalfPageUp,
316 Hover,
317 Indent,
318 JoinLines,
319 LineDown,
320 LineUp,
321 MoveDown,
322 MoveLeft,
323 MoveLineDown,
324 MoveLineUp,
325 MoveRight,
326 MoveToBeginning,
327 MoveToBeginningOfLine,
328 MoveToEnclosingBracket,
329 MoveToEnd,
330 MoveToEndOfLine,
331 MoveToEndOfParagraph,
332 MoveToNextSubwordEnd,
333 MoveToNextWordEnd,
334 MoveToPreviousSubwordStart,
335 MoveToPreviousWordStart,
336 MoveToStartOfParagraph,
337 MoveUp,
338 Newline,
339 NewlineAbove,
340 NewlineBelow,
341 NextScreen,
342 OpenExcerpts,
343 Outdent,
344 PageDown,
345 PageUp,
346 Paste,
347 Redo,
348 RedoSelection,
349 Rename,
350 RestartLanguageServer,
351 RevealInFinder,
352 ReverseLines,
353 ScrollCursorBottom,
354 ScrollCursorCenter,
355 ScrollCursorTop,
356 SelectAll,
357 SelectDown,
358 SelectLargerSyntaxNode,
359 SelectLeft,
360 SelectLine,
361 SelectRight,
362 SelectSmallerSyntaxNode,
363 SelectToBeginning,
364 SelectToEnd,
365 SelectToEndOfParagraph,
366 SelectToNextSubwordEnd,
367 SelectToNextWordEnd,
368 SelectToPreviousSubwordStart,
369 SelectToPreviousWordStart,
370 SelectToStartOfParagraph,
371 SelectUp,
372 ShowCharacterPalette,
373 ShowCompletions,
374 ShuffleLines,
375 SortLinesCaseInsensitive,
376 SortLinesCaseSensitive,
377 SplitSelectionIntoLines,
378 Tab,
379 TabPrev,
380 ToggleInlayHints,
381 ToggleSoftWrap,
382 Transpose,
383 Undo,
384 UndoSelection,
385 UnfoldLines,
386);
387
388// impl_actions!(
389// editor,
390// [
391// SelectNext,
392// SelectPrevious,
393// SelectAllMatches,
394// SelectToBeginningOfLine,
395// SelectToEndOfLine,
396// ToggleCodeActions,
397// MovePageUp,
398// MovePageDown,
399// ConfirmCompletion,
400// ConfirmCodeAction,
401// ToggleComments,
402// FoldAt,
403// UnfoldAt,
404// GutterHover
405// ]
406// );
407
408enum DocumentHighlightRead {}
409enum DocumentHighlightWrite {}
410enum InputComposition {}
411
412#[derive(Copy, Clone, PartialEq, Eq)]
413pub enum Direction {
414 Prev,
415 Next,
416}
417
418pub fn init_settings(cx: &mut AppContext) {
419 EditorSettings::register(cx);
420}
421
422pub fn init(cx: &mut AppContext) {
423 init_settings(cx);
424 // cx.register_action_type(Editor::new_file);
425 // cx.register_action_type(Editor::new_file_in_direction);
426 // cx.register_action_type(Editor::cancel);
427 // cx.register_action_type(Editor::newline);
428 // cx.register_action_type(Editor::newline_above);
429 // cx.register_action_type(Editor::newline_below);
430 // cx.register_action_type(Editor::backspace);
431 // cx.register_action_type(Editor::delete);
432 // cx.register_action_type(Editor::tab);
433 // cx.register_action_type(Editor::tab_prev);
434 // cx.register_action_type(Editor::indent);
435 // cx.register_action_type(Editor::outdent);
436 // cx.register_action_type(Editor::delete_line);
437 // cx.register_action_type(Editor::join_lines);
438 // cx.register_action_type(Editor::sort_lines_case_sensitive);
439 // cx.register_action_type(Editor::sort_lines_case_insensitive);
440 // cx.register_action_type(Editor::reverse_lines);
441 // cx.register_action_type(Editor::shuffle_lines);
442 // cx.register_action_type(Editor::convert_to_upper_case);
443 // cx.register_action_type(Editor::convert_to_lower_case);
444 // cx.register_action_type(Editor::convert_to_title_case);
445 // cx.register_action_type(Editor::convert_to_snake_case);
446 // cx.register_action_type(Editor::convert_to_kebab_case);
447 // cx.register_action_type(Editor::convert_to_upper_camel_case);
448 // cx.register_action_type(Editor::convert_to_lower_camel_case);
449 // cx.register_action_type(Editor::delete_to_previous_word_start);
450 // cx.register_action_type(Editor::delete_to_previous_subword_start);
451 // cx.register_action_type(Editor::delete_to_next_word_end);
452 // cx.register_action_type(Editor::delete_to_next_subword_end);
453 // cx.register_action_type(Editor::delete_to_beginning_of_line);
454 // cx.register_action_type(Editor::delete_to_end_of_line);
455 // cx.register_action_type(Editor::cut_to_end_of_line);
456 // cx.register_action_type(Editor::duplicate_line);
457 // cx.register_action_type(Editor::move_line_up);
458 // cx.register_action_type(Editor::move_line_down);
459 // cx.register_action_type(Editor::transpose);
460 // cx.register_action_type(Editor::cut);
461 // cx.register_action_type(Editor::copy);
462 // cx.register_action_type(Editor::paste);
463 // cx.register_action_type(Editor::undo);
464 // cx.register_action_type(Editor::redo);
465 // cx.register_action_type(Editor::move_page_up);
466 // cx.register_action_type::<MoveDown>();
467 // cx.register_action_type(Editor::move_page_down);
468 // cx.register_action_type(Editor::next_screen);
469 // cx.register_action_type::<MoveLeft>();
470 // cx.register_action_type::<MoveRight>();
471 // cx.register_action_type(Editor::move_to_previous_word_start);
472 // cx.register_action_type(Editor::move_to_previous_subword_start);
473 // cx.register_action_type(Editor::move_to_next_word_end);
474 // cx.register_action_type(Editor::move_to_next_subword_end);
475 // cx.register_action_type(Editor::move_to_beginning_of_line);
476 // cx.register_action_type(Editor::move_to_end_of_line);
477 // cx.register_action_type(Editor::move_to_start_of_paragraph);
478 // cx.register_action_type(Editor::move_to_end_of_paragraph);
479 // cx.register_action_type(Editor::move_to_beginning);
480 // cx.register_action_type(Editor::move_to_end);
481 // cx.register_action_type(Editor::select_up);
482 // cx.register_action_type(Editor::select_down);
483 // cx.register_action_type(Editor::select_left);
484 // cx.register_action_type(Editor::select_right);
485 // cx.register_action_type(Editor::select_to_previous_word_start);
486 // cx.register_action_type(Editor::select_to_previous_subword_start);
487 // cx.register_action_type(Editor::select_to_next_word_end);
488 // cx.register_action_type(Editor::select_to_next_subword_end);
489 // cx.register_action_type(Editor::select_to_beginning_of_line);
490 // cx.register_action_type(Editor::select_to_end_of_line);
491 // cx.register_action_type(Editor::select_to_start_of_paragraph);
492 // cx.register_action_type(Editor::select_to_end_of_paragraph);
493 // cx.register_action_type(Editor::select_to_beginning);
494 // cx.register_action_type(Editor::select_to_end);
495 // cx.register_action_type(Editor::select_all);
496 // cx.register_action_type(Editor::select_all_matches);
497 // cx.register_action_type(Editor::select_line);
498 // cx.register_action_type(Editor::split_selection_into_lines);
499 // cx.register_action_type(Editor::add_selection_above);
500 // cx.register_action_type(Editor::add_selection_below);
501 // cx.register_action_type(Editor::select_next);
502 // cx.register_action_type(Editor::select_previous);
503 // cx.register_action_type(Editor::toggle_comments);
504 // cx.register_action_type(Editor::select_larger_syntax_node);
505 // cx.register_action_type(Editor::select_smaller_syntax_node);
506 // cx.register_action_type(Editor::move_to_enclosing_bracket);
507 // cx.register_action_type(Editor::undo_selection);
508 // cx.register_action_type(Editor::redo_selection);
509 // cx.register_action_type(Editor::go_to_diagnostic);
510 // cx.register_action_type(Editor::go_to_prev_diagnostic);
511 // cx.register_action_type(Editor::go_to_hunk);
512 // cx.register_action_type(Editor::go_to_prev_hunk);
513 // cx.register_action_type(Editor::go_to_definition);
514 // cx.register_action_type(Editor::go_to_definition_split);
515 // cx.register_action_type(Editor::go_to_type_definition);
516 // cx.register_action_type(Editor::go_to_type_definition_split);
517 // cx.register_action_type(Editor::fold);
518 // cx.register_action_type(Editor::fold_at);
519 // cx.register_action_type(Editor::unfold_lines);
520 // cx.register_action_type(Editor::unfold_at);
521 // cx.register_action_type(Editor::gutter_hover);
522 // cx.register_action_type(Editor::fold_selected_ranges);
523 // cx.register_action_type(Editor::show_completions);
524 // cx.register_action_type(Editor::toggle_code_actions);
525 // cx.register_action_type(Editor::open_excerpts);
526 // cx.register_action_type(Editor::toggle_soft_wrap);
527 // cx.register_action_type(Editor::toggle_inlay_hints);
528 // cx.register_action_type(Editor::reveal_in_finder);
529 // cx.register_action_type(Editor::copy_path);
530 // cx.register_action_type(Editor::copy_relative_path);
531 // cx.register_action_type(Editor::copy_highlight_json);
532 // cx.add_async_action(Editor::format);
533 // cx.register_action_type(Editor::restart_language_server);
534 // cx.register_action_type(Editor::show_character_palette);
535 // cx.add_async_action(Editor::confirm_completion);
536 // cx.add_async_action(Editor::confirm_code_action);
537 // cx.add_async_action(Editor::rename);
538 // cx.add_async_action(Editor::confirm_rename);
539 // cx.add_async_action(Editor::find_all_references);
540 // cx.register_action_type(Editor::next_copilot_suggestion);
541 // cx.register_action_type(Editor::previous_copilot_suggestion);
542 // cx.register_action_type(Editor::copilot_suggest);
543 // cx.register_action_type(Editor::context_menu_first);
544 // cx.register_action_type(Editor::context_menu_prev);
545 // cx.register_action_type(Editor::context_menu_next);
546 // cx.register_action_type(Editor::context_menu_last);
547
548 hover_popover::init(cx);
549
550 workspace::register_project_item::<Editor>(cx);
551 workspace::register_followable_item::<Editor>(cx);
552 workspace::register_deserializable_item::<Editor>(cx);
553}
554
555trait InvalidationRegion {
556 fn ranges(&self) -> &[Range<Anchor>];
557}
558
559#[derive(Clone, Debug, PartialEq)]
560pub enum SelectPhase {
561 Begin {
562 position: DisplayPoint,
563 add: bool,
564 click_count: usize,
565 },
566 BeginColumnar {
567 position: DisplayPoint,
568 goal_column: u32,
569 },
570 Extend {
571 position: DisplayPoint,
572 click_count: usize,
573 },
574 Update {
575 position: DisplayPoint,
576 goal_column: u32,
577 scroll_position: gpui::Point<f32>,
578 },
579 End,
580}
581
582#[derive(Clone, Debug)]
583pub enum SelectMode {
584 Character,
585 Word(Range<Anchor>),
586 Line(Range<Anchor>),
587 All,
588}
589
590#[derive(Copy, Clone, PartialEq, Eq, Debug)]
591pub enum EditorMode {
592 SingleLine,
593 AutoHeight { max_lines: usize },
594 Full,
595}
596
597#[derive(Clone, Debug)]
598pub enum SoftWrap {
599 None,
600 EditorWidth,
601 Column(u32),
602}
603
604#[derive(Clone)]
605pub struct EditorStyle {
606 pub background: Hsla,
607 pub local_player: PlayerColor,
608 pub text: TextStyle,
609 pub scrollbar_width: Pixels,
610 pub syntax: Arc<SyntaxTheme>,
611 pub diagnostic_style: DiagnosticStyle,
612}
613
614type CompletionId = usize;
615
616// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
617// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
618
619type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
620type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
621
622pub struct Editor {
623 handle: WeakView<Self>,
624 focus_handle: FocusHandle,
625 buffer: Model<MultiBuffer>,
626 display_map: Model<DisplayMap>,
627 pub selections: SelectionsCollection,
628 pub scroll_manager: ScrollManager,
629 columnar_selection_tail: Option<Anchor>,
630 add_selections_state: Option<AddSelectionsState>,
631 select_next_state: Option<SelectNextState>,
632 select_prev_state: Option<SelectNextState>,
633 selection_history: SelectionHistory,
634 autoclose_regions: Vec<AutocloseRegion>,
635 snippet_stack: InvalidationStack<SnippetState>,
636 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
637 ime_transaction: Option<TransactionId>,
638 active_diagnostics: Option<ActiveDiagnosticGroup>,
639 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
640 // get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
641 // override_text_style: Option<Box<OverrideTextStyle>>,
642 project: Option<Model<Project>>,
643 collaboration_hub: Option<Box<dyn CollaborationHub>>,
644 blink_manager: Model<BlinkManager>,
645 pub show_local_selections: bool,
646 mode: EditorMode,
647 show_gutter: bool,
648 show_wrap_guides: Option<bool>,
649 placeholder_text: Option<Arc<str>>,
650 highlighted_rows: Option<Range<u32>>,
651 background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
652 inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
653 nav_history: Option<ItemNavHistory>,
654 context_menu: RwLock<Option<ContextMenu>>,
655 // mouse_context_menu: View<context_menu::ContextMenu>,
656 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
657 next_completion_id: CompletionId,
658 available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
659 code_actions_task: Option<Task<()>>,
660 document_highlights_task: Option<Task<()>>,
661 pending_rename: Option<RenameState>,
662 searchable: bool,
663 cursor_shape: CursorShape,
664 collapse_matches: bool,
665 autoindent_mode: Option<AutoindentMode>,
666 workspace: Option<(WeakView<Workspace>, i64)>,
667 keymap_context_layers: BTreeMap<TypeId, DispatchContext>,
668 input_enabled: bool,
669 read_only: bool,
670 leader_peer_id: Option<PeerId>,
671 remote_id: Option<ViewId>,
672 hover_state: HoverState,
673 gutter_hovered: bool,
674 link_go_to_definition_state: LinkGoToDefinitionState,
675 copilot_state: CopilotState,
676 inlay_hint_cache: InlayHintCache,
677 next_inlay_id: usize,
678 _subscriptions: Vec<Subscription>,
679 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
680 gutter_width: Pixels,
681 style: Option<EditorStyle>,
682}
683
684pub struct EditorSnapshot {
685 pub mode: EditorMode,
686 pub show_gutter: bool,
687 pub display_snapshot: DisplaySnapshot,
688 pub placeholder_text: Option<Arc<str>>,
689 is_focused: bool,
690 scroll_anchor: ScrollAnchor,
691 ongoing_scroll: OngoingScroll,
692}
693
694pub struct RemoteSelection {
695 pub replica_id: ReplicaId,
696 pub selection: Selection<Anchor>,
697 pub cursor_shape: CursorShape,
698 pub peer_id: PeerId,
699 pub line_mode: bool,
700 pub participant_index: Option<ParticipantIndex>,
701}
702
703#[derive(Clone, Debug)]
704struct SelectionHistoryEntry {
705 selections: Arc<[Selection<Anchor>]>,
706 select_next_state: Option<SelectNextState>,
707 select_prev_state: Option<SelectNextState>,
708 add_selections_state: Option<AddSelectionsState>,
709}
710
711enum SelectionHistoryMode {
712 Normal,
713 Undoing,
714 Redoing,
715}
716
717impl Default for SelectionHistoryMode {
718 fn default() -> Self {
719 Self::Normal
720 }
721}
722
723#[derive(Default)]
724struct SelectionHistory {
725 #[allow(clippy::type_complexity)]
726 selections_by_transaction:
727 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
728 mode: SelectionHistoryMode,
729 undo_stack: VecDeque<SelectionHistoryEntry>,
730 redo_stack: VecDeque<SelectionHistoryEntry>,
731}
732
733impl SelectionHistory {
734 fn insert_transaction(
735 &mut self,
736 transaction_id: TransactionId,
737 selections: Arc<[Selection<Anchor>]>,
738 ) {
739 self.selections_by_transaction
740 .insert(transaction_id, (selections, None));
741 }
742
743 #[allow(clippy::type_complexity)]
744 fn transaction(
745 &self,
746 transaction_id: TransactionId,
747 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
748 self.selections_by_transaction.get(&transaction_id)
749 }
750
751 #[allow(clippy::type_complexity)]
752 fn transaction_mut(
753 &mut self,
754 transaction_id: TransactionId,
755 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
756 self.selections_by_transaction.get_mut(&transaction_id)
757 }
758
759 fn push(&mut self, entry: SelectionHistoryEntry) {
760 if !entry.selections.is_empty() {
761 match self.mode {
762 SelectionHistoryMode::Normal => {
763 self.push_undo(entry);
764 self.redo_stack.clear();
765 }
766 SelectionHistoryMode::Undoing => self.push_redo(entry),
767 SelectionHistoryMode::Redoing => self.push_undo(entry),
768 }
769 }
770 }
771
772 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
773 if self
774 .undo_stack
775 .back()
776 .map_or(true, |e| e.selections != entry.selections)
777 {
778 self.undo_stack.push_back(entry);
779 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
780 self.undo_stack.pop_front();
781 }
782 }
783 }
784
785 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
786 if self
787 .redo_stack
788 .back()
789 .map_or(true, |e| e.selections != entry.selections)
790 {
791 self.redo_stack.push_back(entry);
792 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
793 self.redo_stack.pop_front();
794 }
795 }
796 }
797}
798
799#[derive(Clone, Debug)]
800struct AddSelectionsState {
801 above: bool,
802 stack: Vec<usize>,
803}
804
805#[derive(Clone)]
806struct SelectNextState {
807 query: AhoCorasick,
808 wordwise: bool,
809 done: bool,
810}
811
812impl std::fmt::Debug for SelectNextState {
813 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
814 f.debug_struct(std::any::type_name::<Self>())
815 .field("wordwise", &self.wordwise)
816 .field("done", &self.done)
817 .finish()
818 }
819}
820
821#[derive(Debug)]
822struct AutocloseRegion {
823 selection_id: usize,
824 range: Range<Anchor>,
825 pair: BracketPair,
826}
827
828#[derive(Debug)]
829struct SnippetState {
830 ranges: Vec<Vec<Range<Anchor>>>,
831 active_index: usize,
832}
833
834pub struct RenameState {
835 pub range: Range<Anchor>,
836 pub old_name: Arc<str>,
837 pub editor: View<Editor>,
838 block_id: BlockId,
839}
840
841struct InvalidationStack<T>(Vec<T>);
842
843enum ContextMenu {
844 Completions(CompletionsMenu),
845 CodeActions(CodeActionsMenu),
846}
847
848impl ContextMenu {
849 fn select_first(
850 &mut self,
851 project: Option<&Model<Project>>,
852 cx: &mut ViewContext<Editor>,
853 ) -> bool {
854 if self.visible() {
855 match self {
856 ContextMenu::Completions(menu) => menu.select_first(project, cx),
857 ContextMenu::CodeActions(menu) => menu.select_first(cx),
858 }
859 true
860 } else {
861 false
862 }
863 }
864
865 fn select_prev(
866 &mut self,
867 project: Option<&Model<Project>>,
868 cx: &mut ViewContext<Editor>,
869 ) -> bool {
870 if self.visible() {
871 match self {
872 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
873 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
874 }
875 true
876 } else {
877 false
878 }
879 }
880
881 fn select_next(
882 &mut self,
883 project: Option<&Model<Project>>,
884 cx: &mut ViewContext<Editor>,
885 ) -> bool {
886 if self.visible() {
887 match self {
888 ContextMenu::Completions(menu) => menu.select_next(project, cx),
889 ContextMenu::CodeActions(menu) => menu.select_next(cx),
890 }
891 true
892 } else {
893 false
894 }
895 }
896
897 fn select_last(
898 &mut self,
899 project: Option<&Model<Project>>,
900 cx: &mut ViewContext<Editor>,
901 ) -> bool {
902 if self.visible() {
903 match self {
904 ContextMenu::Completions(menu) => menu.select_last(project, cx),
905 ContextMenu::CodeActions(menu) => menu.select_last(cx),
906 }
907 true
908 } else {
909 false
910 }
911 }
912
913 fn visible(&self) -> bool {
914 match self {
915 ContextMenu::Completions(menu) => menu.visible(),
916 ContextMenu::CodeActions(menu) => menu.visible(),
917 }
918 }
919
920 fn render(
921 &self,
922 cursor_position: DisplayPoint,
923 style: &EditorStyle,
924 workspace: Option<WeakView<Workspace>>,
925 cx: &mut ViewContext<Editor>,
926 ) -> (DisplayPoint, AnyElement<Editor>) {
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 ) -> AnyElement<Editor> {
1259 todo!("old implementation below")
1260 }
1261
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.focus_handle.is_focused(cx) {
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 Some(
4401 IconButton::new("code_actions", ui2::Icon::Bolt)
4402 .on_click(|editor: &mut Editor, cx| {
4403 editor.toggle_code_actions(
4404 &ToggleCodeActions {
4405 deployed_from_indicator: true,
4406 },
4407 cx,
4408 );
4409 })
4410 .render(),
4411 )
4412 } else {
4413 None
4414 }
4415 }
4416
4417 // pub fn render_fold_indicators(
4418 // &self,
4419 // fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
4420 // style: &EditorStyle,
4421 // gutter_hovered: bool,
4422 // line_height: f32,
4423 // gutter_margin: f32,
4424 // cx: &mut ViewContext<Self>,
4425 // ) -> Vec<Option<AnyElement<Self>>> {
4426 // enum FoldIndicators {}
4427
4428 // let style = style.folds.clone();
4429
4430 // fold_data
4431 // .iter()
4432 // .enumerate()
4433 // .map(|(ix, fold_data)| {
4434 // fold_data
4435 // .map(|(fold_status, buffer_row, active)| {
4436 // (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4437 // MouseEventHandler::new::<FoldIndicators, _>(
4438 // ix as usize,
4439 // cx,
4440 // |mouse_state, _| {
4441 // Svg::new(match fold_status {
4442 // FoldStatus::Folded => style.folded_icon.clone(),
4443 // FoldStatus::Foldable => style.foldable_icon.clone(),
4444 // })
4445 // .with_color(
4446 // style
4447 // .indicator
4448 // .in_state(fold_status == FoldStatus::Folded)
4449 // .style_for(mouse_state)
4450 // .color,
4451 // )
4452 // .constrained()
4453 // .with_width(gutter_margin * style.icon_margin_scale)
4454 // .aligned()
4455 // .constrained()
4456 // .with_height(line_height)
4457 // .with_width(gutter_margin)
4458 // .aligned()
4459 // },
4460 // )
4461 // .with_cursor_style(CursorStyle::PointingHand)
4462 // .with_padding(Padding::uniform(3.))
4463 // .on_click(MouseButton::Left, {
4464 // move |_, editor, cx| match fold_status {
4465 // FoldStatus::Folded => {
4466 // editor.unfold_at(&UnfoldAt { buffer_row }, cx);
4467 // }
4468 // FoldStatus::Foldable => {
4469 // editor.fold_at(&FoldAt { buffer_row }, cx);
4470 // }
4471 // }
4472 // })
4473 // .into_any()
4474 // })
4475 // })
4476 // .flatten()
4477 // })
4478 // .collect()
4479 // }
4480
4481 pub fn context_menu_visible(&self) -> bool {
4482 self.context_menu
4483 .read()
4484 .as_ref()
4485 .map_or(false, |menu| menu.visible())
4486 }
4487
4488 pub fn render_context_menu(
4489 &self,
4490 cursor_position: DisplayPoint,
4491 style: &EditorStyle,
4492 cx: &mut ViewContext<Editor>,
4493 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
4494 self.context_menu.read().as_ref().map(|menu| {
4495 menu.render(
4496 cursor_position,
4497 style,
4498 self.workspace.as_ref().map(|(w, _)| w.clone()),
4499 cx,
4500 )
4501 })
4502 }
4503
4504 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4505 cx.notify();
4506 self.completion_tasks.clear();
4507 let context_menu = self.context_menu.write().take();
4508 if context_menu.is_some() {
4509 self.update_visible_copilot_suggestion(cx);
4510 }
4511 context_menu
4512 }
4513
4514 pub fn insert_snippet(
4515 &mut self,
4516 insertion_ranges: &[Range<usize>],
4517 snippet: Snippet,
4518 cx: &mut ViewContext<Self>,
4519 ) -> Result<()> {
4520 let tabstops = self.buffer.update(cx, |buffer, cx| {
4521 let snippet_text: Arc<str> = snippet.text.clone().into();
4522 buffer.edit(
4523 insertion_ranges
4524 .iter()
4525 .cloned()
4526 .map(|range| (range, snippet_text.clone())),
4527 Some(AutoindentMode::EachLine),
4528 cx,
4529 );
4530
4531 let snapshot = &*buffer.read(cx);
4532 let snippet = &snippet;
4533 snippet
4534 .tabstops
4535 .iter()
4536 .map(|tabstop| {
4537 let mut tabstop_ranges = tabstop
4538 .iter()
4539 .flat_map(|tabstop_range| {
4540 let mut delta = 0_isize;
4541 insertion_ranges.iter().map(move |insertion_range| {
4542 let insertion_start = insertion_range.start as isize + delta;
4543 delta +=
4544 snippet.text.len() as isize - insertion_range.len() as isize;
4545
4546 let start = snapshot.anchor_before(
4547 (insertion_start + tabstop_range.start) as usize,
4548 );
4549 let end = snapshot
4550 .anchor_after((insertion_start + tabstop_range.end) as usize);
4551 start..end
4552 })
4553 })
4554 .collect::<Vec<_>>();
4555 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4556 tabstop_ranges
4557 })
4558 .collect::<Vec<_>>()
4559 });
4560
4561 if let Some(tabstop) = tabstops.first() {
4562 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4563 s.select_ranges(tabstop.iter().cloned());
4564 });
4565 self.snippet_stack.push(SnippetState {
4566 active_index: 0,
4567 ranges: tabstops,
4568 });
4569 }
4570
4571 Ok(())
4572 }
4573
4574 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4575 self.move_to_snippet_tabstop(Bias::Right, cx)
4576 }
4577
4578 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4579 self.move_to_snippet_tabstop(Bias::Left, cx)
4580 }
4581
4582 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4583 if let Some(mut snippet) = self.snippet_stack.pop() {
4584 match bias {
4585 Bias::Left => {
4586 if snippet.active_index > 0 {
4587 snippet.active_index -= 1;
4588 } else {
4589 self.snippet_stack.push(snippet);
4590 return false;
4591 }
4592 }
4593 Bias::Right => {
4594 if snippet.active_index + 1 < snippet.ranges.len() {
4595 snippet.active_index += 1;
4596 } else {
4597 self.snippet_stack.push(snippet);
4598 return false;
4599 }
4600 }
4601 }
4602 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4603 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4604 s.select_anchor_ranges(current_ranges.iter().cloned())
4605 });
4606 // If snippet state is not at the last tabstop, push it back on the stack
4607 if snippet.active_index + 1 < snippet.ranges.len() {
4608 self.snippet_stack.push(snippet);
4609 }
4610 return true;
4611 }
4612 }
4613
4614 false
4615 }
4616
4617 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4618 self.transact(cx, |this, cx| {
4619 this.select_all(&SelectAll, cx);
4620 this.insert("", cx);
4621 });
4622 }
4623
4624 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4625 self.transact(cx, |this, cx| {
4626 this.select_autoclose_pair(cx);
4627 let mut selections = this.selections.all::<Point>(cx);
4628 if !this.selections.line_mode {
4629 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4630 for selection in &mut selections {
4631 if selection.is_empty() {
4632 let old_head = selection.head();
4633 let mut new_head =
4634 movement::left(&display_map, old_head.to_display_point(&display_map))
4635 .to_point(&display_map);
4636 if let Some((buffer, line_buffer_range)) = display_map
4637 .buffer_snapshot
4638 .buffer_line_for_row(old_head.row)
4639 {
4640 let indent_size =
4641 buffer.indent_size_for_line(line_buffer_range.start.row);
4642 let indent_len = match indent_size.kind {
4643 IndentKind::Space => {
4644 buffer.settings_at(line_buffer_range.start, cx).tab_size
4645 }
4646 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4647 };
4648 if old_head.column <= indent_size.len && old_head.column > 0 {
4649 let indent_len = indent_len.get();
4650 new_head = cmp::min(
4651 new_head,
4652 Point::new(
4653 old_head.row,
4654 ((old_head.column - 1) / indent_len) * indent_len,
4655 ),
4656 );
4657 }
4658 }
4659
4660 selection.set_head(new_head, SelectionGoal::None);
4661 }
4662 }
4663 }
4664
4665 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4666 this.insert("", cx);
4667 this.refresh_copilot_suggestions(true, cx);
4668 });
4669 }
4670
4671 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4672 self.transact(cx, |this, cx| {
4673 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4674 let line_mode = s.line_mode;
4675 s.move_with(|map, selection| {
4676 if selection.is_empty() && !line_mode {
4677 let cursor = movement::right(map, selection.head());
4678 selection.end = cursor;
4679 selection.reversed = true;
4680 selection.goal = SelectionGoal::None;
4681 }
4682 })
4683 });
4684 this.insert("", cx);
4685 this.refresh_copilot_suggestions(true, cx);
4686 });
4687 }
4688
4689 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4690 if self.move_to_prev_snippet_tabstop(cx) {
4691 return;
4692 }
4693
4694 self.outdent(&Outdent, cx);
4695 }
4696
4697 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4698 if self.move_to_next_snippet_tabstop(cx) {
4699 return;
4700 }
4701
4702 let mut selections = self.selections.all_adjusted(cx);
4703 let buffer = self.buffer.read(cx);
4704 let snapshot = buffer.snapshot(cx);
4705 let rows_iter = selections.iter().map(|s| s.head().row);
4706 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4707
4708 let mut edits = Vec::new();
4709 let mut prev_edited_row = 0;
4710 let mut row_delta = 0;
4711 for selection in &mut selections {
4712 if selection.start.row != prev_edited_row {
4713 row_delta = 0;
4714 }
4715 prev_edited_row = selection.end.row;
4716
4717 // If the selection is non-empty, then increase the indentation of the selected lines.
4718 if !selection.is_empty() {
4719 row_delta =
4720 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4721 continue;
4722 }
4723
4724 // If the selection is empty and the cursor is in the leading whitespace before the
4725 // suggested indentation, then auto-indent the line.
4726 let cursor = selection.head();
4727 let current_indent = snapshot.indent_size_for_line(cursor.row);
4728 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4729 if cursor.column < suggested_indent.len
4730 && cursor.column <= current_indent.len
4731 && current_indent.len <= suggested_indent.len
4732 {
4733 selection.start = Point::new(cursor.row, suggested_indent.len);
4734 selection.end = selection.start;
4735 if row_delta == 0 {
4736 edits.extend(Buffer::edit_for_indent_size_adjustment(
4737 cursor.row,
4738 current_indent,
4739 suggested_indent,
4740 ));
4741 row_delta = suggested_indent.len - current_indent.len;
4742 }
4743 continue;
4744 }
4745 }
4746
4747 // Accept copilot suggestion if there is only one selection and the cursor is not
4748 // in the leading whitespace.
4749 if self.selections.count() == 1
4750 && cursor.column >= current_indent.len
4751 && self.has_active_copilot_suggestion(cx)
4752 {
4753 self.accept_copilot_suggestion(cx);
4754 return;
4755 }
4756
4757 // Otherwise, insert a hard or soft tab.
4758 let settings = buffer.settings_at(cursor, cx);
4759 let tab_size = if settings.hard_tabs {
4760 IndentSize::tab()
4761 } else {
4762 let tab_size = settings.tab_size.get();
4763 let char_column = snapshot
4764 .text_for_range(Point::new(cursor.row, 0)..cursor)
4765 .flat_map(str::chars)
4766 .count()
4767 + row_delta as usize;
4768 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4769 IndentSize::spaces(chars_to_next_tab_stop)
4770 };
4771 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4772 selection.end = selection.start;
4773 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4774 row_delta += tab_size.len;
4775 }
4776
4777 self.transact(cx, |this, cx| {
4778 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4779 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4780 this.refresh_copilot_suggestions(true, cx);
4781 });
4782 }
4783
4784 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4785 let mut selections = self.selections.all::<Point>(cx);
4786 let mut prev_edited_row = 0;
4787 let mut row_delta = 0;
4788 let mut edits = Vec::new();
4789 let buffer = self.buffer.read(cx);
4790 let snapshot = buffer.snapshot(cx);
4791 for selection in &mut selections {
4792 if selection.start.row != prev_edited_row {
4793 row_delta = 0;
4794 }
4795 prev_edited_row = selection.end.row;
4796
4797 row_delta =
4798 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4799 }
4800
4801 self.transact(cx, |this, cx| {
4802 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4803 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4804 });
4805 }
4806
4807 fn indent_selection(
4808 buffer: &MultiBuffer,
4809 snapshot: &MultiBufferSnapshot,
4810 selection: &mut Selection<Point>,
4811 edits: &mut Vec<(Range<Point>, String)>,
4812 delta_for_start_row: u32,
4813 cx: &AppContext,
4814 ) -> u32 {
4815 let settings = buffer.settings_at(selection.start, cx);
4816 let tab_size = settings.tab_size.get();
4817 let indent_kind = if settings.hard_tabs {
4818 IndentKind::Tab
4819 } else {
4820 IndentKind::Space
4821 };
4822 let mut start_row = selection.start.row;
4823 let mut end_row = selection.end.row + 1;
4824
4825 // If a selection ends at the beginning of a line, don't indent
4826 // that last line.
4827 if selection.end.column == 0 {
4828 end_row -= 1;
4829 }
4830
4831 // Avoid re-indenting a row that has already been indented by a
4832 // previous selection, but still update this selection's column
4833 // to reflect that indentation.
4834 if delta_for_start_row > 0 {
4835 start_row += 1;
4836 selection.start.column += delta_for_start_row;
4837 if selection.end.row == selection.start.row {
4838 selection.end.column += delta_for_start_row;
4839 }
4840 }
4841
4842 let mut delta_for_end_row = 0;
4843 for row in start_row..end_row {
4844 let current_indent = snapshot.indent_size_for_line(row);
4845 let indent_delta = match (current_indent.kind, indent_kind) {
4846 (IndentKind::Space, IndentKind::Space) => {
4847 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4848 IndentSize::spaces(columns_to_next_tab_stop)
4849 }
4850 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4851 (_, IndentKind::Tab) => IndentSize::tab(),
4852 };
4853
4854 let row_start = Point::new(row, 0);
4855 edits.push((
4856 row_start..row_start,
4857 indent_delta.chars().collect::<String>(),
4858 ));
4859
4860 // Update this selection's endpoints to reflect the indentation.
4861 if row == selection.start.row {
4862 selection.start.column += indent_delta.len;
4863 }
4864 if row == selection.end.row {
4865 selection.end.column += indent_delta.len;
4866 delta_for_end_row = indent_delta.len;
4867 }
4868 }
4869
4870 if selection.start.row == selection.end.row {
4871 delta_for_start_row + delta_for_end_row
4872 } else {
4873 delta_for_end_row
4874 }
4875 }
4876
4877 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4878 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4879 let selections = self.selections.all::<Point>(cx);
4880 let mut deletion_ranges = Vec::new();
4881 let mut last_outdent = None;
4882 {
4883 let buffer = self.buffer.read(cx);
4884 let snapshot = buffer.snapshot(cx);
4885 for selection in &selections {
4886 let settings = buffer.settings_at(selection.start, cx);
4887 let tab_size = settings.tab_size.get();
4888 let mut rows = selection.spanned_rows(false, &display_map);
4889
4890 // Avoid re-outdenting a row that has already been outdented by a
4891 // previous selection.
4892 if let Some(last_row) = last_outdent {
4893 if last_row == rows.start {
4894 rows.start += 1;
4895 }
4896 }
4897
4898 for row in rows {
4899 let indent_size = snapshot.indent_size_for_line(row);
4900 if indent_size.len > 0 {
4901 let deletion_len = match indent_size.kind {
4902 IndentKind::Space => {
4903 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4904 if columns_to_prev_tab_stop == 0 {
4905 tab_size
4906 } else {
4907 columns_to_prev_tab_stop
4908 }
4909 }
4910 IndentKind::Tab => 1,
4911 };
4912 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4913 last_outdent = Some(row);
4914 }
4915 }
4916 }
4917 }
4918
4919 self.transact(cx, |this, cx| {
4920 this.buffer.update(cx, |buffer, cx| {
4921 let empty_str: Arc<str> = "".into();
4922 buffer.edit(
4923 deletion_ranges
4924 .into_iter()
4925 .map(|range| (range, empty_str.clone())),
4926 None,
4927 cx,
4928 );
4929 });
4930 let selections = this.selections.all::<usize>(cx);
4931 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4932 });
4933 }
4934
4935 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4936 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4937 let selections = self.selections.all::<Point>(cx);
4938
4939 let mut new_cursors = Vec::new();
4940 let mut edit_ranges = Vec::new();
4941 let mut selections = selections.iter().peekable();
4942 while let Some(selection) = selections.next() {
4943 let mut rows = selection.spanned_rows(false, &display_map);
4944 let goal_display_column = selection.head().to_display_point(&display_map).column();
4945
4946 // Accumulate contiguous regions of rows that we want to delete.
4947 while let Some(next_selection) = selections.peek() {
4948 let next_rows = next_selection.spanned_rows(false, &display_map);
4949 if next_rows.start <= rows.end {
4950 rows.end = next_rows.end;
4951 selections.next().unwrap();
4952 } else {
4953 break;
4954 }
4955 }
4956
4957 let buffer = &display_map.buffer_snapshot;
4958 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4959 let edit_end;
4960 let cursor_buffer_row;
4961 if buffer.max_point().row >= rows.end {
4962 // If there's a line after the range, delete the \n from the end of the row range
4963 // and position the cursor on the next line.
4964 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4965 cursor_buffer_row = rows.end;
4966 } else {
4967 // If there isn't a line after the range, delete the \n from the line before the
4968 // start of the row range and position the cursor there.
4969 edit_start = edit_start.saturating_sub(1);
4970 edit_end = buffer.len();
4971 cursor_buffer_row = rows.start.saturating_sub(1);
4972 }
4973
4974 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4975 *cursor.column_mut() =
4976 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4977
4978 new_cursors.push((
4979 selection.id,
4980 buffer.anchor_after(cursor.to_point(&display_map)),
4981 ));
4982 edit_ranges.push(edit_start..edit_end);
4983 }
4984
4985 self.transact(cx, |this, cx| {
4986 let buffer = this.buffer.update(cx, |buffer, cx| {
4987 let empty_str: Arc<str> = "".into();
4988 buffer.edit(
4989 edit_ranges
4990 .into_iter()
4991 .map(|range| (range, empty_str.clone())),
4992 None,
4993 cx,
4994 );
4995 buffer.snapshot(cx)
4996 });
4997 let new_selections = new_cursors
4998 .into_iter()
4999 .map(|(id, cursor)| {
5000 let cursor = cursor.to_point(&buffer);
5001 Selection {
5002 id,
5003 start: cursor,
5004 end: cursor,
5005 reversed: false,
5006 goal: SelectionGoal::None,
5007 }
5008 })
5009 .collect();
5010
5011 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5012 s.select(new_selections);
5013 });
5014 });
5015 }
5016
5017 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5018 let mut row_ranges = Vec::<Range<u32>>::new();
5019 for selection in self.selections.all::<Point>(cx) {
5020 let start = selection.start.row;
5021 let end = if selection.start.row == selection.end.row {
5022 selection.start.row + 1
5023 } else {
5024 selection.end.row
5025 };
5026
5027 if let Some(last_row_range) = row_ranges.last_mut() {
5028 if start <= last_row_range.end {
5029 last_row_range.end = end;
5030 continue;
5031 }
5032 }
5033 row_ranges.push(start..end);
5034 }
5035
5036 let snapshot = self.buffer.read(cx).snapshot(cx);
5037 let mut cursor_positions = Vec::new();
5038 for row_range in &row_ranges {
5039 let anchor = snapshot.anchor_before(Point::new(
5040 row_range.end - 1,
5041 snapshot.line_len(row_range.end - 1),
5042 ));
5043 cursor_positions.push(anchor.clone()..anchor);
5044 }
5045
5046 self.transact(cx, |this, cx| {
5047 for row_range in row_ranges.into_iter().rev() {
5048 for row in row_range.rev() {
5049 let end_of_line = Point::new(row, snapshot.line_len(row));
5050 let indent = snapshot.indent_size_for_line(row + 1);
5051 let start_of_next_line = Point::new(row + 1, indent.len);
5052
5053 let replace = if snapshot.line_len(row + 1) > indent.len {
5054 " "
5055 } else {
5056 ""
5057 };
5058
5059 this.buffer.update(cx, |buffer, cx| {
5060 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5061 });
5062 }
5063 }
5064
5065 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5066 s.select_anchor_ranges(cursor_positions)
5067 });
5068 });
5069 }
5070
5071 pub fn sort_lines_case_sensitive(
5072 &mut self,
5073 _: &SortLinesCaseSensitive,
5074 cx: &mut ViewContext<Self>,
5075 ) {
5076 self.manipulate_lines(cx, |lines| lines.sort())
5077 }
5078
5079 pub fn sort_lines_case_insensitive(
5080 &mut self,
5081 _: &SortLinesCaseInsensitive,
5082 cx: &mut ViewContext<Self>,
5083 ) {
5084 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5085 }
5086
5087 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5088 self.manipulate_lines(cx, |lines| lines.reverse())
5089 }
5090
5091 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5092 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5093 }
5094
5095 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5096 where
5097 Fn: FnMut(&mut [&str]),
5098 {
5099 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5100 let buffer = self.buffer.read(cx).snapshot(cx);
5101
5102 let mut edits = Vec::new();
5103
5104 let selections = self.selections.all::<Point>(cx);
5105 let mut selections = selections.iter().peekable();
5106 let mut contiguous_row_selections = Vec::new();
5107 let mut new_selections = Vec::new();
5108
5109 while let Some(selection) = selections.next() {
5110 let (start_row, end_row) = consume_contiguous_rows(
5111 &mut contiguous_row_selections,
5112 selection,
5113 &display_map,
5114 &mut selections,
5115 );
5116
5117 let start_point = Point::new(start_row, 0);
5118 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
5119 let text = buffer
5120 .text_for_range(start_point..end_point)
5121 .collect::<String>();
5122 let mut lines = text.split("\n").collect_vec();
5123
5124 let lines_len = lines.len();
5125 callback(&mut lines);
5126
5127 // This is a current limitation with selections.
5128 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
5129 debug_assert!(
5130 lines.len() == lines_len,
5131 "callback should not change the number of lines"
5132 );
5133
5134 edits.push((start_point..end_point, lines.join("\n")));
5135 let start_anchor = buffer.anchor_after(start_point);
5136 let end_anchor = buffer.anchor_before(end_point);
5137
5138 // Make selection and push
5139 new_selections.push(Selection {
5140 id: selection.id,
5141 start: start_anchor.to_offset(&buffer),
5142 end: end_anchor.to_offset(&buffer),
5143 goal: SelectionGoal::None,
5144 reversed: selection.reversed,
5145 });
5146 }
5147
5148 self.transact(cx, |this, cx| {
5149 this.buffer.update(cx, |buffer, cx| {
5150 buffer.edit(edits, None, cx);
5151 });
5152
5153 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5154 s.select(new_selections);
5155 });
5156
5157 this.request_autoscroll(Autoscroll::fit(), cx);
5158 });
5159 }
5160
5161 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5162 self.manipulate_text(cx, |text| text.to_uppercase())
5163 }
5164
5165 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5166 self.manipulate_text(cx, |text| text.to_lowercase())
5167 }
5168
5169 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5170 self.manipulate_text(cx, |text| {
5171 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5172 // https://github.com/rutrum/convert-case/issues/16
5173 text.split("\n")
5174 .map(|line| line.to_case(Case::Title))
5175 .join("\n")
5176 })
5177 }
5178
5179 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5180 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5181 }
5182
5183 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5184 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5185 }
5186
5187 pub fn convert_to_upper_camel_case(
5188 &mut self,
5189 _: &ConvertToUpperCamelCase,
5190 cx: &mut ViewContext<Self>,
5191 ) {
5192 self.manipulate_text(cx, |text| {
5193 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5194 // https://github.com/rutrum/convert-case/issues/16
5195 text.split("\n")
5196 .map(|line| line.to_case(Case::UpperCamel))
5197 .join("\n")
5198 })
5199 }
5200
5201 pub fn convert_to_lower_camel_case(
5202 &mut self,
5203 _: &ConvertToLowerCamelCase,
5204 cx: &mut ViewContext<Self>,
5205 ) {
5206 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5207 }
5208
5209 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5210 where
5211 Fn: FnMut(&str) -> String,
5212 {
5213 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5214 let buffer = self.buffer.read(cx).snapshot(cx);
5215
5216 let mut new_selections = Vec::new();
5217 let mut edits = Vec::new();
5218 let mut selection_adjustment = 0i32;
5219
5220 for selection in self.selections.all::<usize>(cx) {
5221 let selection_is_empty = selection.is_empty();
5222
5223 let (start, end) = if selection_is_empty {
5224 let word_range = movement::surrounding_word(
5225 &display_map,
5226 selection.start.to_display_point(&display_map),
5227 );
5228 let start = word_range.start.to_offset(&display_map, Bias::Left);
5229 let end = word_range.end.to_offset(&display_map, Bias::Left);
5230 (start, end)
5231 } else {
5232 (selection.start, selection.end)
5233 };
5234
5235 let text = buffer.text_for_range(start..end).collect::<String>();
5236 let old_length = text.len() as i32;
5237 let text = callback(&text);
5238
5239 new_selections.push(Selection {
5240 start: (start as i32 - selection_adjustment) as usize,
5241 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
5242 goal: SelectionGoal::None,
5243 ..selection
5244 });
5245
5246 selection_adjustment += old_length - text.len() as i32;
5247
5248 edits.push((start..end, text));
5249 }
5250
5251 self.transact(cx, |this, cx| {
5252 this.buffer.update(cx, |buffer, cx| {
5253 buffer.edit(edits, None, cx);
5254 });
5255
5256 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5257 s.select(new_selections);
5258 });
5259
5260 this.request_autoscroll(Autoscroll::fit(), cx);
5261 });
5262 }
5263
5264 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
5265 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5266 let buffer = &display_map.buffer_snapshot;
5267 let selections = self.selections.all::<Point>(cx);
5268
5269 let mut edits = Vec::new();
5270 let mut selections_iter = selections.iter().peekable();
5271 while let Some(selection) = selections_iter.next() {
5272 // Avoid duplicating the same lines twice.
5273 let mut rows = selection.spanned_rows(false, &display_map);
5274
5275 while let Some(next_selection) = selections_iter.peek() {
5276 let next_rows = next_selection.spanned_rows(false, &display_map);
5277 if next_rows.start < rows.end {
5278 rows.end = next_rows.end;
5279 selections_iter.next().unwrap();
5280 } else {
5281 break;
5282 }
5283 }
5284
5285 // Copy the text from the selected row region and splice it at the start of the region.
5286 let start = Point::new(rows.start, 0);
5287 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
5288 let text = buffer
5289 .text_for_range(start..end)
5290 .chain(Some("\n"))
5291 .collect::<String>();
5292 edits.push((start..start, text));
5293 }
5294
5295 self.transact(cx, |this, cx| {
5296 this.buffer.update(cx, |buffer, cx| {
5297 buffer.edit(edits, None, cx);
5298 });
5299
5300 this.request_autoscroll(Autoscroll::fit(), cx);
5301 });
5302 }
5303
5304 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5305 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5306 let buffer = self.buffer.read(cx).snapshot(cx);
5307
5308 let mut edits = Vec::new();
5309 let mut unfold_ranges = Vec::new();
5310 let mut refold_ranges = Vec::new();
5311
5312 let selections = self.selections.all::<Point>(cx);
5313 let mut selections = selections.iter().peekable();
5314 let mut contiguous_row_selections = Vec::new();
5315 let mut new_selections = Vec::new();
5316
5317 while let Some(selection) = selections.next() {
5318 // Find all the selections that span a contiguous row range
5319 let (start_row, end_row) = consume_contiguous_rows(
5320 &mut contiguous_row_selections,
5321 selection,
5322 &display_map,
5323 &mut selections,
5324 );
5325
5326 // Move the text spanned by the row range to be before the line preceding the row range
5327 if start_row > 0 {
5328 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
5329 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
5330 let insertion_point = display_map
5331 .prev_line_boundary(Point::new(start_row - 1, 0))
5332 .0;
5333
5334 // Don't move lines across excerpts
5335 if buffer
5336 .excerpt_boundaries_in_range((
5337 Bound::Excluded(insertion_point),
5338 Bound::Included(range_to_move.end),
5339 ))
5340 .next()
5341 .is_none()
5342 {
5343 let text = buffer
5344 .text_for_range(range_to_move.clone())
5345 .flat_map(|s| s.chars())
5346 .skip(1)
5347 .chain(['\n'])
5348 .collect::<String>();
5349
5350 edits.push((
5351 buffer.anchor_after(range_to_move.start)
5352 ..buffer.anchor_before(range_to_move.end),
5353 String::new(),
5354 ));
5355 let insertion_anchor = buffer.anchor_after(insertion_point);
5356 edits.push((insertion_anchor..insertion_anchor, text));
5357
5358 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5359
5360 // Move selections up
5361 new_selections.extend(contiguous_row_selections.drain(..).map(
5362 |mut selection| {
5363 selection.start.row -= row_delta;
5364 selection.end.row -= row_delta;
5365 selection
5366 },
5367 ));
5368
5369 // Move folds up
5370 unfold_ranges.push(range_to_move.clone());
5371 for fold in display_map.folds_in_range(
5372 buffer.anchor_before(range_to_move.start)
5373 ..buffer.anchor_after(range_to_move.end),
5374 ) {
5375 let mut start = fold.start.to_point(&buffer);
5376 let mut end = fold.end.to_point(&buffer);
5377 start.row -= row_delta;
5378 end.row -= row_delta;
5379 refold_ranges.push(start..end);
5380 }
5381 }
5382 }
5383
5384 // If we didn't move line(s), preserve the existing selections
5385 new_selections.append(&mut contiguous_row_selections);
5386 }
5387
5388 self.transact(cx, |this, cx| {
5389 this.unfold_ranges(unfold_ranges, true, true, cx);
5390 this.buffer.update(cx, |buffer, cx| {
5391 for (range, text) in edits {
5392 buffer.edit([(range, text)], None, cx);
5393 }
5394 });
5395 this.fold_ranges(refold_ranges, true, cx);
5396 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5397 s.select(new_selections);
5398 })
5399 });
5400 }
5401
5402 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5403 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5404 let buffer = self.buffer.read(cx).snapshot(cx);
5405
5406 let mut edits = Vec::new();
5407 let mut unfold_ranges = Vec::new();
5408 let mut refold_ranges = Vec::new();
5409
5410 let selections = self.selections.all::<Point>(cx);
5411 let mut selections = selections.iter().peekable();
5412 let mut contiguous_row_selections = Vec::new();
5413 let mut new_selections = Vec::new();
5414
5415 while let Some(selection) = selections.next() {
5416 // Find all the selections that span a contiguous row range
5417 let (start_row, end_row) = consume_contiguous_rows(
5418 &mut contiguous_row_selections,
5419 selection,
5420 &display_map,
5421 &mut selections,
5422 );
5423
5424 // Move the text spanned by the row range to be after the last line of the row range
5425 if end_row <= buffer.max_point().row {
5426 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
5427 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
5428
5429 // Don't move lines across excerpt boundaries
5430 if buffer
5431 .excerpt_boundaries_in_range((
5432 Bound::Excluded(range_to_move.start),
5433 Bound::Included(insertion_point),
5434 ))
5435 .next()
5436 .is_none()
5437 {
5438 let mut text = String::from("\n");
5439 text.extend(buffer.text_for_range(range_to_move.clone()));
5440 text.pop(); // Drop trailing newline
5441 edits.push((
5442 buffer.anchor_after(range_to_move.start)
5443 ..buffer.anchor_before(range_to_move.end),
5444 String::new(),
5445 ));
5446 let insertion_anchor = buffer.anchor_after(insertion_point);
5447 edits.push((insertion_anchor..insertion_anchor, text));
5448
5449 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5450
5451 // Move selections down
5452 new_selections.extend(contiguous_row_selections.drain(..).map(
5453 |mut selection| {
5454 selection.start.row += row_delta;
5455 selection.end.row += row_delta;
5456 selection
5457 },
5458 ));
5459
5460 // Move folds down
5461 unfold_ranges.push(range_to_move.clone());
5462 for fold in display_map.folds_in_range(
5463 buffer.anchor_before(range_to_move.start)
5464 ..buffer.anchor_after(range_to_move.end),
5465 ) {
5466 let mut start = fold.start.to_point(&buffer);
5467 let mut end = fold.end.to_point(&buffer);
5468 start.row += row_delta;
5469 end.row += row_delta;
5470 refold_ranges.push(start..end);
5471 }
5472 }
5473 }
5474
5475 // If we didn't move line(s), preserve the existing selections
5476 new_selections.append(&mut contiguous_row_selections);
5477 }
5478
5479 self.transact(cx, |this, cx| {
5480 this.unfold_ranges(unfold_ranges, true, true, cx);
5481 this.buffer.update(cx, |buffer, cx| {
5482 for (range, text) in edits {
5483 buffer.edit([(range, text)], None, cx);
5484 }
5485 });
5486 this.fold_ranges(refold_ranges, true, cx);
5487 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5488 });
5489 }
5490
5491 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5492 let text_layout_details = &self.text_layout_details(cx);
5493 self.transact(cx, |this, cx| {
5494 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5495 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5496 let line_mode = s.line_mode;
5497 s.move_with(|display_map, selection| {
5498 if !selection.is_empty() || line_mode {
5499 return;
5500 }
5501
5502 let mut head = selection.head();
5503 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5504 if head.column() == display_map.line_len(head.row()) {
5505 transpose_offset = display_map
5506 .buffer_snapshot
5507 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5508 }
5509
5510 if transpose_offset == 0 {
5511 return;
5512 }
5513
5514 *head.column_mut() += 1;
5515 head = display_map.clip_point(head, Bias::Right);
5516 let goal = SelectionGoal::HorizontalPosition(
5517 display_map.x_for_point(head, &text_layout_details).into(),
5518 );
5519 selection.collapse_to(head, goal);
5520
5521 let transpose_start = display_map
5522 .buffer_snapshot
5523 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5524 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5525 let transpose_end = display_map
5526 .buffer_snapshot
5527 .clip_offset(transpose_offset + 1, Bias::Right);
5528 if let Some(ch) =
5529 display_map.buffer_snapshot.chars_at(transpose_start).next()
5530 {
5531 edits.push((transpose_start..transpose_offset, String::new()));
5532 edits.push((transpose_end..transpose_end, ch.to_string()));
5533 }
5534 }
5535 });
5536 edits
5537 });
5538 this.buffer
5539 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5540 let selections = this.selections.all::<usize>(cx);
5541 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5542 s.select(selections);
5543 });
5544 });
5545 }
5546
5547 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5548 let mut text = String::new();
5549 let buffer = self.buffer.read(cx).snapshot(cx);
5550 let mut selections = self.selections.all::<Point>(cx);
5551 let mut clipboard_selections = Vec::with_capacity(selections.len());
5552 {
5553 let max_point = buffer.max_point();
5554 let mut is_first = true;
5555 for selection in &mut selections {
5556 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5557 if is_entire_line {
5558 selection.start = Point::new(selection.start.row, 0);
5559 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5560 selection.goal = SelectionGoal::None;
5561 }
5562 if is_first {
5563 is_first = false;
5564 } else {
5565 text += "\n";
5566 }
5567 let mut len = 0;
5568 for chunk in buffer.text_for_range(selection.start..selection.end) {
5569 text.push_str(chunk);
5570 len += chunk.len();
5571 }
5572 clipboard_selections.push(ClipboardSelection {
5573 len,
5574 is_entire_line,
5575 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5576 });
5577 }
5578 }
5579
5580 self.transact(cx, |this, cx| {
5581 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5582 s.select(selections);
5583 });
5584 this.insert("", cx);
5585 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5586 });
5587 }
5588
5589 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5590 let selections = self.selections.all::<Point>(cx);
5591 let buffer = self.buffer.read(cx).read(cx);
5592 let mut text = String::new();
5593
5594 let mut clipboard_selections = Vec::with_capacity(selections.len());
5595 {
5596 let max_point = buffer.max_point();
5597 let mut is_first = true;
5598 for selection in selections.iter() {
5599 let mut start = selection.start;
5600 let mut end = selection.end;
5601 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5602 if is_entire_line {
5603 start = Point::new(start.row, 0);
5604 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5605 }
5606 if is_first {
5607 is_first = false;
5608 } else {
5609 text += "\n";
5610 }
5611 let mut len = 0;
5612 for chunk in buffer.text_for_range(start..end) {
5613 text.push_str(chunk);
5614 len += chunk.len();
5615 }
5616 clipboard_selections.push(ClipboardSelection {
5617 len,
5618 is_entire_line,
5619 first_line_indent: buffer.indent_size_for_line(start.row).len,
5620 });
5621 }
5622 }
5623
5624 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5625 }
5626
5627 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5628 self.transact(cx, |this, cx| {
5629 if let Some(item) = cx.read_from_clipboard() {
5630 let clipboard_text = Cow::Borrowed(item.text());
5631 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5632 let old_selections = this.selections.all::<usize>(cx);
5633 let all_selections_were_entire_line =
5634 clipboard_selections.iter().all(|s| s.is_entire_line);
5635 let first_selection_indent_column =
5636 clipboard_selections.first().map(|s| s.first_line_indent);
5637 if clipboard_selections.len() != old_selections.len() {
5638 clipboard_selections.drain(..);
5639 }
5640
5641 this.buffer.update(cx, |buffer, cx| {
5642 let snapshot = buffer.read(cx);
5643 let mut start_offset = 0;
5644 let mut edits = Vec::new();
5645 let mut original_indent_columns = Vec::new();
5646 let line_mode = this.selections.line_mode;
5647 for (ix, selection) in old_selections.iter().enumerate() {
5648 let to_insert;
5649 let entire_line;
5650 let original_indent_column;
5651 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5652 let end_offset = start_offset + clipboard_selection.len;
5653 to_insert = &clipboard_text[start_offset..end_offset];
5654 entire_line = clipboard_selection.is_entire_line;
5655 start_offset = end_offset + 1;
5656 original_indent_column =
5657 Some(clipboard_selection.first_line_indent);
5658 } else {
5659 to_insert = clipboard_text.as_str();
5660 entire_line = all_selections_were_entire_line;
5661 original_indent_column = first_selection_indent_column
5662 }
5663
5664 // If the corresponding selection was empty when this slice of the
5665 // clipboard text was written, then the entire line containing the
5666 // selection was copied. If this selection is also currently empty,
5667 // then paste the line before the current line of the buffer.
5668 let range = if selection.is_empty() && !line_mode && entire_line {
5669 let column = selection.start.to_point(&snapshot).column as usize;
5670 let line_start = selection.start - column;
5671 line_start..line_start
5672 } else {
5673 selection.range()
5674 };
5675
5676 edits.push((range, to_insert));
5677 original_indent_columns.extend(original_indent_column);
5678 }
5679 drop(snapshot);
5680
5681 buffer.edit(
5682 edits,
5683 Some(AutoindentMode::Block {
5684 original_indent_columns,
5685 }),
5686 cx,
5687 );
5688 });
5689
5690 let selections = this.selections.all::<usize>(cx);
5691 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5692 } else {
5693 this.insert(&clipboard_text, cx);
5694 }
5695 }
5696 });
5697 }
5698
5699 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5700 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5701 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5702 self.change_selections(None, cx, |s| {
5703 s.select_anchors(selections.to_vec());
5704 });
5705 }
5706 self.request_autoscroll(Autoscroll::fit(), cx);
5707 self.unmark_text(cx);
5708 self.refresh_copilot_suggestions(true, cx);
5709 cx.emit(Event::Edited);
5710 }
5711 }
5712
5713 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5714 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5715 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5716 {
5717 self.change_selections(None, cx, |s| {
5718 s.select_anchors(selections.to_vec());
5719 });
5720 }
5721 self.request_autoscroll(Autoscroll::fit(), cx);
5722 self.unmark_text(cx);
5723 self.refresh_copilot_suggestions(true, cx);
5724 cx.emit(Event::Edited);
5725 }
5726 }
5727
5728 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5729 self.buffer
5730 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5731 }
5732
5733 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5734 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5735 let line_mode = s.line_mode;
5736 s.move_with(|map, selection| {
5737 let cursor = if selection.is_empty() && !line_mode {
5738 movement::left(map, selection.start)
5739 } else {
5740 selection.start
5741 };
5742 selection.collapse_to(cursor, SelectionGoal::None);
5743 });
5744 })
5745 }
5746
5747 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5748 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5749 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5750 })
5751 }
5752
5753 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5754 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5755 let line_mode = s.line_mode;
5756 s.move_with(|map, selection| {
5757 let cursor = if selection.is_empty() && !line_mode {
5758 movement::right(map, selection.end)
5759 } else {
5760 selection.end
5761 };
5762 selection.collapse_to(cursor, SelectionGoal::None)
5763 });
5764 })
5765 }
5766
5767 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5768 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5769 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5770 })
5771 }
5772
5773 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5774 if self.take_rename(true, cx).is_some() {
5775 return;
5776 }
5777
5778 if matches!(self.mode, EditorMode::SingleLine) {
5779 cx.propagate();
5780 return;
5781 }
5782
5783 let text_layout_details = &self.text_layout_details(cx);
5784
5785 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5786 let line_mode = s.line_mode;
5787 s.move_with(|map, selection| {
5788 if !selection.is_empty() && !line_mode {
5789 selection.goal = SelectionGoal::None;
5790 }
5791 let (cursor, goal) = movement::up(
5792 map,
5793 selection.start,
5794 selection.goal,
5795 false,
5796 &text_layout_details,
5797 );
5798 selection.collapse_to(cursor, goal);
5799 });
5800 })
5801 }
5802
5803 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5804 if self.take_rename(true, cx).is_some() {
5805 return;
5806 }
5807
5808 if matches!(self.mode, EditorMode::SingleLine) {
5809 cx.propagate();
5810 return;
5811 }
5812
5813 let row_count = if let Some(row_count) = self.visible_line_count() {
5814 row_count as u32 - 1
5815 } else {
5816 return;
5817 };
5818
5819 let autoscroll = if action.center_cursor {
5820 Autoscroll::center()
5821 } else {
5822 Autoscroll::fit()
5823 };
5824
5825 let text_layout_details = &self.text_layout_details(cx);
5826
5827 self.change_selections(Some(autoscroll), cx, |s| {
5828 let line_mode = s.line_mode;
5829 s.move_with(|map, selection| {
5830 if !selection.is_empty() && !line_mode {
5831 selection.goal = SelectionGoal::None;
5832 }
5833 let (cursor, goal) = movement::up_by_rows(
5834 map,
5835 selection.end,
5836 row_count,
5837 selection.goal,
5838 false,
5839 &text_layout_details,
5840 );
5841 selection.collapse_to(cursor, goal);
5842 });
5843 });
5844 }
5845
5846 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5847 let text_layout_details = &self.text_layout_details(cx);
5848 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5849 s.move_heads_with(|map, head, goal| {
5850 movement::up(map, head, goal, false, &text_layout_details)
5851 })
5852 })
5853 }
5854
5855 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5856 self.take_rename(true, cx);
5857
5858 if self.mode == EditorMode::SingleLine {
5859 cx.propagate();
5860 return;
5861 }
5862
5863 let text_layout_details = &self.text_layout_details(cx);
5864 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5865 let line_mode = s.line_mode;
5866 s.move_with(|map, selection| {
5867 if !selection.is_empty() && !line_mode {
5868 selection.goal = SelectionGoal::None;
5869 }
5870 let (cursor, goal) = movement::down(
5871 map,
5872 selection.end,
5873 selection.goal,
5874 false,
5875 &text_layout_details,
5876 );
5877 selection.collapse_to(cursor, goal);
5878 });
5879 });
5880 }
5881
5882 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5883 if self.take_rename(true, cx).is_some() {
5884 return;
5885 }
5886
5887 if self
5888 .context_menu
5889 .write()
5890 .as_mut()
5891 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5892 .unwrap_or(false)
5893 {
5894 return;
5895 }
5896
5897 if matches!(self.mode, EditorMode::SingleLine) {
5898 cx.propagate();
5899 return;
5900 }
5901
5902 let row_count = if let Some(row_count) = self.visible_line_count() {
5903 row_count as u32 - 1
5904 } else {
5905 return;
5906 };
5907
5908 let autoscroll = if action.center_cursor {
5909 Autoscroll::center()
5910 } else {
5911 Autoscroll::fit()
5912 };
5913
5914 let text_layout_details = &self.text_layout_details(cx);
5915 self.change_selections(Some(autoscroll), cx, |s| {
5916 let line_mode = s.line_mode;
5917 s.move_with(|map, selection| {
5918 if !selection.is_empty() && !line_mode {
5919 selection.goal = SelectionGoal::None;
5920 }
5921 let (cursor, goal) = movement::down_by_rows(
5922 map,
5923 selection.end,
5924 row_count,
5925 selection.goal,
5926 false,
5927 &text_layout_details,
5928 );
5929 selection.collapse_to(cursor, goal);
5930 });
5931 });
5932 }
5933
5934 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5935 let text_layout_details = &self.text_layout_details(cx);
5936 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5937 s.move_heads_with(|map, head, goal| {
5938 movement::down(map, head, goal, false, &text_layout_details)
5939 })
5940 });
5941 }
5942
5943 // pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5944 // if let Some(context_menu) = self.context_menu.write().as_mut() {
5945 // context_menu.select_first(self.project.as_ref(), cx);
5946 // }
5947 // }
5948
5949 // pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5950 // if let Some(context_menu) = self.context_menu.write().as_mut() {
5951 // context_menu.select_prev(self.project.as_ref(), cx);
5952 // }
5953 // }
5954
5955 // pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5956 // if let Some(context_menu) = self.context_menu.write().as_mut() {
5957 // context_menu.select_next(self.project.as_ref(), cx);
5958 // }
5959 // }
5960
5961 // pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5962 // if let Some(context_menu) = self.context_menu.write().as_mut() {
5963 // context_menu.select_last(self.project.as_ref(), cx);
5964 // }
5965 // }
5966
5967 pub fn move_to_previous_word_start(
5968 &mut self,
5969 _: &MoveToPreviousWordStart,
5970 cx: &mut ViewContext<Self>,
5971 ) {
5972 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5973 s.move_cursors_with(|map, head, _| {
5974 (
5975 movement::previous_word_start(map, head),
5976 SelectionGoal::None,
5977 )
5978 });
5979 })
5980 }
5981
5982 pub fn move_to_previous_subword_start(
5983 &mut self,
5984 _: &MoveToPreviousSubwordStart,
5985 cx: &mut ViewContext<Self>,
5986 ) {
5987 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5988 s.move_cursors_with(|map, head, _| {
5989 (
5990 movement::previous_subword_start(map, head),
5991 SelectionGoal::None,
5992 )
5993 });
5994 })
5995 }
5996
5997 pub fn select_to_previous_word_start(
5998 &mut self,
5999 _: &SelectToPreviousWordStart,
6000 cx: &mut ViewContext<Self>,
6001 ) {
6002 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6003 s.move_heads_with(|map, head, _| {
6004 (
6005 movement::previous_word_start(map, head),
6006 SelectionGoal::None,
6007 )
6008 });
6009 })
6010 }
6011
6012 pub fn select_to_previous_subword_start(
6013 &mut self,
6014 _: &SelectToPreviousSubwordStart,
6015 cx: &mut ViewContext<Self>,
6016 ) {
6017 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6018 s.move_heads_with(|map, head, _| {
6019 (
6020 movement::previous_subword_start(map, head),
6021 SelectionGoal::None,
6022 )
6023 });
6024 })
6025 }
6026
6027 pub fn delete_to_previous_word_start(
6028 &mut self,
6029 _: &DeleteToPreviousWordStart,
6030 cx: &mut ViewContext<Self>,
6031 ) {
6032 self.transact(cx, |this, cx| {
6033 this.select_autoclose_pair(cx);
6034 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6035 let line_mode = s.line_mode;
6036 s.move_with(|map, selection| {
6037 if selection.is_empty() && !line_mode {
6038 let cursor = movement::previous_word_start(map, selection.head());
6039 selection.set_head(cursor, SelectionGoal::None);
6040 }
6041 });
6042 });
6043 this.insert("", cx);
6044 });
6045 }
6046
6047 pub fn delete_to_previous_subword_start(
6048 &mut self,
6049 _: &DeleteToPreviousSubwordStart,
6050 cx: &mut ViewContext<Self>,
6051 ) {
6052 self.transact(cx, |this, cx| {
6053 this.select_autoclose_pair(cx);
6054 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6055 let line_mode = s.line_mode;
6056 s.move_with(|map, selection| {
6057 if selection.is_empty() && !line_mode {
6058 let cursor = movement::previous_subword_start(map, selection.head());
6059 selection.set_head(cursor, SelectionGoal::None);
6060 }
6061 });
6062 });
6063 this.insert("", cx);
6064 });
6065 }
6066
6067 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
6068 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6069 s.move_cursors_with(|map, head, _| {
6070 (movement::next_word_end(map, head), SelectionGoal::None)
6071 });
6072 })
6073 }
6074
6075 pub fn move_to_next_subword_end(
6076 &mut self,
6077 _: &MoveToNextSubwordEnd,
6078 cx: &mut ViewContext<Self>,
6079 ) {
6080 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6081 s.move_cursors_with(|map, head, _| {
6082 (movement::next_subword_end(map, head), SelectionGoal::None)
6083 });
6084 })
6085 }
6086
6087 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
6088 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6089 s.move_heads_with(|map, head, _| {
6090 (movement::next_word_end(map, head), SelectionGoal::None)
6091 });
6092 })
6093 }
6094
6095 pub fn select_to_next_subword_end(
6096 &mut self,
6097 _: &SelectToNextSubwordEnd,
6098 cx: &mut ViewContext<Self>,
6099 ) {
6100 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6101 s.move_heads_with(|map, head, _| {
6102 (movement::next_subword_end(map, head), SelectionGoal::None)
6103 });
6104 })
6105 }
6106
6107 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
6108 self.transact(cx, |this, cx| {
6109 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6110 let line_mode = s.line_mode;
6111 s.move_with(|map, selection| {
6112 if selection.is_empty() && !line_mode {
6113 let cursor = movement::next_word_end(map, selection.head());
6114 selection.set_head(cursor, SelectionGoal::None);
6115 }
6116 });
6117 });
6118 this.insert("", cx);
6119 });
6120 }
6121
6122 pub fn delete_to_next_subword_end(
6123 &mut self,
6124 _: &DeleteToNextSubwordEnd,
6125 cx: &mut ViewContext<Self>,
6126 ) {
6127 self.transact(cx, |this, cx| {
6128 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6129 s.move_with(|map, selection| {
6130 if selection.is_empty() {
6131 let cursor = movement::next_subword_end(map, selection.head());
6132 selection.set_head(cursor, SelectionGoal::None);
6133 }
6134 });
6135 });
6136 this.insert("", cx);
6137 });
6138 }
6139
6140 pub fn move_to_beginning_of_line(
6141 &mut self,
6142 _: &MoveToBeginningOfLine,
6143 cx: &mut ViewContext<Self>,
6144 ) {
6145 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6146 s.move_cursors_with(|map, head, _| {
6147 (
6148 movement::indented_line_beginning(map, head, true),
6149 SelectionGoal::None,
6150 )
6151 });
6152 })
6153 }
6154
6155 pub fn select_to_beginning_of_line(
6156 &mut self,
6157 action: &SelectToBeginningOfLine,
6158 cx: &mut ViewContext<Self>,
6159 ) {
6160 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6161 s.move_heads_with(|map, head, _| {
6162 (
6163 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6164 SelectionGoal::None,
6165 )
6166 });
6167 });
6168 }
6169
6170 pub fn delete_to_beginning_of_line(
6171 &mut self,
6172 _: &DeleteToBeginningOfLine,
6173 cx: &mut ViewContext<Self>,
6174 ) {
6175 self.transact(cx, |this, cx| {
6176 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6177 s.move_with(|_, selection| {
6178 selection.reversed = true;
6179 });
6180 });
6181
6182 this.select_to_beginning_of_line(
6183 &SelectToBeginningOfLine {
6184 stop_at_soft_wraps: false,
6185 },
6186 cx,
6187 );
6188 this.backspace(&Backspace, cx);
6189 });
6190 }
6191
6192 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6193 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6194 s.move_cursors_with(|map, head, _| {
6195 (movement::line_end(map, head, true), SelectionGoal::None)
6196 });
6197 })
6198 }
6199
6200 pub fn select_to_end_of_line(
6201 &mut self,
6202 action: &SelectToEndOfLine,
6203 cx: &mut ViewContext<Self>,
6204 ) {
6205 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6206 s.move_heads_with(|map, head, _| {
6207 (
6208 movement::line_end(map, head, action.stop_at_soft_wraps),
6209 SelectionGoal::None,
6210 )
6211 });
6212 })
6213 }
6214
6215 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6216 self.transact(cx, |this, cx| {
6217 this.select_to_end_of_line(
6218 &SelectToEndOfLine {
6219 stop_at_soft_wraps: false,
6220 },
6221 cx,
6222 );
6223 this.delete(&Delete, cx);
6224 });
6225 }
6226
6227 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6228 self.transact(cx, |this, cx| {
6229 this.select_to_end_of_line(
6230 &SelectToEndOfLine {
6231 stop_at_soft_wraps: false,
6232 },
6233 cx,
6234 );
6235 this.cut(&Cut, cx);
6236 });
6237 }
6238
6239 pub fn move_to_start_of_paragraph(
6240 &mut self,
6241 _: &MoveToStartOfParagraph,
6242 cx: &mut ViewContext<Self>,
6243 ) {
6244 if matches!(self.mode, EditorMode::SingleLine) {
6245 cx.propagate();
6246 return;
6247 }
6248
6249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6250 s.move_with(|map, selection| {
6251 selection.collapse_to(
6252 movement::start_of_paragraph(map, selection.head(), 1),
6253 SelectionGoal::None,
6254 )
6255 });
6256 })
6257 }
6258
6259 pub fn move_to_end_of_paragraph(
6260 &mut self,
6261 _: &MoveToEndOfParagraph,
6262 cx: &mut ViewContext<Self>,
6263 ) {
6264 if matches!(self.mode, EditorMode::SingleLine) {
6265 cx.propagate();
6266 return;
6267 }
6268
6269 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6270 s.move_with(|map, selection| {
6271 selection.collapse_to(
6272 movement::end_of_paragraph(map, selection.head(), 1),
6273 SelectionGoal::None,
6274 )
6275 });
6276 })
6277 }
6278
6279 pub fn select_to_start_of_paragraph(
6280 &mut self,
6281 _: &SelectToStartOfParagraph,
6282 cx: &mut ViewContext<Self>,
6283 ) {
6284 if matches!(self.mode, EditorMode::SingleLine) {
6285 cx.propagate();
6286 return;
6287 }
6288
6289 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6290 s.move_heads_with(|map, head, _| {
6291 (
6292 movement::start_of_paragraph(map, head, 1),
6293 SelectionGoal::None,
6294 )
6295 });
6296 })
6297 }
6298
6299 pub fn select_to_end_of_paragraph(
6300 &mut self,
6301 _: &SelectToEndOfParagraph,
6302 cx: &mut ViewContext<Self>,
6303 ) {
6304 if matches!(self.mode, EditorMode::SingleLine) {
6305 cx.propagate();
6306 return;
6307 }
6308
6309 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6310 s.move_heads_with(|map, head, _| {
6311 (
6312 movement::end_of_paragraph(map, head, 1),
6313 SelectionGoal::None,
6314 )
6315 });
6316 })
6317 }
6318
6319 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6320 if matches!(self.mode, EditorMode::SingleLine) {
6321 cx.propagate();
6322 return;
6323 }
6324
6325 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6326 s.select_ranges(vec![0..0]);
6327 });
6328 }
6329
6330 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6331 let mut selection = self.selections.last::<Point>(cx);
6332 selection.set_head(Point::zero(), SelectionGoal::None);
6333
6334 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6335 s.select(vec![selection]);
6336 });
6337 }
6338
6339 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6340 if matches!(self.mode, EditorMode::SingleLine) {
6341 cx.propagate();
6342 return;
6343 }
6344
6345 let cursor = self.buffer.read(cx).read(cx).len();
6346 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6347 s.select_ranges(vec![cursor..cursor])
6348 });
6349 }
6350
6351 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6352 self.nav_history = nav_history;
6353 }
6354
6355 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6356 self.nav_history.as_ref()
6357 }
6358
6359 fn push_to_nav_history(
6360 &mut self,
6361 cursor_anchor: Anchor,
6362 new_position: Option<Point>,
6363 cx: &mut ViewContext<Self>,
6364 ) {
6365 if let Some(nav_history) = self.nav_history.as_mut() {
6366 let buffer = self.buffer.read(cx).read(cx);
6367 let cursor_position = cursor_anchor.to_point(&buffer);
6368 let scroll_state = self.scroll_manager.anchor();
6369 let scroll_top_row = scroll_state.top_row(&buffer);
6370 drop(buffer);
6371
6372 if let Some(new_position) = new_position {
6373 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6374 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6375 return;
6376 }
6377 }
6378
6379 nav_history.push(
6380 Some(NavigationData {
6381 cursor_anchor,
6382 cursor_position,
6383 scroll_anchor: scroll_state,
6384 scroll_top_row,
6385 }),
6386 cx,
6387 );
6388 }
6389 }
6390
6391 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6392 let buffer = self.buffer.read(cx).snapshot(cx);
6393 let mut selection = self.selections.first::<usize>(cx);
6394 selection.set_head(buffer.len(), SelectionGoal::None);
6395 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6396 s.select(vec![selection]);
6397 });
6398 }
6399
6400 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6401 let end = self.buffer.read(cx).read(cx).len();
6402 self.change_selections(None, cx, |s| {
6403 s.select_ranges(vec![0..end]);
6404 });
6405 }
6406
6407 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6408 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6409 let mut selections = self.selections.all::<Point>(cx);
6410 let max_point = display_map.buffer_snapshot.max_point();
6411 for selection in &mut selections {
6412 let rows = selection.spanned_rows(true, &display_map);
6413 selection.start = Point::new(rows.start, 0);
6414 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
6415 selection.reversed = false;
6416 }
6417 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6418 s.select(selections);
6419 });
6420 }
6421
6422 pub fn split_selection_into_lines(
6423 &mut self,
6424 _: &SplitSelectionIntoLines,
6425 cx: &mut ViewContext<Self>,
6426 ) {
6427 let mut to_unfold = Vec::new();
6428 let mut new_selection_ranges = Vec::new();
6429 {
6430 let selections = self.selections.all::<Point>(cx);
6431 let buffer = self.buffer.read(cx).read(cx);
6432 for selection in selections {
6433 for row in selection.start.row..selection.end.row {
6434 let cursor = Point::new(row, buffer.line_len(row));
6435 new_selection_ranges.push(cursor..cursor);
6436 }
6437 new_selection_ranges.push(selection.end..selection.end);
6438 to_unfold.push(selection.start..selection.end);
6439 }
6440 }
6441 self.unfold_ranges(to_unfold, true, true, cx);
6442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6443 s.select_ranges(new_selection_ranges);
6444 });
6445 }
6446
6447 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6448 self.add_selection(true, cx);
6449 }
6450
6451 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6452 self.add_selection(false, cx);
6453 }
6454
6455 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6456 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6457 let mut selections = self.selections.all::<Point>(cx);
6458 let text_layout_details = self.text_layout_details(cx);
6459 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6460 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6461 let range = oldest_selection.display_range(&display_map).sorted();
6462
6463 let start_x = display_map.x_for_point(range.start, &text_layout_details);
6464 let end_x = display_map.x_for_point(range.end, &text_layout_details);
6465 let positions = start_x.min(end_x)..start_x.max(end_x);
6466
6467 selections.clear();
6468 let mut stack = Vec::new();
6469 for row in range.start.row()..=range.end.row() {
6470 if let Some(selection) = self.selections.build_columnar_selection(
6471 &display_map,
6472 row,
6473 &positions,
6474 oldest_selection.reversed,
6475 &text_layout_details,
6476 ) {
6477 stack.push(selection.id);
6478 selections.push(selection);
6479 }
6480 }
6481
6482 if above {
6483 stack.reverse();
6484 }
6485
6486 AddSelectionsState { above, stack }
6487 });
6488
6489 let last_added_selection = *state.stack.last().unwrap();
6490 let mut new_selections = Vec::new();
6491 if above == state.above {
6492 let end_row = if above {
6493 0
6494 } else {
6495 display_map.max_point().row()
6496 };
6497
6498 'outer: for selection in selections {
6499 if selection.id == last_added_selection {
6500 let range = selection.display_range(&display_map).sorted();
6501 debug_assert_eq!(range.start.row(), range.end.row());
6502 let mut row = range.start.row();
6503 let positions = if let SelectionGoal::HorizontalRange { start, end } =
6504 selection.goal
6505 {
6506 px(start)..px(end)
6507 } else {
6508 let start_x = display_map.x_for_point(range.start, &text_layout_details);
6509 let end_x = display_map.x_for_point(range.end, &text_layout_details);
6510 start_x.min(end_x)..start_x.max(end_x)
6511 };
6512
6513 while row != end_row {
6514 if above {
6515 row -= 1;
6516 } else {
6517 row += 1;
6518 }
6519
6520 if let Some(new_selection) = self.selections.build_columnar_selection(
6521 &display_map,
6522 row,
6523 &positions,
6524 selection.reversed,
6525 &text_layout_details,
6526 ) {
6527 state.stack.push(new_selection.id);
6528 if above {
6529 new_selections.push(new_selection);
6530 new_selections.push(selection);
6531 } else {
6532 new_selections.push(selection);
6533 new_selections.push(new_selection);
6534 }
6535
6536 continue 'outer;
6537 }
6538 }
6539 }
6540
6541 new_selections.push(selection);
6542 }
6543 } else {
6544 new_selections = selections;
6545 new_selections.retain(|s| s.id != last_added_selection);
6546 state.stack.pop();
6547 }
6548
6549 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6550 s.select(new_selections);
6551 });
6552 if state.stack.len() > 1 {
6553 self.add_selections_state = Some(state);
6554 }
6555 }
6556
6557 pub fn select_next_match_internal(
6558 &mut self,
6559 display_map: &DisplaySnapshot,
6560 replace_newest: bool,
6561 autoscroll: Option<Autoscroll>,
6562 cx: &mut ViewContext<Self>,
6563 ) -> Result<()> {
6564 fn select_next_match_ranges(
6565 this: &mut Editor,
6566 range: Range<usize>,
6567 replace_newest: bool,
6568 auto_scroll: Option<Autoscroll>,
6569 cx: &mut ViewContext<Editor>,
6570 ) {
6571 this.unfold_ranges([range.clone()], false, true, cx);
6572 this.change_selections(auto_scroll, cx, |s| {
6573 if replace_newest {
6574 s.delete(s.newest_anchor().id);
6575 }
6576 s.insert_range(range.clone());
6577 });
6578 }
6579
6580 let buffer = &display_map.buffer_snapshot;
6581 let mut selections = self.selections.all::<usize>(cx);
6582 if let Some(mut select_next_state) = self.select_next_state.take() {
6583 let query = &select_next_state.query;
6584 if !select_next_state.done {
6585 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6586 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6587 let mut next_selected_range = None;
6588
6589 let bytes_after_last_selection =
6590 buffer.bytes_in_range(last_selection.end..buffer.len());
6591 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6592 let query_matches = query
6593 .stream_find_iter(bytes_after_last_selection)
6594 .map(|result| (last_selection.end, result))
6595 .chain(
6596 query
6597 .stream_find_iter(bytes_before_first_selection)
6598 .map(|result| (0, result)),
6599 );
6600
6601 for (start_offset, query_match) in query_matches {
6602 let query_match = query_match.unwrap(); // can only fail due to I/O
6603 let offset_range =
6604 start_offset + query_match.start()..start_offset + query_match.end();
6605 let display_range = offset_range.start.to_display_point(&display_map)
6606 ..offset_range.end.to_display_point(&display_map);
6607
6608 if !select_next_state.wordwise
6609 || (!movement::is_inside_word(&display_map, display_range.start)
6610 && !movement::is_inside_word(&display_map, display_range.end))
6611 {
6612 if selections
6613 .iter()
6614 .find(|selection| selection.range().overlaps(&offset_range))
6615 .is_none()
6616 {
6617 next_selected_range = Some(offset_range);
6618 break;
6619 }
6620 }
6621 }
6622
6623 if let Some(next_selected_range) = next_selected_range {
6624 select_next_match_ranges(
6625 self,
6626 next_selected_range,
6627 replace_newest,
6628 autoscroll,
6629 cx,
6630 );
6631 } else {
6632 select_next_state.done = true;
6633 }
6634 }
6635
6636 self.select_next_state = Some(select_next_state);
6637 } else if selections.len() == 1 {
6638 let selection = selections.last_mut().unwrap();
6639 if selection.start == selection.end {
6640 let word_range = movement::surrounding_word(
6641 &display_map,
6642 selection.start.to_display_point(&display_map),
6643 );
6644 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6645 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6646 selection.goal = SelectionGoal::None;
6647 selection.reversed = false;
6648
6649 let query = buffer
6650 .text_for_range(selection.start..selection.end)
6651 .collect::<String>();
6652
6653 let is_empty = query.is_empty();
6654 let select_state = SelectNextState {
6655 query: AhoCorasick::new(&[query])?,
6656 wordwise: true,
6657 done: is_empty,
6658 };
6659 select_next_match_ranges(
6660 self,
6661 selection.start..selection.end,
6662 replace_newest,
6663 autoscroll,
6664 cx,
6665 );
6666 self.select_next_state = Some(select_state);
6667 } else {
6668 let query = buffer
6669 .text_for_range(selection.start..selection.end)
6670 .collect::<String>();
6671 self.select_next_state = Some(SelectNextState {
6672 query: AhoCorasick::new(&[query])?,
6673 wordwise: false,
6674 done: false,
6675 });
6676 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6677 }
6678 }
6679 Ok(())
6680 }
6681
6682 pub fn select_all_matches(
6683 &mut self,
6684 action: &SelectAllMatches,
6685 cx: &mut ViewContext<Self>,
6686 ) -> Result<()> {
6687 self.push_to_selection_history();
6688 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6689
6690 loop {
6691 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6692
6693 if self
6694 .select_next_state
6695 .as_ref()
6696 .map(|selection_state| selection_state.done)
6697 .unwrap_or(true)
6698 {
6699 break;
6700 }
6701 }
6702
6703 Ok(())
6704 }
6705
6706 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6707 self.push_to_selection_history();
6708 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6709 self.select_next_match_internal(
6710 &display_map,
6711 action.replace_newest,
6712 Some(Autoscroll::newest()),
6713 cx,
6714 )?;
6715 Ok(())
6716 }
6717
6718 pub fn select_previous(
6719 &mut self,
6720 action: &SelectPrevious,
6721 cx: &mut ViewContext<Self>,
6722 ) -> Result<()> {
6723 self.push_to_selection_history();
6724 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6725 let buffer = &display_map.buffer_snapshot;
6726 let mut selections = self.selections.all::<usize>(cx);
6727 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6728 let query = &select_prev_state.query;
6729 if !select_prev_state.done {
6730 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6731 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6732 let mut next_selected_range = None;
6733 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6734 let bytes_before_last_selection =
6735 buffer.reversed_bytes_in_range(0..last_selection.start);
6736 let bytes_after_first_selection =
6737 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6738 let query_matches = query
6739 .stream_find_iter(bytes_before_last_selection)
6740 .map(|result| (last_selection.start, result))
6741 .chain(
6742 query
6743 .stream_find_iter(bytes_after_first_selection)
6744 .map(|result| (buffer.len(), result)),
6745 );
6746 for (end_offset, query_match) in query_matches {
6747 let query_match = query_match.unwrap(); // can only fail due to I/O
6748 let offset_range =
6749 end_offset - query_match.end()..end_offset - query_match.start();
6750 let display_range = offset_range.start.to_display_point(&display_map)
6751 ..offset_range.end.to_display_point(&display_map);
6752
6753 if !select_prev_state.wordwise
6754 || (!movement::is_inside_word(&display_map, display_range.start)
6755 && !movement::is_inside_word(&display_map, display_range.end))
6756 {
6757 next_selected_range = Some(offset_range);
6758 break;
6759 }
6760 }
6761
6762 if let Some(next_selected_range) = next_selected_range {
6763 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6764 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6765 if action.replace_newest {
6766 s.delete(s.newest_anchor().id);
6767 }
6768 s.insert_range(next_selected_range);
6769 });
6770 } else {
6771 select_prev_state.done = true;
6772 }
6773 }
6774
6775 self.select_prev_state = Some(select_prev_state);
6776 } else if selections.len() == 1 {
6777 let selection = selections.last_mut().unwrap();
6778 if selection.start == selection.end {
6779 let word_range = movement::surrounding_word(
6780 &display_map,
6781 selection.start.to_display_point(&display_map),
6782 );
6783 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6784 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6785 selection.goal = SelectionGoal::None;
6786 selection.reversed = false;
6787
6788 let query = buffer
6789 .text_for_range(selection.start..selection.end)
6790 .collect::<String>();
6791 let query = query.chars().rev().collect::<String>();
6792 let select_state = SelectNextState {
6793 query: AhoCorasick::new(&[query])?,
6794 wordwise: true,
6795 done: false,
6796 };
6797 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6798 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6799 s.select(selections);
6800 });
6801 self.select_prev_state = Some(select_state);
6802 } else {
6803 let query = buffer
6804 .text_for_range(selection.start..selection.end)
6805 .collect::<String>();
6806 let query = query.chars().rev().collect::<String>();
6807 self.select_prev_state = Some(SelectNextState {
6808 query: AhoCorasick::new(&[query])?,
6809 wordwise: false,
6810 done: false,
6811 });
6812 self.select_previous(action, cx)?;
6813 }
6814 }
6815 Ok(())
6816 }
6817
6818 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6819 let text_layout_details = &self.text_layout_details(cx);
6820 self.transact(cx, |this, cx| {
6821 let mut selections = this.selections.all::<Point>(cx);
6822 let mut edits = Vec::new();
6823 let mut selection_edit_ranges = Vec::new();
6824 let mut last_toggled_row = None;
6825 let snapshot = this.buffer.read(cx).read(cx);
6826 let empty_str: Arc<str> = "".into();
6827 let mut suffixes_inserted = Vec::new();
6828
6829 fn comment_prefix_range(
6830 snapshot: &MultiBufferSnapshot,
6831 row: u32,
6832 comment_prefix: &str,
6833 comment_prefix_whitespace: &str,
6834 ) -> Range<Point> {
6835 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6836
6837 let mut line_bytes = snapshot
6838 .bytes_in_range(start..snapshot.max_point())
6839 .flatten()
6840 .copied();
6841
6842 // If this line currently begins with the line comment prefix, then record
6843 // the range containing the prefix.
6844 if line_bytes
6845 .by_ref()
6846 .take(comment_prefix.len())
6847 .eq(comment_prefix.bytes())
6848 {
6849 // Include any whitespace that matches the comment prefix.
6850 let matching_whitespace_len = line_bytes
6851 .zip(comment_prefix_whitespace.bytes())
6852 .take_while(|(a, b)| a == b)
6853 .count() as u32;
6854 let end = Point::new(
6855 start.row,
6856 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6857 );
6858 start..end
6859 } else {
6860 start..start
6861 }
6862 }
6863
6864 fn comment_suffix_range(
6865 snapshot: &MultiBufferSnapshot,
6866 row: u32,
6867 comment_suffix: &str,
6868 comment_suffix_has_leading_space: bool,
6869 ) -> Range<Point> {
6870 let end = Point::new(row, snapshot.line_len(row));
6871 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6872
6873 let mut line_end_bytes = snapshot
6874 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6875 .flatten()
6876 .copied();
6877
6878 let leading_space_len = if suffix_start_column > 0
6879 && line_end_bytes.next() == Some(b' ')
6880 && comment_suffix_has_leading_space
6881 {
6882 1
6883 } else {
6884 0
6885 };
6886
6887 // If this line currently begins with the line comment prefix, then record
6888 // the range containing the prefix.
6889 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6890 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6891 start..end
6892 } else {
6893 end..end
6894 }
6895 }
6896
6897 // TODO: Handle selections that cross excerpts
6898 for selection in &mut selections {
6899 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6900 let language = if let Some(language) =
6901 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6902 {
6903 language
6904 } else {
6905 continue;
6906 };
6907
6908 selection_edit_ranges.clear();
6909
6910 // If multiple selections contain a given row, avoid processing that
6911 // row more than once.
6912 let mut start_row = selection.start.row;
6913 if last_toggled_row == Some(start_row) {
6914 start_row += 1;
6915 }
6916 let end_row =
6917 if selection.end.row > selection.start.row && selection.end.column == 0 {
6918 selection.end.row - 1
6919 } else {
6920 selection.end.row
6921 };
6922 last_toggled_row = Some(end_row);
6923
6924 if start_row > end_row {
6925 continue;
6926 }
6927
6928 // If the language has line comments, toggle those.
6929 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6930 // Split the comment prefix's trailing whitespace into a separate string,
6931 // as that portion won't be used for detecting if a line is a comment.
6932 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6933 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6934 let mut all_selection_lines_are_comments = true;
6935
6936 for row in start_row..=end_row {
6937 if snapshot.is_line_blank(row) && start_row < end_row {
6938 continue;
6939 }
6940
6941 let prefix_range = comment_prefix_range(
6942 snapshot.deref(),
6943 row,
6944 comment_prefix,
6945 comment_prefix_whitespace,
6946 );
6947 if prefix_range.is_empty() {
6948 all_selection_lines_are_comments = false;
6949 }
6950 selection_edit_ranges.push(prefix_range);
6951 }
6952
6953 if all_selection_lines_are_comments {
6954 edits.extend(
6955 selection_edit_ranges
6956 .iter()
6957 .cloned()
6958 .map(|range| (range, empty_str.clone())),
6959 );
6960 } else {
6961 let min_column = selection_edit_ranges
6962 .iter()
6963 .map(|r| r.start.column)
6964 .min()
6965 .unwrap_or(0);
6966 edits.extend(selection_edit_ranges.iter().map(|range| {
6967 let position = Point::new(range.start.row, min_column);
6968 (position..position, full_comment_prefix.clone())
6969 }));
6970 }
6971 } else if let Some((full_comment_prefix, comment_suffix)) =
6972 language.block_comment_delimiters()
6973 {
6974 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6975 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6976 let prefix_range = comment_prefix_range(
6977 snapshot.deref(),
6978 start_row,
6979 comment_prefix,
6980 comment_prefix_whitespace,
6981 );
6982 let suffix_range = comment_suffix_range(
6983 snapshot.deref(),
6984 end_row,
6985 comment_suffix.trim_start_matches(' '),
6986 comment_suffix.starts_with(' '),
6987 );
6988
6989 if prefix_range.is_empty() || suffix_range.is_empty() {
6990 edits.push((
6991 prefix_range.start..prefix_range.start,
6992 full_comment_prefix.clone(),
6993 ));
6994 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6995 suffixes_inserted.push((end_row, comment_suffix.len()));
6996 } else {
6997 edits.push((prefix_range, empty_str.clone()));
6998 edits.push((suffix_range, empty_str.clone()));
6999 }
7000 } else {
7001 continue;
7002 }
7003 }
7004
7005 drop(snapshot);
7006 this.buffer.update(cx, |buffer, cx| {
7007 buffer.edit(edits, None, cx);
7008 });
7009
7010 // Adjust selections so that they end before any comment suffixes that
7011 // were inserted.
7012 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
7013 let mut selections = this.selections.all::<Point>(cx);
7014 let snapshot = this.buffer.read(cx).read(cx);
7015 for selection in &mut selections {
7016 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
7017 match row.cmp(&selection.end.row) {
7018 Ordering::Less => {
7019 suffixes_inserted.next();
7020 continue;
7021 }
7022 Ordering::Greater => break,
7023 Ordering::Equal => {
7024 if selection.end.column == snapshot.line_len(row) {
7025 if selection.is_empty() {
7026 selection.start.column -= suffix_len as u32;
7027 }
7028 selection.end.column -= suffix_len as u32;
7029 }
7030 break;
7031 }
7032 }
7033 }
7034 }
7035
7036 drop(snapshot);
7037 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7038
7039 let selections = this.selections.all::<Point>(cx);
7040 let selections_on_single_row = selections.windows(2).all(|selections| {
7041 selections[0].start.row == selections[1].start.row
7042 && selections[0].end.row == selections[1].end.row
7043 && selections[0].start.row == selections[0].end.row
7044 });
7045 let selections_selecting = selections
7046 .iter()
7047 .any(|selection| selection.start != selection.end);
7048 let advance_downwards = action.advance_downwards
7049 && selections_on_single_row
7050 && !selections_selecting
7051 && this.mode != EditorMode::SingleLine;
7052
7053 if advance_downwards {
7054 let snapshot = this.buffer.read(cx).snapshot(cx);
7055
7056 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7057 s.move_cursors_with(|display_snapshot, display_point, _| {
7058 let mut point = display_point.to_point(display_snapshot);
7059 point.row += 1;
7060 point = snapshot.clip_point(point, Bias::Left);
7061 let display_point = point.to_display_point(display_snapshot);
7062 let goal = SelectionGoal::HorizontalPosition(
7063 display_snapshot
7064 .x_for_point(display_point, &text_layout_details)
7065 .into(),
7066 );
7067 (display_point, goal)
7068 })
7069 });
7070 }
7071 });
7072 }
7073
7074 pub fn select_larger_syntax_node(
7075 &mut self,
7076 _: &SelectLargerSyntaxNode,
7077 cx: &mut ViewContext<Self>,
7078 ) {
7079 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7080 let buffer = self.buffer.read(cx).snapshot(cx);
7081 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
7082
7083 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7084 let mut selected_larger_node = false;
7085 let new_selections = old_selections
7086 .iter()
7087 .map(|selection| {
7088 let old_range = selection.start..selection.end;
7089 let mut new_range = old_range.clone();
7090 while let Some(containing_range) =
7091 buffer.range_for_syntax_ancestor(new_range.clone())
7092 {
7093 new_range = containing_range;
7094 if !display_map.intersects_fold(new_range.start)
7095 && !display_map.intersects_fold(new_range.end)
7096 {
7097 break;
7098 }
7099 }
7100
7101 selected_larger_node |= new_range != old_range;
7102 Selection {
7103 id: selection.id,
7104 start: new_range.start,
7105 end: new_range.end,
7106 goal: SelectionGoal::None,
7107 reversed: selection.reversed,
7108 }
7109 })
7110 .collect::<Vec<_>>();
7111
7112 if selected_larger_node {
7113 stack.push(old_selections);
7114 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7115 s.select(new_selections);
7116 });
7117 }
7118 self.select_larger_syntax_node_stack = stack;
7119 }
7120
7121 pub fn select_smaller_syntax_node(
7122 &mut self,
7123 _: &SelectSmallerSyntaxNode,
7124 cx: &mut ViewContext<Self>,
7125 ) {
7126 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7127 if let Some(selections) = stack.pop() {
7128 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7129 s.select(selections.to_vec());
7130 });
7131 }
7132 self.select_larger_syntax_node_stack = stack;
7133 }
7134
7135 pub fn move_to_enclosing_bracket(
7136 &mut self,
7137 _: &MoveToEnclosingBracket,
7138 cx: &mut ViewContext<Self>,
7139 ) {
7140 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7141 s.move_offsets_with(|snapshot, selection| {
7142 let Some(enclosing_bracket_ranges) =
7143 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
7144 else {
7145 return;
7146 };
7147
7148 let mut best_length = usize::MAX;
7149 let mut best_inside = false;
7150 let mut best_in_bracket_range = false;
7151 let mut best_destination = None;
7152 for (open, close) in enclosing_bracket_ranges {
7153 let close = close.to_inclusive();
7154 let length = close.end() - open.start;
7155 let inside = selection.start >= open.end && selection.end <= *close.start();
7156 let in_bracket_range = open.to_inclusive().contains(&selection.head())
7157 || close.contains(&selection.head());
7158
7159 // If best is next to a bracket and current isn't, skip
7160 if !in_bracket_range && best_in_bracket_range {
7161 continue;
7162 }
7163
7164 // Prefer smaller lengths unless best is inside and current isn't
7165 if length > best_length && (best_inside || !inside) {
7166 continue;
7167 }
7168
7169 best_length = length;
7170 best_inside = inside;
7171 best_in_bracket_range = in_bracket_range;
7172 best_destination = Some(
7173 if close.contains(&selection.start) && close.contains(&selection.end) {
7174 if inside {
7175 open.end
7176 } else {
7177 open.start
7178 }
7179 } else {
7180 if inside {
7181 *close.start()
7182 } else {
7183 *close.end()
7184 }
7185 },
7186 );
7187 }
7188
7189 if let Some(destination) = best_destination {
7190 selection.collapse_to(destination, SelectionGoal::None);
7191 }
7192 })
7193 });
7194 }
7195
7196 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7197 self.end_selection(cx);
7198 self.selection_history.mode = SelectionHistoryMode::Undoing;
7199 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
7200 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7201 self.select_next_state = entry.select_next_state;
7202 self.select_prev_state = entry.select_prev_state;
7203 self.add_selections_state = entry.add_selections_state;
7204 self.request_autoscroll(Autoscroll::newest(), cx);
7205 }
7206 self.selection_history.mode = SelectionHistoryMode::Normal;
7207 }
7208
7209 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7210 self.end_selection(cx);
7211 self.selection_history.mode = SelectionHistoryMode::Redoing;
7212 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7213 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7214 self.select_next_state = entry.select_next_state;
7215 self.select_prev_state = entry.select_prev_state;
7216 self.add_selections_state = entry.add_selections_state;
7217 self.request_autoscroll(Autoscroll::newest(), cx);
7218 }
7219 self.selection_history.mode = SelectionHistoryMode::Normal;
7220 }
7221
7222 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
7223 self.go_to_diagnostic_impl(Direction::Next, cx)
7224 }
7225
7226 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
7227 self.go_to_diagnostic_impl(Direction::Prev, cx)
7228 }
7229
7230 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
7231 let buffer = self.buffer.read(cx).snapshot(cx);
7232 let selection = self.selections.newest::<usize>(cx);
7233
7234 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
7235 if direction == Direction::Next {
7236 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
7237 let (group_id, jump_to) = popover.activation_info();
7238 if self.activate_diagnostics(group_id, cx) {
7239 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7240 let mut new_selection = s.newest_anchor().clone();
7241 new_selection.collapse_to(jump_to, SelectionGoal::None);
7242 s.select_anchors(vec![new_selection.clone()]);
7243 });
7244 }
7245 return;
7246 }
7247 }
7248
7249 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
7250 active_diagnostics
7251 .primary_range
7252 .to_offset(&buffer)
7253 .to_inclusive()
7254 });
7255 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
7256 if active_primary_range.contains(&selection.head()) {
7257 *active_primary_range.end()
7258 } else {
7259 selection.head()
7260 }
7261 } else {
7262 selection.head()
7263 };
7264
7265 loop {
7266 let mut diagnostics = if direction == Direction::Prev {
7267 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
7268 } else {
7269 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
7270 };
7271 let group = diagnostics.find_map(|entry| {
7272 if entry.diagnostic.is_primary
7273 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
7274 && !entry.range.is_empty()
7275 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
7276 && !entry.range.contains(&search_start)
7277 {
7278 Some((entry.range, entry.diagnostic.group_id))
7279 } else {
7280 None
7281 }
7282 });
7283
7284 if let Some((primary_range, group_id)) = group {
7285 if self.activate_diagnostics(group_id, cx) {
7286 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7287 s.select(vec![Selection {
7288 id: selection.id,
7289 start: primary_range.start,
7290 end: primary_range.start,
7291 reversed: false,
7292 goal: SelectionGoal::None,
7293 }]);
7294 });
7295 }
7296 break;
7297 } else {
7298 // Cycle around to the start of the buffer, potentially moving back to the start of
7299 // the currently active diagnostic.
7300 active_primary_range.take();
7301 if direction == Direction::Prev {
7302 if search_start == buffer.len() {
7303 break;
7304 } else {
7305 search_start = buffer.len();
7306 }
7307 } else if search_start == 0 {
7308 break;
7309 } else {
7310 search_start = 0;
7311 }
7312 }
7313 }
7314 }
7315
7316 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7317 let snapshot = self
7318 .display_map
7319 .update(cx, |display_map, cx| display_map.snapshot(cx));
7320 let selection = self.selections.newest::<Point>(cx);
7321
7322 if !self.seek_in_direction(
7323 &snapshot,
7324 selection.head(),
7325 false,
7326 snapshot
7327 .buffer_snapshot
7328 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7329 cx,
7330 ) {
7331 let wrapped_point = Point::zero();
7332 self.seek_in_direction(
7333 &snapshot,
7334 wrapped_point,
7335 true,
7336 snapshot
7337 .buffer_snapshot
7338 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7339 cx,
7340 );
7341 }
7342 }
7343
7344 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7345 let snapshot = self
7346 .display_map
7347 .update(cx, |display_map, cx| display_map.snapshot(cx));
7348 let selection = self.selections.newest::<Point>(cx);
7349
7350 if !self.seek_in_direction(
7351 &snapshot,
7352 selection.head(),
7353 false,
7354 snapshot
7355 .buffer_snapshot
7356 .git_diff_hunks_in_range_rev(0..selection.head().row),
7357 cx,
7358 ) {
7359 let wrapped_point = snapshot.buffer_snapshot.max_point();
7360 self.seek_in_direction(
7361 &snapshot,
7362 wrapped_point,
7363 true,
7364 snapshot
7365 .buffer_snapshot
7366 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7367 cx,
7368 );
7369 }
7370 }
7371
7372 fn seek_in_direction(
7373 &mut self,
7374 snapshot: &DisplaySnapshot,
7375 initial_point: Point,
7376 is_wrapped: bool,
7377 hunks: impl Iterator<Item = DiffHunk<u32>>,
7378 cx: &mut ViewContext<Editor>,
7379 ) -> bool {
7380 let display_point = initial_point.to_display_point(snapshot);
7381 let mut hunks = hunks
7382 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7383 .filter(|hunk| {
7384 if is_wrapped {
7385 true
7386 } else {
7387 !hunk.contains_display_row(display_point.row())
7388 }
7389 })
7390 .dedup();
7391
7392 if let Some(hunk) = hunks.next() {
7393 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7394 let row = hunk.start_display_row();
7395 let point = DisplayPoint::new(row, 0);
7396 s.select_display_ranges([point..point]);
7397 });
7398
7399 true
7400 } else {
7401 false
7402 }
7403 }
7404
7405 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7406 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7407 }
7408
7409 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7410 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7411 }
7412
7413 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7414 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7415 }
7416
7417 pub fn go_to_type_definition_split(
7418 &mut self,
7419 _: &GoToTypeDefinitionSplit,
7420 cx: &mut ViewContext<Self>,
7421 ) {
7422 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7423 }
7424
7425 fn go_to_definition_of_kind(
7426 &mut self,
7427 kind: GotoDefinitionKind,
7428 split: bool,
7429 cx: &mut ViewContext<Self>,
7430 ) {
7431 let Some(workspace) = self.workspace() else {
7432 return;
7433 };
7434 let buffer = self.buffer.read(cx);
7435 let head = self.selections.newest::<usize>(cx).head();
7436 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7437 text_anchor
7438 } else {
7439 return;
7440 };
7441
7442 let project = workspace.read(cx).project().clone();
7443 let definitions = project.update(cx, |project, cx| match kind {
7444 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7445 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7446 });
7447
7448 cx.spawn(|editor, mut cx| async move {
7449 let definitions = definitions.await?;
7450 editor.update(&mut cx, |editor, cx| {
7451 editor.navigate_to_definitions(
7452 definitions
7453 .into_iter()
7454 .map(GoToDefinitionLink::Text)
7455 .collect(),
7456 split,
7457 cx,
7458 );
7459 })?;
7460 Ok::<(), anyhow::Error>(())
7461 })
7462 .detach_and_log_err(cx);
7463 }
7464
7465 pub fn navigate_to_definitions(
7466 &mut self,
7467 mut definitions: Vec<GoToDefinitionLink>,
7468 split: bool,
7469 cx: &mut ViewContext<Editor>,
7470 ) {
7471 let Some(workspace) = self.workspace() else {
7472 return;
7473 };
7474 let pane = workspace.read(cx).active_pane().clone();
7475 // If there is one definition, just open it directly
7476 if definitions.len() == 1 {
7477 let definition = definitions.pop().unwrap();
7478 let target_task = match definition {
7479 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7480 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7481 self.compute_target_location(lsp_location, server_id, cx)
7482 }
7483 };
7484 cx.spawn(|editor, mut cx| async move {
7485 let target = target_task.await.context("target resolution task")?;
7486 if let Some(target) = target {
7487 editor.update(&mut cx, |editor, cx| {
7488 let range = target.range.to_offset(target.buffer.read(cx));
7489 let range = editor.range_for_match(&range);
7490 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7491 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7492 s.select_ranges([range]);
7493 });
7494 } else {
7495 cx.window_context().defer(move |cx| {
7496 let target_editor: View<Self> =
7497 workspace.update(cx, |workspace, cx| {
7498 if split {
7499 workspace.split_project_item(target.buffer.clone(), cx)
7500 } else {
7501 workspace.open_project_item(target.buffer.clone(), cx)
7502 }
7503 });
7504 target_editor.update(cx, |target_editor, cx| {
7505 // When selecting a definition in a different buffer, disable the nav history
7506 // to avoid creating a history entry at the previous cursor location.
7507 pane.update(cx, |pane, _| pane.disable_history());
7508 target_editor.change_selections(
7509 Some(Autoscroll::fit()),
7510 cx,
7511 |s| {
7512 s.select_ranges([range]);
7513 },
7514 );
7515 pane.update(cx, |pane, _| pane.enable_history());
7516 });
7517 });
7518 }
7519 })
7520 } else {
7521 Ok(())
7522 }
7523 })
7524 .detach_and_log_err(cx);
7525 } else if !definitions.is_empty() {
7526 let replica_id = self.replica_id(cx);
7527 cx.spawn(|editor, mut cx| async move {
7528 let (title, location_tasks) = editor
7529 .update(&mut cx, |editor, cx| {
7530 let title = definitions
7531 .iter()
7532 .find_map(|definition| match definition {
7533 GoToDefinitionLink::Text(link) => {
7534 link.origin.as_ref().map(|origin| {
7535 let buffer = origin.buffer.read(cx);
7536 format!(
7537 "Definitions for {}",
7538 buffer
7539 .text_for_range(origin.range.clone())
7540 .collect::<String>()
7541 )
7542 })
7543 }
7544 GoToDefinitionLink::InlayHint(_, _) => None,
7545 })
7546 .unwrap_or("Definitions".to_string());
7547 let location_tasks = definitions
7548 .into_iter()
7549 .map(|definition| match definition {
7550 GoToDefinitionLink::Text(link) => {
7551 Task::Ready(Some(Ok(Some(link.target))))
7552 }
7553 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7554 editor.compute_target_location(lsp_location, server_id, cx)
7555 }
7556 })
7557 .collect::<Vec<_>>();
7558 (title, location_tasks)
7559 })
7560 .context("location tasks preparation")?;
7561
7562 let locations = futures::future::join_all(location_tasks)
7563 .await
7564 .into_iter()
7565 .filter_map(|location| location.transpose())
7566 .collect::<Result<_>>()
7567 .context("location tasks")?;
7568 workspace.update(&mut cx, |workspace, cx| {
7569 Self::open_locations_in_multibuffer(
7570 workspace, locations, replica_id, title, split, cx,
7571 )
7572 });
7573
7574 anyhow::Ok(())
7575 })
7576 .detach_and_log_err(cx);
7577 }
7578 }
7579
7580 fn compute_target_location(
7581 &self,
7582 lsp_location: lsp::Location,
7583 server_id: LanguageServerId,
7584 cx: &mut ViewContext<Editor>,
7585 ) -> Task<anyhow::Result<Option<Location>>> {
7586 let Some(project) = self.project.clone() else {
7587 return Task::Ready(Some(Ok(None)));
7588 };
7589
7590 cx.spawn(move |editor, mut cx| async move {
7591 let location_task = editor.update(&mut cx, |editor, cx| {
7592 project.update(cx, |project, cx| {
7593 let language_server_name =
7594 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7595 project
7596 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7597 .map(|(_, lsp_adapter)| {
7598 LanguageServerName(Arc::from(lsp_adapter.name()))
7599 })
7600 });
7601 language_server_name.map(|language_server_name| {
7602 project.open_local_buffer_via_lsp(
7603 lsp_location.uri.clone(),
7604 server_id,
7605 language_server_name,
7606 cx,
7607 )
7608 })
7609 })
7610 })?;
7611 let location = match location_task {
7612 Some(task) => Some({
7613 let target_buffer_handle = task.await.context("open local buffer")?;
7614 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
7615 let target_start = target_buffer
7616 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
7617 let target_end = target_buffer
7618 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
7619 target_buffer.anchor_after(target_start)
7620 ..target_buffer.anchor_before(target_end)
7621 })?;
7622 Location {
7623 buffer: target_buffer_handle,
7624 range,
7625 }
7626 }),
7627 None => None,
7628 };
7629 Ok(location)
7630 })
7631 }
7632
7633 // pub fn find_all_references(
7634 // workspace: &mut Workspace,
7635 // _: &FindAllReferences,
7636 // cx: &mut ViewContext<Workspace>,
7637 // ) -> Option<Task<Result<()>>> {
7638 // let active_item = workspace.active_item(cx)?;
7639 // let editor_handle = active_item.act_as::<Self>(cx)?;
7640
7641 // let editor = editor_handle.read(cx);
7642 // let buffer = editor.buffer.read(cx);
7643 // let head = editor.selections.newest::<usize>(cx).head();
7644 // let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7645 // let replica_id = editor.replica_id(cx);
7646
7647 // let project = workspace.project().clone();
7648 // let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7649 // Some(cx.spawn_labeled(
7650 // "Finding All References...",
7651 // |workspace, mut cx| async move {
7652 // let locations = references.await?;
7653 // if locations.is_empty() {
7654 // return Ok(());
7655 // }
7656
7657 // workspace.update(&mut cx, |workspace, cx| {
7658 // let title = locations
7659 // .first()
7660 // .as_ref()
7661 // .map(|location| {
7662 // let buffer = location.buffer.read(cx);
7663 // format!(
7664 // "References to `{}`",
7665 // buffer
7666 // .text_for_range(location.range.clone())
7667 // .collect::<String>()
7668 // )
7669 // })
7670 // .unwrap();
7671 // Self::open_locations_in_multibuffer(
7672 // workspace, locations, replica_id, title, false, cx,
7673 // );
7674 // })?;
7675
7676 // Ok(())
7677 // },
7678 // ))
7679 // }
7680
7681 /// Opens a multibuffer with the given project locations in it
7682 pub fn open_locations_in_multibuffer(
7683 workspace: &mut Workspace,
7684 mut locations: Vec<Location>,
7685 replica_id: ReplicaId,
7686 title: String,
7687 split: bool,
7688 cx: &mut ViewContext<Workspace>,
7689 ) {
7690 // If there are multiple definitions, open them in a multibuffer
7691 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7692 let mut locations = locations.into_iter().peekable();
7693 let mut ranges_to_highlight = Vec::new();
7694
7695 let excerpt_buffer = cx.build_model(|cx| {
7696 let mut multibuffer = MultiBuffer::new(replica_id);
7697 while let Some(location) = locations.next() {
7698 let buffer = location.buffer.read(cx);
7699 let mut ranges_for_buffer = Vec::new();
7700 let range = location.range.to_offset(buffer);
7701 ranges_for_buffer.push(range.clone());
7702
7703 while let Some(next_location) = locations.peek() {
7704 if next_location.buffer == location.buffer {
7705 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7706 locations.next();
7707 } else {
7708 break;
7709 }
7710 }
7711
7712 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7713 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7714 location.buffer.clone(),
7715 ranges_for_buffer,
7716 1,
7717 cx,
7718 ))
7719 }
7720
7721 multibuffer.with_title(title)
7722 });
7723
7724 let editor = cx.build_view(|cx| {
7725 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7726 });
7727 editor.update(cx, |editor, cx| {
7728 editor.highlight_background::<Self>(
7729 ranges_to_highlight,
7730 |theme| todo!("theme.editor.highlighted_line_background"),
7731 cx,
7732 );
7733 });
7734 if split {
7735 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7736 } else {
7737 workspace.add_item(Box::new(editor), cx);
7738 }
7739 }
7740
7741 // pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7742 // use language::ToOffset as _;
7743
7744 // let project = self.project.clone()?;
7745 // let selection = self.selections.newest_anchor().clone();
7746 // let (cursor_buffer, cursor_buffer_position) = self
7747 // .buffer
7748 // .read(cx)
7749 // .text_anchor_for_position(selection.head(), cx)?;
7750 // let (tail_buffer, _) = self
7751 // .buffer
7752 // .read(cx)
7753 // .text_anchor_for_position(selection.tail(), cx)?;
7754 // if tail_buffer != cursor_buffer {
7755 // return None;
7756 // }
7757
7758 // let snapshot = cursor_buffer.read(cx).snapshot();
7759 // let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7760 // let prepare_rename = project.update(cx, |project, cx| {
7761 // project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7762 // });
7763
7764 // Some(cx.spawn(|this, mut cx| async move {
7765 // let rename_range = if let Some(range) = prepare_rename.await? {
7766 // Some(range)
7767 // } else {
7768 // this.update(&mut cx, |this, cx| {
7769 // let buffer = this.buffer.read(cx).snapshot(cx);
7770 // let mut buffer_highlights = this
7771 // .document_highlights_for_position(selection.head(), &buffer)
7772 // .filter(|highlight| {
7773 // highlight.start.excerpt_id == selection.head().excerpt_id
7774 // && highlight.end.excerpt_id == selection.head().excerpt_id
7775 // });
7776 // buffer_highlights
7777 // .next()
7778 // .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7779 // })?
7780 // };
7781 // if let Some(rename_range) = rename_range {
7782 // let rename_buffer_range = rename_range.to_offset(&snapshot);
7783 // let cursor_offset_in_rename_range =
7784 // cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7785
7786 // this.update(&mut cx, |this, cx| {
7787 // this.take_rename(false, cx);
7788 // let buffer = this.buffer.read(cx).read(cx);
7789 // let cursor_offset = selection.head().to_offset(&buffer);
7790 // let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7791 // let rename_end = rename_start + rename_buffer_range.len();
7792 // let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7793 // let mut old_highlight_id = None;
7794 // let old_name: Arc<str> = buffer
7795 // .chunks(rename_start..rename_end, true)
7796 // .map(|chunk| {
7797 // if old_highlight_id.is_none() {
7798 // old_highlight_id = chunk.syntax_highlight_id;
7799 // }
7800 // chunk.text
7801 // })
7802 // .collect::<String>()
7803 // .into();
7804
7805 // drop(buffer);
7806
7807 // // Position the selection in the rename editor so that it matches the current selection.
7808 // this.show_local_selections = false;
7809 // let rename_editor = cx.build_view(|cx| {
7810 // let mut editor = Editor::single_line(cx);
7811 // if let Some(old_highlight_id) = old_highlight_id {
7812 // editor.override_text_style =
7813 // Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7814 // }
7815 // editor.buffer.update(cx, |buffer, cx| {
7816 // buffer.edit([(0..0, old_name.clone())], None, cx)
7817 // });
7818 // editor.select_all(&SelectAll, cx);
7819 // editor
7820 // });
7821
7822 // let ranges = this
7823 // .clear_background_highlights::<DocumentHighlightWrite>(cx)
7824 // .into_iter()
7825 // .flat_map(|(_, ranges)| ranges.into_iter())
7826 // .chain(
7827 // this.clear_background_highlights::<DocumentHighlightRead>(cx)
7828 // .into_iter()
7829 // .flat_map(|(_, ranges)| ranges.into_iter()),
7830 // )
7831 // .collect();
7832
7833 // this.highlight_text::<Rename>(
7834 // ranges,
7835 // HighlightStyle {
7836 // fade_out: Some(style.rename_fade),
7837 // ..Default::default()
7838 // },
7839 // cx,
7840 // );
7841 // cx.focus(&rename_editor);
7842 // let block_id = this.insert_blocks(
7843 // [BlockProperties {
7844 // style: BlockStyle::Flex,
7845 // position: range.start.clone(),
7846 // height: 1,
7847 // render: Arc::new({
7848 // let editor = rename_editor.clone();
7849 // move |cx: &mut BlockContext| {
7850 // ChildView::new(&editor, cx)
7851 // .contained()
7852 // .with_padding_left(cx.anchor_x)
7853 // .into_any()
7854 // }
7855 // }),
7856 // disposition: BlockDisposition::Below,
7857 // }],
7858 // Some(Autoscroll::fit()),
7859 // cx,
7860 // )[0];
7861 // this.pending_rename = Some(RenameState {
7862 // range,
7863 // old_name,
7864 // editor: rename_editor,
7865 // block_id,
7866 // });
7867 // })?;
7868 // }
7869
7870 // Ok(())
7871 // }))
7872 // }
7873
7874 // pub fn confirm_rename(
7875 // workspace: &mut Workspace,
7876 // _: &ConfirmRename,
7877 // cx: &mut ViewContext<Workspace>,
7878 // ) -> Option<Task<Result<()>>> {
7879 // let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7880
7881 // let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7882 // let rename = editor.take_rename(false, cx)?;
7883 // let buffer = editor.buffer.read(cx);
7884 // let (start_buffer, start) =
7885 // buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7886 // let (end_buffer, end) =
7887 // buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7888 // if start_buffer == end_buffer {
7889 // let new_name = rename.editor.read(cx).text(cx);
7890 // Some((start_buffer, start..end, rename.old_name, new_name))
7891 // } else {
7892 // None
7893 // }
7894 // })?;
7895
7896 // let rename = workspace.project().clone().update(cx, |project, cx| {
7897 // project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7898 // });
7899
7900 // let editor = editor.downgrade();
7901 // Some(cx.spawn(|workspace, mut cx| async move {
7902 // let project_transaction = rename.await?;
7903 // Self::open_project_transaction(
7904 // &editor,
7905 // workspace,
7906 // project_transaction,
7907 // format!("Rename: {} → {}", old_name, new_name),
7908 // cx.clone(),
7909 // )
7910 // .await?;
7911
7912 // editor.update(&mut cx, |editor, cx| {
7913 // editor.refresh_document_highlights(cx);
7914 // })?;
7915 // Ok(())
7916 // }))
7917 // }
7918
7919 fn take_rename(
7920 &mut self,
7921 moving_cursor: bool,
7922 cx: &mut ViewContext<Self>,
7923 ) -> Option<RenameState> {
7924 let rename = self.pending_rename.take()?;
7925 self.remove_blocks(
7926 [rename.block_id].into_iter().collect(),
7927 Some(Autoscroll::fit()),
7928 cx,
7929 );
7930 self.clear_highlights::<Rename>(cx);
7931 self.show_local_selections = true;
7932
7933 if moving_cursor {
7934 let rename_editor = rename.editor.read(cx);
7935 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7936
7937 // Update the selection to match the position of the selection inside
7938 // the rename editor.
7939 let snapshot = self.buffer.read(cx).read(cx);
7940 let rename_range = rename.range.to_offset(&snapshot);
7941 let cursor_in_editor = snapshot
7942 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7943 .min(rename_range.end);
7944 drop(snapshot);
7945
7946 self.change_selections(None, cx, |s| {
7947 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7948 });
7949 } else {
7950 self.refresh_document_highlights(cx);
7951 }
7952
7953 Some(rename)
7954 }
7955
7956 #[cfg(any(test, feature = "test-support"))]
7957 pub fn pending_rename(&self) -> Option<&RenameState> {
7958 self.pending_rename.as_ref()
7959 }
7960
7961 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7962 let project = match &self.project {
7963 Some(project) => project.clone(),
7964 None => return None,
7965 };
7966
7967 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7968 }
7969
7970 fn perform_format(
7971 &mut self,
7972 project: Model<Project>,
7973 trigger: FormatTrigger,
7974 cx: &mut ViewContext<Self>,
7975 ) -> Task<Result<()>> {
7976 let buffer = self.buffer().clone();
7977 let buffers = buffer.read(cx).all_buffers();
7978
7979 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
7980 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7981
7982 cx.spawn(|_, mut cx| async move {
7983 let transaction = futures::select_biased! {
7984 _ = timeout => {
7985 log::warn!("timed out waiting for formatting");
7986 None
7987 }
7988 transaction = format.log_err().fuse() => transaction,
7989 };
7990
7991 buffer.update(&mut cx, |buffer, cx| {
7992 if let Some(transaction) = transaction {
7993 if !buffer.is_singleton() {
7994 buffer.push_transaction(&transaction.0, cx);
7995 }
7996 }
7997
7998 cx.notify();
7999 });
8000
8001 Ok(())
8002 })
8003 }
8004
8005 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
8006 if let Some(project) = self.project.clone() {
8007 self.buffer.update(cx, |multi_buffer, cx| {
8008 project.update(cx, |project, cx| {
8009 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
8010 });
8011 })
8012 }
8013 }
8014
8015 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
8016 cx.show_character_palette();
8017 }
8018
8019 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
8020 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
8021 let buffer = self.buffer.read(cx).snapshot(cx);
8022 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
8023 let is_valid = buffer
8024 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
8025 .any(|entry| {
8026 entry.diagnostic.is_primary
8027 && !entry.range.is_empty()
8028 && entry.range.start == primary_range_start
8029 && entry.diagnostic.message == active_diagnostics.primary_message
8030 });
8031
8032 if is_valid != active_diagnostics.is_valid {
8033 active_diagnostics.is_valid = is_valid;
8034 let mut new_styles = HashMap::default();
8035 for (block_id, diagnostic) in &active_diagnostics.blocks {
8036 new_styles.insert(
8037 *block_id,
8038 diagnostic_block_renderer(diagnostic.clone(), is_valid),
8039 );
8040 }
8041 self.display_map
8042 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
8043 }
8044 }
8045 }
8046
8047 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
8048 self.dismiss_diagnostics(cx);
8049 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
8050 let buffer = self.buffer.read(cx).snapshot(cx);
8051
8052 let mut primary_range = None;
8053 let mut primary_message = None;
8054 let mut group_end = Point::zero();
8055 let diagnostic_group = buffer
8056 .diagnostic_group::<Point>(group_id)
8057 .map(|entry| {
8058 if entry.range.end > group_end {
8059 group_end = entry.range.end;
8060 }
8061 if entry.diagnostic.is_primary {
8062 primary_range = Some(entry.range.clone());
8063 primary_message = Some(entry.diagnostic.message.clone());
8064 }
8065 entry
8066 })
8067 .collect::<Vec<_>>();
8068 let primary_range = primary_range?;
8069 let primary_message = primary_message?;
8070 let primary_range =
8071 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8072
8073 let blocks = display_map
8074 .insert_blocks(
8075 diagnostic_group.iter().map(|entry| {
8076 let diagnostic = entry.diagnostic.clone();
8077 let message_height = diagnostic.message.lines().count() as u8;
8078 BlockProperties {
8079 style: BlockStyle::Fixed,
8080 position: buffer.anchor_after(entry.range.start),
8081 height: message_height,
8082 render: diagnostic_block_renderer(diagnostic, true),
8083 disposition: BlockDisposition::Below,
8084 }
8085 }),
8086 cx,
8087 )
8088 .into_iter()
8089 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8090 .collect();
8091
8092 Some(ActiveDiagnosticGroup {
8093 primary_range,
8094 primary_message,
8095 blocks,
8096 is_valid: true,
8097 })
8098 });
8099 self.active_diagnostics.is_some()
8100 }
8101
8102 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8103 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8104 self.display_map.update(cx, |display_map, cx| {
8105 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8106 });
8107 cx.notify();
8108 }
8109 }
8110
8111 pub fn set_selections_from_remote(
8112 &mut self,
8113 selections: Vec<Selection<Anchor>>,
8114 pending_selection: Option<Selection<Anchor>>,
8115 cx: &mut ViewContext<Self>,
8116 ) {
8117 let old_cursor_position = self.selections.newest_anchor().head();
8118 self.selections.change_with(cx, |s| {
8119 s.select_anchors(selections);
8120 if let Some(pending_selection) = pending_selection {
8121 s.set_pending(pending_selection, SelectMode::Character);
8122 } else {
8123 s.clear_pending();
8124 }
8125 });
8126 self.selections_did_change(false, &old_cursor_position, cx);
8127 }
8128
8129 fn push_to_selection_history(&mut self) {
8130 self.selection_history.push(SelectionHistoryEntry {
8131 selections: self.selections.disjoint_anchors(),
8132 select_next_state: self.select_next_state.clone(),
8133 select_prev_state: self.select_prev_state.clone(),
8134 add_selections_state: self.add_selections_state.clone(),
8135 });
8136 }
8137
8138 pub fn transact(
8139 &mut self,
8140 cx: &mut ViewContext<Self>,
8141 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8142 ) -> Option<TransactionId> {
8143 self.start_transaction_at(Instant::now(), cx);
8144 update(self, cx);
8145 self.end_transaction_at(Instant::now(), cx)
8146 }
8147
8148 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8149 self.end_selection(cx);
8150 if let Some(tx_id) = self
8151 .buffer
8152 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8153 {
8154 self.selection_history
8155 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8156 }
8157 }
8158
8159 fn end_transaction_at(
8160 &mut self,
8161 now: Instant,
8162 cx: &mut ViewContext<Self>,
8163 ) -> Option<TransactionId> {
8164 if let Some(tx_id) = self
8165 .buffer
8166 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8167 {
8168 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8169 *end_selections = Some(self.selections.disjoint_anchors());
8170 } else {
8171 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8172 }
8173
8174 cx.emit(Event::Edited);
8175 Some(tx_id)
8176 } else {
8177 None
8178 }
8179 }
8180
8181 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8182 let mut fold_ranges = Vec::new();
8183
8184 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8185
8186 let selections = self.selections.all_adjusted(cx);
8187 for selection in selections {
8188 let range = selection.range().sorted();
8189 let buffer_start_row = range.start.row;
8190
8191 for row in (0..=range.end.row).rev() {
8192 let fold_range = display_map.foldable_range(row);
8193
8194 if let Some(fold_range) = fold_range {
8195 if fold_range.end.row >= buffer_start_row {
8196 fold_ranges.push(fold_range);
8197 if row <= range.start.row {
8198 break;
8199 }
8200 }
8201 }
8202 }
8203 }
8204
8205 self.fold_ranges(fold_ranges, true, cx);
8206 }
8207
8208 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8209 let buffer_row = fold_at.buffer_row;
8210 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8211
8212 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8213 let autoscroll = self
8214 .selections
8215 .all::<Point>(cx)
8216 .iter()
8217 .any(|selection| fold_range.overlaps(&selection.range()));
8218
8219 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8220 }
8221 }
8222
8223 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8224 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8225 let buffer = &display_map.buffer_snapshot;
8226 let selections = self.selections.all::<Point>(cx);
8227 let ranges = selections
8228 .iter()
8229 .map(|s| {
8230 let range = s.display_range(&display_map).sorted();
8231 let mut start = range.start.to_point(&display_map);
8232 let mut end = range.end.to_point(&display_map);
8233 start.column = 0;
8234 end.column = buffer.line_len(end.row);
8235 start..end
8236 })
8237 .collect::<Vec<_>>();
8238
8239 self.unfold_ranges(ranges, true, true, cx);
8240 }
8241
8242 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8243 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8244
8245 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8246 ..Point::new(
8247 unfold_at.buffer_row,
8248 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8249 );
8250
8251 let autoscroll = self
8252 .selections
8253 .all::<Point>(cx)
8254 .iter()
8255 .any(|selection| selection.range().overlaps(&intersection_range));
8256
8257 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8258 }
8259
8260 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8261 let selections = self.selections.all::<Point>(cx);
8262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8263 let line_mode = self.selections.line_mode;
8264 let ranges = selections.into_iter().map(|s| {
8265 if line_mode {
8266 let start = Point::new(s.start.row, 0);
8267 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8268 start..end
8269 } else {
8270 s.start..s.end
8271 }
8272 });
8273 self.fold_ranges(ranges, true, cx);
8274 }
8275
8276 pub fn fold_ranges<T: ToOffset + Clone>(
8277 &mut self,
8278 ranges: impl IntoIterator<Item = Range<T>>,
8279 auto_scroll: bool,
8280 cx: &mut ViewContext<Self>,
8281 ) {
8282 let mut ranges = ranges.into_iter().peekable();
8283 if ranges.peek().is_some() {
8284 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8285
8286 if auto_scroll {
8287 self.request_autoscroll(Autoscroll::fit(), cx);
8288 }
8289
8290 cx.notify();
8291 }
8292 }
8293
8294 pub fn unfold_ranges<T: ToOffset + Clone>(
8295 &mut self,
8296 ranges: impl IntoIterator<Item = Range<T>>,
8297 inclusive: bool,
8298 auto_scroll: bool,
8299 cx: &mut ViewContext<Self>,
8300 ) {
8301 let mut ranges = ranges.into_iter().peekable();
8302 if ranges.peek().is_some() {
8303 self.display_map
8304 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8305 if auto_scroll {
8306 self.request_autoscroll(Autoscroll::fit(), cx);
8307 }
8308
8309 cx.notify();
8310 }
8311 }
8312
8313 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
8314 if hovered != self.gutter_hovered {
8315 self.gutter_hovered = hovered;
8316 cx.notify();
8317 }
8318 }
8319
8320 pub fn insert_blocks(
8321 &mut self,
8322 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8323 autoscroll: Option<Autoscroll>,
8324 cx: &mut ViewContext<Self>,
8325 ) -> Vec<BlockId> {
8326 let blocks = self
8327 .display_map
8328 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8329 if let Some(autoscroll) = autoscroll {
8330 self.request_autoscroll(autoscroll, cx);
8331 }
8332 blocks
8333 }
8334
8335 pub fn replace_blocks(
8336 &mut self,
8337 blocks: HashMap<BlockId, RenderBlock>,
8338 autoscroll: Option<Autoscroll>,
8339 cx: &mut ViewContext<Self>,
8340 ) {
8341 self.display_map
8342 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8343 if let Some(autoscroll) = autoscroll {
8344 self.request_autoscroll(autoscroll, cx);
8345 }
8346 }
8347
8348 pub fn remove_blocks(
8349 &mut self,
8350 block_ids: HashSet<BlockId>,
8351 autoscroll: Option<Autoscroll>,
8352 cx: &mut ViewContext<Self>,
8353 ) {
8354 self.display_map.update(cx, |display_map, cx| {
8355 display_map.remove_blocks(block_ids, cx)
8356 });
8357 if let Some(autoscroll) = autoscroll {
8358 self.request_autoscroll(autoscroll, cx);
8359 }
8360 }
8361
8362 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8363 self.display_map
8364 .update(cx, |map, cx| map.snapshot(cx))
8365 .longest_row()
8366 }
8367
8368 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8369 self.display_map
8370 .update(cx, |map, cx| map.snapshot(cx))
8371 .max_point()
8372 }
8373
8374 pub fn text(&self, cx: &AppContext) -> String {
8375 self.buffer.read(cx).read(cx).text()
8376 }
8377
8378 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8379 self.transact(cx, |this, cx| {
8380 this.buffer
8381 .read(cx)
8382 .as_singleton()
8383 .expect("you can only call set_text on editors for singleton buffers")
8384 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8385 });
8386 }
8387
8388 pub fn display_text(&self, cx: &mut AppContext) -> String {
8389 self.display_map
8390 .update(cx, |map, cx| map.snapshot(cx))
8391 .text()
8392 }
8393
8394 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8395 let mut wrap_guides = smallvec::smallvec![];
8396
8397 if self.show_wrap_guides == Some(false) {
8398 return wrap_guides;
8399 }
8400
8401 let settings = self.buffer.read(cx).settings_at(0, cx);
8402 if settings.show_wrap_guides {
8403 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8404 wrap_guides.push((soft_wrap as usize, true));
8405 }
8406 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8407 }
8408
8409 wrap_guides
8410 }
8411
8412 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8413 let settings = self.buffer.read(cx).settings_at(0, cx);
8414 let mode = self
8415 .soft_wrap_mode_override
8416 .unwrap_or_else(|| settings.soft_wrap);
8417 match mode {
8418 language_settings::SoftWrap::None => SoftWrap::None,
8419 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8420 language_settings::SoftWrap::PreferredLineLength => {
8421 SoftWrap::Column(settings.preferred_line_length)
8422 }
8423 }
8424 }
8425
8426 pub fn set_soft_wrap_mode(
8427 &mut self,
8428 mode: language_settings::SoftWrap,
8429 cx: &mut ViewContext<Self>,
8430 ) {
8431 self.soft_wrap_mode_override = Some(mode);
8432 cx.notify();
8433 }
8434
8435 pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8436 self.display_map
8437 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8438 }
8439
8440 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8441 if self.soft_wrap_mode_override.is_some() {
8442 self.soft_wrap_mode_override.take();
8443 } else {
8444 let soft_wrap = match self.soft_wrap_mode(cx) {
8445 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8446 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8447 };
8448 self.soft_wrap_mode_override = Some(soft_wrap);
8449 }
8450 cx.notify();
8451 }
8452
8453 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8454 self.show_gutter = show_gutter;
8455 cx.notify();
8456 }
8457
8458 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8459 self.show_wrap_guides = Some(show_gutter);
8460 cx.notify();
8461 }
8462
8463 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8464 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8465 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8466 cx.reveal_path(&file.abs_path(cx));
8467 }
8468 }
8469 }
8470
8471 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8472 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8473 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8474 if let Some(path) = file.abs_path(cx).to_str() {
8475 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8476 }
8477 }
8478 }
8479 }
8480
8481 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8482 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8483 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8484 if let Some(path) = file.path().to_str() {
8485 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8486 }
8487 }
8488 }
8489 }
8490
8491 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8492 self.highlighted_rows = rows;
8493 }
8494
8495 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8496 self.highlighted_rows.clone()
8497 }
8498
8499 pub fn highlight_background<T: 'static>(
8500 &mut self,
8501 ranges: Vec<Range<Anchor>>,
8502 color_fetcher: fn(&ThemeColors) -> Hsla,
8503 cx: &mut ViewContext<Self>,
8504 ) {
8505 self.background_highlights
8506 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8507 cx.notify();
8508 }
8509
8510 pub fn highlight_inlay_background<T: 'static>(
8511 &mut self,
8512 ranges: Vec<InlayHighlight>,
8513 color_fetcher: fn(&ThemeColors) -> Hsla,
8514 cx: &mut ViewContext<Self>,
8515 ) {
8516 // TODO: no actual highlights happen for inlays currently, find a way to do that
8517 self.inlay_background_highlights
8518 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8519 cx.notify();
8520 }
8521
8522 pub fn clear_background_highlights<T: 'static>(
8523 &mut self,
8524 cx: &mut ViewContext<Self>,
8525 ) -> Option<BackgroundHighlight> {
8526 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8527 let inlay_highlights = self
8528 .inlay_background_highlights
8529 .remove(&Some(TypeId::of::<T>()));
8530 if text_highlights.is_some() || inlay_highlights.is_some() {
8531 cx.notify();
8532 }
8533 text_highlights
8534 }
8535
8536 #[cfg(feature = "test-support")]
8537 pub fn all_text_background_highlights(
8538 &mut self,
8539 cx: &mut ViewContext<Self>,
8540 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8541 let snapshot = self.snapshot(cx);
8542 let buffer = &snapshot.buffer_snapshot;
8543 let start = buffer.anchor_before(0);
8544 let end = buffer.anchor_after(buffer.len());
8545 let theme = cx.theme().colors();
8546 self.background_highlights_in_range(start..end, &snapshot, theme)
8547 }
8548
8549 fn document_highlights_for_position<'a>(
8550 &'a self,
8551 position: Anchor,
8552 buffer: &'a MultiBufferSnapshot,
8553 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8554 let read_highlights = self
8555 .background_highlights
8556 .get(&TypeId::of::<DocumentHighlightRead>())
8557 .map(|h| &h.1);
8558 let write_highlights = self
8559 .background_highlights
8560 .get(&TypeId::of::<DocumentHighlightWrite>())
8561 .map(|h| &h.1);
8562 let left_position = position.bias_left(buffer);
8563 let right_position = position.bias_right(buffer);
8564 read_highlights
8565 .into_iter()
8566 .chain(write_highlights)
8567 .flat_map(move |ranges| {
8568 let start_ix = match ranges.binary_search_by(|probe| {
8569 let cmp = probe.end.cmp(&left_position, buffer);
8570 if cmp.is_ge() {
8571 Ordering::Greater
8572 } else {
8573 Ordering::Less
8574 }
8575 }) {
8576 Ok(i) | Err(i) => i,
8577 };
8578
8579 let right_position = right_position.clone();
8580 ranges[start_ix..]
8581 .iter()
8582 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8583 })
8584 }
8585
8586 pub fn background_highlights_in_range(
8587 &self,
8588 search_range: Range<Anchor>,
8589 display_snapshot: &DisplaySnapshot,
8590 theme: &ThemeColors,
8591 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8592 let mut results = Vec::new();
8593 for (color_fetcher, ranges) in self.background_highlights.values() {
8594 let color = color_fetcher(theme);
8595 let start_ix = match ranges.binary_search_by(|probe| {
8596 let cmp = probe
8597 .end
8598 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8599 if cmp.is_gt() {
8600 Ordering::Greater
8601 } else {
8602 Ordering::Less
8603 }
8604 }) {
8605 Ok(i) | Err(i) => i,
8606 };
8607 for range in &ranges[start_ix..] {
8608 if range
8609 .start
8610 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8611 .is_ge()
8612 {
8613 break;
8614 }
8615
8616 let start = range.start.to_display_point(&display_snapshot);
8617 let end = range.end.to_display_point(&display_snapshot);
8618 results.push((start..end, color))
8619 }
8620 }
8621 results
8622 }
8623
8624 pub fn background_highlight_row_ranges<T: 'static>(
8625 &self,
8626 search_range: Range<Anchor>,
8627 display_snapshot: &DisplaySnapshot,
8628 count: usize,
8629 ) -> Vec<RangeInclusive<DisplayPoint>> {
8630 let mut results = Vec::new();
8631 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8632 return vec![];
8633 };
8634
8635 let start_ix = match ranges.binary_search_by(|probe| {
8636 let cmp = probe
8637 .end
8638 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8639 if cmp.is_gt() {
8640 Ordering::Greater
8641 } else {
8642 Ordering::Less
8643 }
8644 }) {
8645 Ok(i) | Err(i) => i,
8646 };
8647 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8648 if let (Some(start_display), Some(end_display)) = (start, end) {
8649 results.push(
8650 start_display.to_display_point(display_snapshot)
8651 ..=end_display.to_display_point(display_snapshot),
8652 );
8653 }
8654 };
8655 let mut start_row: Option<Point> = None;
8656 let mut end_row: Option<Point> = None;
8657 if ranges.len() > count {
8658 return Vec::new();
8659 }
8660 for range in &ranges[start_ix..] {
8661 if range
8662 .start
8663 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8664 .is_ge()
8665 {
8666 break;
8667 }
8668 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8669 if let Some(current_row) = &end_row {
8670 if end.row == current_row.row {
8671 continue;
8672 }
8673 }
8674 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8675 if start_row.is_none() {
8676 assert_eq!(end_row, None);
8677 start_row = Some(start);
8678 end_row = Some(end);
8679 continue;
8680 }
8681 if let Some(current_end) = end_row.as_mut() {
8682 if start.row > current_end.row + 1 {
8683 push_region(start_row, end_row);
8684 start_row = Some(start);
8685 end_row = Some(end);
8686 } else {
8687 // Merge two hunks.
8688 *current_end = end;
8689 }
8690 } else {
8691 unreachable!();
8692 }
8693 }
8694 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8695 push_region(start_row, end_row);
8696 results
8697 }
8698
8699 pub fn highlight_text<T: 'static>(
8700 &mut self,
8701 ranges: Vec<Range<Anchor>>,
8702 style: HighlightStyle,
8703 cx: &mut ViewContext<Self>,
8704 ) {
8705 self.display_map.update(cx, |map, _| {
8706 map.highlight_text(TypeId::of::<T>(), ranges, style)
8707 });
8708 cx.notify();
8709 }
8710
8711 pub fn highlight_inlays<T: 'static>(
8712 &mut self,
8713 highlights: Vec<InlayHighlight>,
8714 style: HighlightStyle,
8715 cx: &mut ViewContext<Self>,
8716 ) {
8717 self.display_map.update(cx, |map, _| {
8718 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8719 });
8720 cx.notify();
8721 }
8722
8723 pub fn text_highlights<'a, T: 'static>(
8724 &'a self,
8725 cx: &'a AppContext,
8726 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8727 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8728 }
8729
8730 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8731 let cleared = self
8732 .display_map
8733 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8734 if cleared {
8735 cx.notify();
8736 }
8737 }
8738
8739 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8740 self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
8741 }
8742
8743 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8744 cx.notify();
8745 }
8746
8747 fn on_buffer_event(
8748 &mut self,
8749 multibuffer: Model<MultiBuffer>,
8750 event: &multi_buffer::Event,
8751 cx: &mut ViewContext<Self>,
8752 ) {
8753 match event {
8754 multi_buffer::Event::Edited {
8755 sigleton_buffer_edited,
8756 } => {
8757 self.refresh_active_diagnostics(cx);
8758 self.refresh_code_actions(cx);
8759 if self.has_active_copilot_suggestion(cx) {
8760 self.update_visible_copilot_suggestion(cx);
8761 }
8762 cx.emit(Event::BufferEdited);
8763 cx.emit(ItemEvent::Edit);
8764 cx.emit(ItemEvent::UpdateBreadcrumbs);
8765 cx.emit(SearchEvent::MatchesInvalidated);
8766
8767 if *sigleton_buffer_edited {
8768 if let Some(project) = &self.project {
8769 let project = project.read(cx);
8770 let languages_affected = multibuffer
8771 .read(cx)
8772 .all_buffers()
8773 .into_iter()
8774 .filter_map(|buffer| {
8775 let buffer = buffer.read(cx);
8776 let language = buffer.language()?;
8777 if project.is_local()
8778 && project.language_servers_for_buffer(buffer, cx).count() == 0
8779 {
8780 None
8781 } else {
8782 Some(language)
8783 }
8784 })
8785 .cloned()
8786 .collect::<HashSet<_>>();
8787 if !languages_affected.is_empty() {
8788 self.refresh_inlay_hints(
8789 InlayHintRefreshReason::BufferEdited(languages_affected),
8790 cx,
8791 );
8792 }
8793 }
8794 }
8795 }
8796 multi_buffer::Event::ExcerptsAdded {
8797 buffer,
8798 predecessor,
8799 excerpts,
8800 } => {
8801 cx.emit(Event::ExcerptsAdded {
8802 buffer: buffer.clone(),
8803 predecessor: *predecessor,
8804 excerpts: excerpts.clone(),
8805 });
8806 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8807 }
8808 multi_buffer::Event::ExcerptsRemoved { ids } => {
8809 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8810 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8811 }
8812 multi_buffer::Event::Reparsed => {
8813 cx.emit(ItemEvent::UpdateBreadcrumbs);
8814 }
8815 multi_buffer::Event::DirtyChanged => {
8816 cx.emit(ItemEvent::UpdateTab);
8817 }
8818 multi_buffer::Event::Saved
8819 | multi_buffer::Event::FileHandleChanged
8820 | multi_buffer::Event::Reloaded => {
8821 cx.emit(ItemEvent::UpdateTab);
8822 cx.emit(ItemEvent::UpdateBreadcrumbs);
8823 }
8824 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8825 multi_buffer::Event::Closed => cx.emit(ItemEvent::CloseItem),
8826 multi_buffer::Event::DiagnosticsUpdated => {
8827 self.refresh_active_diagnostics(cx);
8828 }
8829 _ => {}
8830 };
8831 }
8832
8833 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
8834 cx.notify();
8835 }
8836
8837 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8838 self.refresh_copilot_suggestions(true, cx);
8839 self.refresh_inlay_hints(
8840 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8841 self.selections.newest_anchor().head(),
8842 &self.buffer.read(cx).snapshot(cx),
8843 cx,
8844 )),
8845 cx,
8846 );
8847 }
8848
8849 // pub fn set_searchable(&mut self, searchable: bool) {
8850 // self.searchable = searchable;
8851 // }
8852
8853 // pub fn searchable(&self) -> bool {
8854 // self.searchable
8855 // }
8856
8857 // fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8858 // let active_item = workspace.active_item(cx);
8859 // let editor_handle = if let Some(editor) = active_item
8860 // .as_ref()
8861 // .and_then(|item| item.act_as::<Self>(cx))
8862 // {
8863 // editor
8864 // } else {
8865 // cx.propagate();
8866 // return;
8867 // };
8868
8869 // let editor = editor_handle.read(cx);
8870 // let buffer = editor.buffer.read(cx);
8871 // if buffer.is_singleton() {
8872 // cx.propagate();
8873 // return;
8874 // }
8875
8876 // let mut new_selections_by_buffer = HashMap::default();
8877 // for selection in editor.selections.all::<usize>(cx) {
8878 // for (buffer, mut range, _) in
8879 // buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8880 // {
8881 // if selection.reversed {
8882 // mem::swap(&mut range.start, &mut range.end);
8883 // }
8884 // new_selections_by_buffer
8885 // .entry(buffer)
8886 // .or_insert(Vec::new())
8887 // .push(range)
8888 // }
8889 // }
8890
8891 // editor_handle.update(cx, |editor, cx| {
8892 // editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8893 // });
8894 // let pane = workspace.active_pane().clone();
8895 // pane.update(cx, |pane, _| pane.disable_history());
8896
8897 // // We defer the pane interaction because we ourselves are a workspace item
8898 // // and activating a new item causes the pane to call a method on us reentrantly,
8899 // // which panics if we're on the stack.
8900 // cx.defer(move |workspace, cx| {
8901 // for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8902 // let editor = workspace.open_project_item::<Self>(buffer, cx);
8903 // editor.update(cx, |editor, cx| {
8904 // editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8905 // s.select_ranges(ranges);
8906 // });
8907 // });
8908 // }
8909
8910 // pane.update(cx, |pane, _| pane.enable_history());
8911 // });
8912 // }
8913
8914 // fn jump(
8915 // workspace: &mut Workspace,
8916 // path: ProjectPath,
8917 // position: Point,
8918 // anchor: language::Anchor,
8919 // cx: &mut ViewContext<Workspace>,
8920 // ) {
8921 // let editor = workspace.open_path(path, None, true, cx);
8922 // cx.spawn(|_, mut cx| async move {
8923 // let editor = editor
8924 // .await?
8925 // .downcast::<Editor>()
8926 // .ok_or_else(|| anyhow!("opened item was not an editor"))?
8927 // .downgrade();
8928 // editor.update(&mut cx, |editor, cx| {
8929 // let buffer = editor
8930 // .buffer()
8931 // .read(cx)
8932 // .as_singleton()
8933 // .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8934 // let buffer = buffer.read(cx);
8935 // let cursor = if buffer.can_resolve(&anchor) {
8936 // language::ToPoint::to_point(&anchor, buffer)
8937 // } else {
8938 // buffer.clip_point(position, Bias::Left)
8939 // };
8940
8941 // let nav_history = editor.nav_history.take();
8942 // editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8943 // s.select_ranges([cursor..cursor]);
8944 // });
8945 // editor.nav_history = nav_history;
8946
8947 // anyhow::Ok(())
8948 // })??;
8949
8950 // anyhow::Ok(())
8951 // })
8952 // .detach_and_log_err(cx);
8953 // }
8954
8955 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8956 let snapshot = self.buffer.read(cx).read(cx);
8957 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8958 Some(
8959 ranges
8960 .iter()
8961 .map(move |range| {
8962 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8963 })
8964 .collect(),
8965 )
8966 }
8967
8968 fn selection_replacement_ranges(
8969 &self,
8970 range: Range<OffsetUtf16>,
8971 cx: &AppContext,
8972 ) -> Vec<Range<OffsetUtf16>> {
8973 let selections = self.selections.all::<OffsetUtf16>(cx);
8974 let newest_selection = selections
8975 .iter()
8976 .max_by_key(|selection| selection.id)
8977 .unwrap();
8978 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8979 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8980 let snapshot = self.buffer.read(cx).read(cx);
8981 selections
8982 .into_iter()
8983 .map(|mut selection| {
8984 selection.start.0 =
8985 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8986 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8987 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8988 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8989 })
8990 .collect()
8991 }
8992
8993 fn report_copilot_event(
8994 &self,
8995 suggestion_id: Option<String>,
8996 suggestion_accepted: bool,
8997 cx: &AppContext,
8998 ) {
8999 let Some(project) = &self.project else { return };
9000
9001 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
9002 let file_extension = self
9003 .buffer
9004 .read(cx)
9005 .as_singleton()
9006 .and_then(|b| b.read(cx).file())
9007 .and_then(|file| Path::new(file.file_name(cx)).extension())
9008 .and_then(|e| e.to_str())
9009 .map(|a| a.to_string());
9010
9011 let telemetry = project.read(cx).client().telemetry().clone();
9012 let telemetry_settings = *TelemetrySettings::get_global(cx);
9013
9014 let event = ClickhouseEvent::Copilot {
9015 suggestion_id,
9016 suggestion_accepted,
9017 file_extension,
9018 };
9019 telemetry.report_clickhouse_event(event, telemetry_settings);
9020 }
9021
9022 #[cfg(any(test, feature = "test-support"))]
9023 fn report_editor_event(
9024 &self,
9025 _operation: &'static str,
9026 _file_extension: Option<String>,
9027 _cx: &AppContext,
9028 ) {
9029 }
9030
9031 #[cfg(not(any(test, feature = "test-support")))]
9032 fn report_editor_event(
9033 &self,
9034 operation: &'static str,
9035 file_extension: Option<String>,
9036 cx: &AppContext,
9037 ) {
9038 let Some(project) = &self.project else { return };
9039
9040 // If None, we are in a file without an extension
9041 let file = self
9042 .buffer
9043 .read(cx)
9044 .as_singleton()
9045 .and_then(|b| b.read(cx).file());
9046 let file_extension = file_extension.or(file
9047 .as_ref()
9048 .and_then(|file| Path::new(file.file_name(cx)).extension())
9049 .and_then(|e| e.to_str())
9050 .map(|a| a.to_string()));
9051
9052 let vim_mode = cx
9053 .global::<SettingsStore>()
9054 .raw_user_settings()
9055 .get("vim_mode")
9056 == Some(&serde_json::Value::Bool(true));
9057 let telemetry_settings = *TelemetrySettings::get_global(cx);
9058 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
9059 let copilot_enabled_for_language = self
9060 .buffer
9061 .read(cx)
9062 .settings_at(0, cx)
9063 .show_copilot_suggestions;
9064
9065 let telemetry = project.read(cx).client().telemetry().clone();
9066 let event = ClickhouseEvent::Editor {
9067 file_extension,
9068 vim_mode,
9069 operation,
9070 copilot_enabled,
9071 copilot_enabled_for_language,
9072 };
9073 telemetry.report_clickhouse_event(event, telemetry_settings)
9074 }
9075
9076 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9077 /// with each line being an array of {text, highlight} objects.
9078 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9079 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9080 return;
9081 };
9082
9083 #[derive(Serialize)]
9084 struct Chunk<'a> {
9085 text: String,
9086 highlight: Option<&'a str>,
9087 }
9088
9089 let snapshot = buffer.read(cx).snapshot();
9090 let range = self
9091 .selected_text_range(cx)
9092 .and_then(|selected_range| {
9093 if selected_range.is_empty() {
9094 None
9095 } else {
9096 Some(selected_range)
9097 }
9098 })
9099 .unwrap_or_else(|| 0..snapshot.len());
9100
9101 let chunks = snapshot.chunks(range, true);
9102 let mut lines = Vec::new();
9103 let mut line: VecDeque<Chunk> = VecDeque::new();
9104
9105 let Some(style) = self.style.as_ref() else {
9106 return;
9107 };
9108
9109 for chunk in chunks {
9110 let highlight = chunk
9111 .syntax_highlight_id
9112 .and_then(|id| id.name(&style.syntax));
9113 let mut chunk_lines = chunk.text.split("\n").peekable();
9114 while let Some(text) = chunk_lines.next() {
9115 let mut merged_with_last_token = false;
9116 if let Some(last_token) = line.back_mut() {
9117 if last_token.highlight == highlight {
9118 last_token.text.push_str(text);
9119 merged_with_last_token = true;
9120 }
9121 }
9122
9123 if !merged_with_last_token {
9124 line.push_back(Chunk {
9125 text: text.into(),
9126 highlight,
9127 });
9128 }
9129
9130 if chunk_lines.peek().is_some() {
9131 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9132 line.pop_front();
9133 }
9134 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9135 line.pop_back();
9136 }
9137
9138 lines.push(mem::take(&mut line));
9139 }
9140 }
9141 }
9142
9143 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9144 return;
9145 };
9146 cx.write_to_clipboard(ClipboardItem::new(lines));
9147 }
9148
9149 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9150 &self.inlay_hint_cache
9151 }
9152
9153 pub fn replay_insert_event(
9154 &mut self,
9155 text: &str,
9156 relative_utf16_range: Option<Range<isize>>,
9157 cx: &mut ViewContext<Self>,
9158 ) {
9159 if !self.input_enabled {
9160 cx.emit(Event::InputIgnored { text: text.into() });
9161 return;
9162 }
9163 if let Some(relative_utf16_range) = relative_utf16_range {
9164 let selections = self.selections.all::<OffsetUtf16>(cx);
9165 self.change_selections(None, cx, |s| {
9166 let new_ranges = selections.into_iter().map(|range| {
9167 let start = OffsetUtf16(
9168 range
9169 .head()
9170 .0
9171 .saturating_add_signed(relative_utf16_range.start),
9172 );
9173 let end = OffsetUtf16(
9174 range
9175 .head()
9176 .0
9177 .saturating_add_signed(relative_utf16_range.end),
9178 );
9179 start..end
9180 });
9181 s.select_ranges(new_ranges);
9182 });
9183 }
9184
9185 self.handle_input(text, cx);
9186 }
9187
9188 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9189 let Some(project) = self.project.as_ref() else {
9190 return false;
9191 };
9192 let project = project.read(cx);
9193
9194 let mut supports = false;
9195 self.buffer().read(cx).for_each_buffer(|buffer| {
9196 if !supports {
9197 supports = project
9198 .language_servers_for_buffer(buffer.read(cx), cx)
9199 .any(
9200 |(_, server)| match server.capabilities().inlay_hint_provider {
9201 Some(lsp::OneOf::Left(enabled)) => enabled,
9202 Some(lsp::OneOf::Right(_)) => true,
9203 None => false,
9204 },
9205 )
9206 }
9207 });
9208 supports
9209 }
9210
9211 pub fn focus(&self, cx: &mut WindowContext) {
9212 cx.focus(&self.focus_handle)
9213 }
9214
9215 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
9216 if self.focus_handle.is_focused(cx) {
9217 // todo!()
9218 // let focused_event = EditorFocused(cx.handle());
9219 // cx.emit_global(focused_event);
9220 cx.emit(Event::Focused);
9221 }
9222 if let Some(rename) = self.pending_rename.as_ref() {
9223 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
9224 cx.focus(&rename_editor_focus_handle);
9225 } else if self.focus_handle.is_focused(cx) {
9226 self.blink_manager.update(cx, BlinkManager::enable);
9227 self.buffer.update(cx, |buffer, cx| {
9228 buffer.finalize_last_transaction(cx);
9229 if self.leader_peer_id.is_none() {
9230 buffer.set_active_selections(
9231 &self.selections.disjoint_anchors(),
9232 self.selections.line_mode,
9233 self.cursor_shape,
9234 cx,
9235 );
9236 }
9237 });
9238 }
9239 }
9240
9241 fn handle_focus_out(&mut self, cx: &mut ViewContext<Self>) {
9242 // todo!()
9243 // let blurred_event = EditorBlurred(cx.handle());
9244 // cx.emit_global(blurred_event);
9245 self.blink_manager.update(cx, BlinkManager::disable);
9246 self.buffer
9247 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9248 self.hide_context_menu(cx);
9249 hide_hover(self, cx);
9250 cx.emit(Event::Blurred);
9251 cx.notify();
9252 }
9253}
9254
9255pub trait CollaborationHub {
9256 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9257 fn user_participant_indices<'a>(
9258 &self,
9259 cx: &'a AppContext,
9260 ) -> &'a HashMap<u64, ParticipantIndex>;
9261}
9262
9263impl CollaborationHub for Model<Project> {
9264 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9265 self.read(cx).collaborators()
9266 }
9267
9268 fn user_participant_indices<'a>(
9269 &self,
9270 cx: &'a AppContext,
9271 ) -> &'a HashMap<u64, ParticipantIndex> {
9272 self.read(cx).user_store().read(cx).participant_indices()
9273 }
9274}
9275
9276fn inlay_hint_settings(
9277 location: Anchor,
9278 snapshot: &MultiBufferSnapshot,
9279 cx: &mut ViewContext<'_, Editor>,
9280) -> InlayHintSettings {
9281 let file = snapshot.file_at(location);
9282 let language = snapshot.language_at(location);
9283 let settings = all_language_settings(file, cx);
9284 settings
9285 .language(language.map(|l| l.name()).as_deref())
9286 .inlay_hints
9287}
9288
9289fn consume_contiguous_rows(
9290 contiguous_row_selections: &mut Vec<Selection<Point>>,
9291 selection: &Selection<Point>,
9292 display_map: &DisplaySnapshot,
9293 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9294) -> (u32, u32) {
9295 contiguous_row_selections.push(selection.clone());
9296 let start_row = selection.start.row;
9297 let mut end_row = ending_row(selection, display_map);
9298
9299 while let Some(next_selection) = selections.peek() {
9300 if next_selection.start.row <= end_row {
9301 end_row = ending_row(next_selection, display_map);
9302 contiguous_row_selections.push(selections.next().unwrap().clone());
9303 } else {
9304 break;
9305 }
9306 }
9307 (start_row, end_row)
9308}
9309
9310fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9311 if next_selection.end.column > 0 || next_selection.is_empty() {
9312 display_map.next_line_boundary(next_selection.end).0.row + 1
9313 } else {
9314 next_selection.end.row
9315 }
9316}
9317
9318impl EditorSnapshot {
9319 pub fn remote_selections_in_range<'a>(
9320 &'a self,
9321 range: &'a Range<Anchor>,
9322 collaboration_hub: &dyn CollaborationHub,
9323 cx: &'a AppContext,
9324 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9325 let participant_indices = collaboration_hub.user_participant_indices(cx);
9326 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9327 let collaborators_by_replica_id = collaborators_by_peer_id
9328 .iter()
9329 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9330 .collect::<HashMap<_, _>>();
9331 self.buffer_snapshot
9332 .remote_selections_in_range(range)
9333 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9334 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9335 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9336 Some(RemoteSelection {
9337 replica_id,
9338 selection,
9339 cursor_shape,
9340 line_mode,
9341 participant_index,
9342 peer_id: collaborator.peer_id,
9343 })
9344 })
9345 }
9346
9347 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9348 self.display_snapshot.buffer_snapshot.language_at(position)
9349 }
9350
9351 pub fn is_focused(&self) -> bool {
9352 self.is_focused
9353 }
9354
9355 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9356 self.placeholder_text.as_ref()
9357 }
9358
9359 pub fn scroll_position(&self) -> gpui::Point<f32> {
9360 self.scroll_anchor.scroll_position(&self.display_snapshot)
9361 }
9362}
9363
9364impl Deref for EditorSnapshot {
9365 type Target = DisplaySnapshot;
9366
9367 fn deref(&self) -> &Self::Target {
9368 &self.display_snapshot
9369 }
9370}
9371
9372#[derive(Clone, Debug, PartialEq, Eq)]
9373pub enum Event {
9374 InputIgnored {
9375 text: Arc<str>,
9376 },
9377 InputHandled {
9378 utf16_range_to_replace: Option<Range<isize>>,
9379 text: Arc<str>,
9380 },
9381 ExcerptsAdded {
9382 buffer: Model<Buffer>,
9383 predecessor: ExcerptId,
9384 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9385 },
9386 ExcerptsRemoved {
9387 ids: Vec<ExcerptId>,
9388 },
9389 BufferEdited,
9390 Edited,
9391 Focused,
9392 Blurred,
9393 DiffBaseChanged,
9394 SelectionsChanged {
9395 local: bool,
9396 },
9397 ScrollPositionChanged {
9398 local: bool,
9399 autoscroll: bool,
9400 },
9401}
9402
9403pub struct EditorFocused(pub View<Editor>);
9404pub struct EditorBlurred(pub View<Editor>);
9405pub struct EditorReleased(pub WeakView<Editor>);
9406
9407// impl Entity for Editor {
9408// type Event = Event;
9409
9410// fn release(&mut self, cx: &mut AppContext) {
9411// cx.emit_global(EditorReleased(self.handle.clone()));
9412// }
9413// }
9414//
9415impl EventEmitter<Event> for Editor {}
9416
9417impl Render for Editor {
9418 type Element = EditorElement;
9419
9420 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
9421 let settings = ThemeSettings::get_global(cx);
9422 let text_style = match self.mode {
9423 EditorMode::SingleLine => {
9424 TextStyle {
9425 color: cx.theme().colors().text,
9426 font_family: "Zed Sans".into(), // todo!()
9427 font_features: FontFeatures::default(),
9428 font_size: rems(1.0).into(),
9429 font_weight: FontWeight::NORMAL,
9430 font_style: FontStyle::Normal,
9431 line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
9432 underline: None,
9433 }
9434 }
9435
9436 EditorMode::AutoHeight { max_lines } => todo!(),
9437
9438 EditorMode::Full => TextStyle {
9439 color: cx.theme().colors().text,
9440 font_family: settings.buffer_font.family.clone(),
9441 font_features: settings.buffer_font.features,
9442 font_size: settings.buffer_font_size.into(),
9443 font_weight: FontWeight::NORMAL,
9444 font_style: FontStyle::Normal,
9445 line_height: relative(settings.buffer_line_height.value()),
9446 underline: None,
9447 },
9448 };
9449
9450 let background = match self.mode {
9451 EditorMode::SingleLine => cx.theme().system().transparent,
9452 EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
9453 EditorMode::Full => cx.theme().colors().editor_background,
9454 };
9455
9456 EditorElement::new(EditorStyle {
9457 background,
9458 local_player: cx.theme().players().local(),
9459 text: text_style,
9460 scrollbar_width: px(12.),
9461 syntax: cx.theme().syntax().clone(),
9462 diagnostic_style: cx.theme().diagnostic_style(),
9463 })
9464 }
9465}
9466
9467// impl View for Editor {
9468// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9469// let style = self.style(cx);
9470// let font_changed = self.display_map.update(cx, |map, cx| {
9471// map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9472// map.set_font_with_size(style.text.font_id, style.text.font_size, cx)
9473// });
9474
9475// if font_changed {
9476// cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9477// hide_hover(editor, cx);
9478// hide_link_definition(editor, cx);
9479// });
9480// }
9481
9482// Stack::new()
9483// .with_child(EditorElement::new(style.clone()))
9484// .with_child(ChildView::new(&self.mouse_context_menu, cx))
9485// .into_any()
9486// }
9487
9488// fn ui_name() -> &'static str {
9489// "Editor"
9490// }
9491
9492// fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
9493// if cx.is_self_focused() {
9494// let focused_event = EditorFocused(cx.handle());
9495// cx.emit(Event::Focused);
9496// cx.emit_global(focused_event);
9497// }
9498// if let Some(rename) = self.pending_rename.as_ref() {
9499// cx.focus(&rename.editor);
9500// } else if cx.is_self_focused() || !focused.is::<Editor>() {
9501// if !self.focused {
9502// self.blink_manager.update(cx, BlinkManager::enable);
9503// }
9504// self.focused = true;
9505// self.buffer.update(cx, |buffer, cx| {
9506// buffer.finalize_last_transaction(cx);
9507// if self.leader_peer_id.is_none() {
9508// buffer.set_active_selections(
9509// &self.selections.disjoint_anchors(),
9510// self.selections.line_mode,
9511// self.cursor_shape,
9512// cx,
9513// );
9514// }
9515// });
9516// }
9517// }
9518
9519// fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
9520// let blurred_event = EditorBlurred(cx.handle());
9521// cx.emit_global(blurred_event);
9522// self.focused = false;
9523// self.blink_manager.update(cx, BlinkManager::disable);
9524// self.buffer
9525// .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9526// self.hide_context_menu(cx);
9527// hide_hover(self, cx);
9528// cx.emit(Event::Blurred);
9529// cx.notify();
9530// }
9531
9532// fn modifiers_changed(
9533// &mut self,
9534// event: &gpui::platform::ModifiersChangedEvent,
9535// cx: &mut ViewContext<Self>,
9536// ) -> bool {
9537// let pending_selection = self.has_pending_selection();
9538
9539// if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9540// if event.cmd && !pending_selection {
9541// let point = point.clone();
9542// let snapshot = self.snapshot(cx);
9543// let kind = point.definition_kind(event.shift);
9544
9545// show_link_definition(kind, self, point, snapshot, cx);
9546// return false;
9547// }
9548// }
9549
9550// {
9551// if self.link_go_to_definition_state.symbol_range.is_some()
9552// || !self.link_go_to_definition_state.definitions.is_empty()
9553// {
9554// self.link_go_to_definition_state.symbol_range.take();
9555// self.link_go_to_definition_state.definitions.clear();
9556// cx.notify();
9557// }
9558
9559// self.link_go_to_definition_state.task = None;
9560
9561// self.clear_highlights::<LinkGoToDefinitionState>(cx);
9562// }
9563
9564// false
9565// }
9566
9567impl InputHandler for Editor {
9568 fn text_for_range(
9569 &mut self,
9570 range_utf16: Range<usize>,
9571 cx: &mut ViewContext<Self>,
9572 ) -> Option<String> {
9573 Some(
9574 self.buffer
9575 .read(cx)
9576 .read(cx)
9577 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9578 .collect(),
9579 )
9580 }
9581
9582 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9583 // Prevent the IME menu from appearing when holding down an alphabetic key
9584 // while input is disabled.
9585 if !self.input_enabled {
9586 return None;
9587 }
9588
9589 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9590 Some(range.start.0..range.end.0)
9591 }
9592
9593 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9594 let snapshot = self.buffer.read(cx).read(cx);
9595 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9596 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9597 }
9598
9599 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9600 self.clear_highlights::<InputComposition>(cx);
9601 self.ime_transaction.take();
9602 }
9603
9604 fn replace_text_in_range(
9605 &mut self,
9606 range_utf16: Option<Range<usize>>,
9607 text: &str,
9608 cx: &mut ViewContext<Self>,
9609 ) {
9610 if !self.input_enabled {
9611 cx.emit(Event::InputIgnored { text: text.into() });
9612 return;
9613 }
9614
9615 self.transact(cx, |this, cx| {
9616 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9617 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9618 Some(this.selection_replacement_ranges(range_utf16, cx))
9619 } else {
9620 this.marked_text_ranges(cx)
9621 };
9622
9623 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9624 let newest_selection_id = this.selections.newest_anchor().id;
9625 this.selections
9626 .all::<OffsetUtf16>(cx)
9627 .iter()
9628 .zip(ranges_to_replace.iter())
9629 .find_map(|(selection, range)| {
9630 if selection.id == newest_selection_id {
9631 Some(
9632 (range.start.0 as isize - selection.head().0 as isize)
9633 ..(range.end.0 as isize - selection.head().0 as isize),
9634 )
9635 } else {
9636 None
9637 }
9638 })
9639 });
9640
9641 cx.emit(Event::InputHandled {
9642 utf16_range_to_replace: range_to_replace,
9643 text: text.into(),
9644 });
9645
9646 if let Some(new_selected_ranges) = new_selected_ranges {
9647 this.change_selections(None, cx, |selections| {
9648 selections.select_ranges(new_selected_ranges)
9649 });
9650 }
9651
9652 this.handle_input(text, cx);
9653 });
9654
9655 if let Some(transaction) = self.ime_transaction {
9656 self.buffer.update(cx, |buffer, cx| {
9657 buffer.group_until_transaction(transaction, cx);
9658 });
9659 }
9660
9661 self.unmark_text(cx);
9662 }
9663
9664 fn replace_and_mark_text_in_range(
9665 &mut self,
9666 range_utf16: Option<Range<usize>>,
9667 text: &str,
9668 new_selected_range_utf16: Option<Range<usize>>,
9669 cx: &mut ViewContext<Self>,
9670 ) {
9671 if !self.input_enabled {
9672 cx.emit(Event::InputIgnored { text: text.into() });
9673 return;
9674 }
9675
9676 let transaction = self.transact(cx, |this, cx| {
9677 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9678 let snapshot = this.buffer.read(cx).read(cx);
9679 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9680 for marked_range in &mut marked_ranges {
9681 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9682 marked_range.start.0 += relative_range_utf16.start;
9683 marked_range.start =
9684 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9685 marked_range.end =
9686 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9687 }
9688 }
9689 Some(marked_ranges)
9690 } else if let Some(range_utf16) = range_utf16 {
9691 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9692 Some(this.selection_replacement_ranges(range_utf16, cx))
9693 } else {
9694 None
9695 };
9696
9697 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9698 let newest_selection_id = this.selections.newest_anchor().id;
9699 this.selections
9700 .all::<OffsetUtf16>(cx)
9701 .iter()
9702 .zip(ranges_to_replace.iter())
9703 .find_map(|(selection, range)| {
9704 if selection.id == newest_selection_id {
9705 Some(
9706 (range.start.0 as isize - selection.head().0 as isize)
9707 ..(range.end.0 as isize - selection.head().0 as isize),
9708 )
9709 } else {
9710 None
9711 }
9712 })
9713 });
9714
9715 cx.emit(Event::InputHandled {
9716 utf16_range_to_replace: range_to_replace,
9717 text: text.into(),
9718 });
9719
9720 if let Some(ranges) = ranges_to_replace {
9721 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9722 }
9723
9724 let marked_ranges = {
9725 let snapshot = this.buffer.read(cx).read(cx);
9726 this.selections
9727 .disjoint_anchors()
9728 .iter()
9729 .map(|selection| {
9730 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9731 })
9732 .collect::<Vec<_>>()
9733 };
9734
9735 if text.is_empty() {
9736 this.unmark_text(cx);
9737 } else {
9738 this.highlight_text::<InputComposition>(
9739 marked_ranges.clone(),
9740 HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
9741 cx,
9742 );
9743 }
9744
9745 this.handle_input(text, cx);
9746
9747 if let Some(new_selected_range) = new_selected_range_utf16 {
9748 let snapshot = this.buffer.read(cx).read(cx);
9749 let new_selected_ranges = marked_ranges
9750 .into_iter()
9751 .map(|marked_range| {
9752 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9753 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9754 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9755 snapshot.clip_offset_utf16(new_start, Bias::Left)
9756 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9757 })
9758 .collect::<Vec<_>>();
9759
9760 drop(snapshot);
9761 this.change_selections(None, cx, |selections| {
9762 selections.select_ranges(new_selected_ranges)
9763 });
9764 }
9765 });
9766
9767 self.ime_transaction = self.ime_transaction.or(transaction);
9768 if let Some(transaction) = self.ime_transaction {
9769 self.buffer.update(cx, |buffer, cx| {
9770 buffer.group_until_transaction(transaction, cx);
9771 });
9772 }
9773
9774 if self.text_highlights::<InputComposition>(cx).is_none() {
9775 self.ime_transaction.take();
9776 }
9777 }
9778
9779 fn bounds_for_range(
9780 &mut self,
9781 range_utf16: Range<usize>,
9782 element_bounds: gpui::Bounds<Pixels>,
9783 cx: &mut ViewContext<Self>,
9784 ) -> Option<gpui::Bounds<Pixels>> {
9785 let text_layout_details = self.text_layout_details(cx);
9786 let style = &text_layout_details.editor_style;
9787 let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
9788 let font_size = style.text.font_size.to_pixels(cx.rem_size());
9789 let line_height = style.text.line_height_in_pixels(cx.rem_size());
9790 let em_width = cx
9791 .text_system()
9792 .typographic_bounds(font_id, font_size, 'm')
9793 .unwrap()
9794 .size
9795 .width;
9796
9797 let snapshot = self.snapshot(cx);
9798 let scroll_position = snapshot.scroll_position();
9799 let scroll_left = scroll_position.x * em_width;
9800
9801 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
9802 let x = snapshot.x_for_point(start, &text_layout_details) - scroll_left + self.gutter_width;
9803 let y = line_height * (start.row() as f32 - scroll_position.y);
9804
9805 Some(Bounds {
9806 origin: element_bounds.origin + point(x, y),
9807 size: size(em_width, line_height),
9808 })
9809 }
9810}
9811
9812// fn build_style(
9813// settings: &ThemeSettings,
9814// get_field_editor_theme: Option<&GetFieldEditorTheme>,
9815// override_text_style: Option<&OverrideTextStyle>,
9816// cx: &mut AppContext,
9817// ) -> EditorStyle {
9818// let font_cache = cx.font_cache();
9819// let line_height_scalar = settings.line_height();
9820// let theme_id = settings.theme.meta.id;
9821// let mut theme = settings.theme.editor.clone();
9822// let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9823// let field_editor_theme = get_field_editor_theme(&settings.theme);
9824// theme.text_color = field_editor_theme.text.color;
9825// theme.selection = field_editor_theme.selection;
9826// theme.background = field_editor_theme
9827// .container
9828// .background_color
9829// .unwrap_or_default();
9830// EditorStyle {
9831// text: field_editor_theme.text,
9832// placeholder_text: field_editor_theme.placeholder_text,
9833// line_height_scalar,
9834// theme,
9835// theme_id,
9836// }
9837// } else {
9838// todo!();
9839// // let font_family_id = settings.buffer_font_family;
9840// // let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9841// // let font_properties = Default::default();
9842// // let font_id = font_cache
9843// // .select_font(font_family_id, &font_properties)
9844// // .unwrap();
9845// // let font_size = settings.buffer_font_size(cx);
9846// // EditorStyle {
9847// // text: TextStyle {
9848// // color: settings.theme.editor.text_color,
9849// // font_family_name,
9850// // font_family_id,
9851// // font_id,
9852// // font_size,
9853// // font_properties,
9854// // underline: Default::default(),
9855// // soft_wrap: false,
9856// // },
9857// // placeholder_text: None,
9858// // line_height_scalar,
9859// // theme,
9860// // theme_id,
9861// // }
9862// };
9863
9864// if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9865// if let Some(highlighted) = style
9866// .text
9867// .clone()
9868// .highlight(highlight_style, font_cache)
9869// .log_err()
9870// {
9871// style.text = highlighted;
9872// }
9873// }
9874
9875// style
9876// }
9877
9878trait SelectionExt {
9879 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9880 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9881 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9882 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9883 -> Range<u32>;
9884}
9885
9886impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9887 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9888 let start = self.start.to_point(buffer);
9889 let end = self.end.to_point(buffer);
9890 if self.reversed {
9891 end..start
9892 } else {
9893 start..end
9894 }
9895 }
9896
9897 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9898 let start = self.start.to_offset(buffer);
9899 let end = self.end.to_offset(buffer);
9900 if self.reversed {
9901 end..start
9902 } else {
9903 start..end
9904 }
9905 }
9906
9907 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9908 let start = self
9909 .start
9910 .to_point(&map.buffer_snapshot)
9911 .to_display_point(map);
9912 let end = self
9913 .end
9914 .to_point(&map.buffer_snapshot)
9915 .to_display_point(map);
9916 if self.reversed {
9917 end..start
9918 } else {
9919 start..end
9920 }
9921 }
9922
9923 fn spanned_rows(
9924 &self,
9925 include_end_if_at_line_start: bool,
9926 map: &DisplaySnapshot,
9927 ) -> Range<u32> {
9928 let start = self.start.to_point(&map.buffer_snapshot);
9929 let mut end = self.end.to_point(&map.buffer_snapshot);
9930 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9931 end.row -= 1;
9932 }
9933
9934 let buffer_start = map.prev_line_boundary(start).0;
9935 let buffer_end = map.next_line_boundary(end).0;
9936 buffer_start.row..buffer_end.row + 1
9937 }
9938}
9939
9940impl<T: InvalidationRegion> InvalidationStack<T> {
9941 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9942 where
9943 S: Clone + ToOffset,
9944 {
9945 while let Some(region) = self.last() {
9946 let all_selections_inside_invalidation_ranges =
9947 if selections.len() == region.ranges().len() {
9948 selections
9949 .iter()
9950 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9951 .all(|(selection, invalidation_range)| {
9952 let head = selection.head().to_offset(buffer);
9953 invalidation_range.start <= head && invalidation_range.end >= head
9954 })
9955 } else {
9956 false
9957 };
9958
9959 if all_selections_inside_invalidation_ranges {
9960 break;
9961 } else {
9962 self.pop();
9963 }
9964 }
9965 }
9966}
9967
9968impl<T> Default for InvalidationStack<T> {
9969 fn default() -> Self {
9970 Self(Default::default())
9971 }
9972}
9973
9974impl<T> Deref for InvalidationStack<T> {
9975 type Target = Vec<T>;
9976
9977 fn deref(&self) -> &Self::Target {
9978 &self.0
9979 }
9980}
9981
9982impl<T> DerefMut for InvalidationStack<T> {
9983 fn deref_mut(&mut self) -> &mut Self::Target {
9984 &mut self.0
9985 }
9986}
9987
9988impl InvalidationRegion for SnippetState {
9989 fn ranges(&self) -> &[Range<Anchor>] {
9990 &self.ranges[self.active_index]
9991 }
9992}
9993
9994// impl Deref for EditorStyle {
9995// type Target = theme::Editor;
9996
9997// fn deref(&self) -> &Self::Target {
9998// &self.theme
9999// }
10000// }
10001
10002pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
10003 let mut highlighted_lines = Vec::new();
10004
10005 for (index, line) in diagnostic.message.lines().enumerate() {
10006 let line = match &diagnostic.source {
10007 Some(source) if index == 0 => {
10008 let source_highlight = Vec::from_iter(0..source.len());
10009 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
10010 }
10011
10012 _ => highlight_diagnostic_message(Vec::new(), line),
10013 };
10014 highlighted_lines.push(line);
10015 }
10016 let message = diagnostic.message;
10017 Arc::new(move |cx: &mut BlockContext| {
10018 todo!()
10019 // let message = message.clone();
10020 // let settings = ThemeSettings::get_global(cx);
10021 // let tooltip_style = settings.theme.tooltip.clone();
10022 // let theme = &settings.theme.editor;
10023 // let style = diagnostic_style(diagnostic.severity, is_valid, theme);
10024 // let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
10025 // let anchor_x = cx.anchor_x;
10026 // enum BlockContextToolip {}
10027 // MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
10028 // Flex::column()
10029 // .with_children(highlighted_lines.iter().map(|(line, highlights)| {
10030 // Label::new(
10031 // line.clone(),
10032 // style.message.clone().with_font_size(font_size),
10033 // )
10034 // .with_highlights(highlights.clone())
10035 // .contained()
10036 // .with_margin_left(anchor_x)
10037 // }))
10038 // .aligned()
10039 // .left()
10040 // .into_any()
10041 // })
10042 // .with_cursor_style(CursorStyle::PointingHand)
10043 // .on_click(MouseButton::Left, move |_, _, cx| {
10044 // cx.write_to_clipboard(ClipboardItem::new(message.clone()));
10045 // })
10046 // // We really need to rethink this ID system...
10047 // .with_tooltip::<BlockContextToolip>(
10048 // cx.block_id,
10049 // "Copy diagnostic message",
10050 // None,
10051 // tooltip_style,
10052 // cx,
10053 // )
10054 // .into_any()
10055 })
10056}
10057
10058pub fn highlight_diagnostic_message(
10059 initial_highlights: Vec<usize>,
10060 message: &str,
10061) -> (String, Vec<usize>) {
10062 let mut message_without_backticks = String::new();
10063 let mut prev_offset = 0;
10064 let mut inside_block = false;
10065 let mut highlights = initial_highlights;
10066 for (match_ix, (offset, _)) in message
10067 .match_indices('`')
10068 .chain([(message.len(), "")])
10069 .enumerate()
10070 {
10071 message_without_backticks.push_str(&message[prev_offset..offset]);
10072 if inside_block {
10073 highlights.extend(prev_offset - match_ix..offset - match_ix);
10074 }
10075
10076 inside_block = !inside_block;
10077 prev_offset = offset + 1;
10078 }
10079
10080 (message_without_backticks, highlights)
10081}
10082
10083pub fn diagnostic_style(
10084 severity: DiagnosticSeverity,
10085 valid: bool,
10086 style: &DiagnosticStyle,
10087) -> Hsla {
10088 match (severity, valid) {
10089 (DiagnosticSeverity::ERROR, true) => style.error,
10090 (DiagnosticSeverity::ERROR, false) => style.error,
10091 (DiagnosticSeverity::WARNING, true) => style.warning,
10092 (DiagnosticSeverity::WARNING, false) => style.warning,
10093 (DiagnosticSeverity::INFORMATION, true) => style.info,
10094 (DiagnosticSeverity::INFORMATION, false) => style.info,
10095 (DiagnosticSeverity::HINT, true) => style.info,
10096 (DiagnosticSeverity::HINT, false) => style.info,
10097 _ => style.ignored,
10098 }
10099}
10100
10101// pub fn combine_syntax_and_fuzzy_match_highlights(
10102// text: &str,
10103// default_style: HighlightStyle,
10104// syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
10105// match_indices: &[usize],
10106// ) -> Vec<(Range<usize>, HighlightStyle)> {
10107// let mut result = Vec::new();
10108// let mut match_indices = match_indices.iter().copied().peekable();
10109
10110// for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
10111// {
10112// syntax_highlight.weight = None;
10113
10114// // Add highlights for any fuzzy match characters before the next
10115// // syntax highlight range.
10116// while let Some(&match_index) = match_indices.peek() {
10117// if match_index >= range.start {
10118// break;
10119// }
10120// match_indices.next();
10121// let end_index = char_ix_after(match_index, text);
10122// let mut match_style = default_style;
10123// match_style.weight = Some(FontWeight::BOLD);
10124// result.push((match_index..end_index, match_style));
10125// }
10126
10127// if range.start == usize::MAX {
10128// break;
10129// }
10130
10131// // Add highlights for any fuzzy match characters within the
10132// // syntax highlight range.
10133// let mut offset = range.start;
10134// while let Some(&match_index) = match_indices.peek() {
10135// if match_index >= range.end {
10136// break;
10137// }
10138
10139// match_indices.next();
10140// if match_index > offset {
10141// result.push((offset..match_index, syntax_highlight));
10142// }
10143
10144// let mut end_index = char_ix_after(match_index, text);
10145// while let Some(&next_match_index) = match_indices.peek() {
10146// if next_match_index == end_index && next_match_index < range.end {
10147// end_index = char_ix_after(next_match_index, text);
10148// match_indices.next();
10149// } else {
10150// break;
10151// }
10152// }
10153
10154// let mut match_style = syntax_highlight;
10155// match_style.weight = Some(FontWeight::BOLD);
10156// result.push((match_index..end_index, match_style));
10157// offset = end_index;
10158// }
10159
10160// if offset < range.end {
10161// result.push((offset..range.end, syntax_highlight));
10162// }
10163// }
10164
10165// fn char_ix_after(ix: usize, text: &str) -> usize {
10166// ix + text[ix..].chars().next().unwrap().len_utf8()
10167// }
10168
10169// result
10170// }
10171
10172// pub fn styled_runs_for_code_label<'a>(
10173// label: &'a CodeLabel,
10174// syntax_theme: &'a theme::SyntaxTheme,
10175// ) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10176// let fade_out = HighlightStyle {
10177// fade_out: Some(0.35),
10178// ..Default::default()
10179// };
10180
10181// let mut prev_end = label.filter_range.end;
10182// label
10183// .runs
10184// .iter()
10185// .enumerate()
10186// .flat_map(move |(ix, (range, highlight_id))| {
10187// let style = if let Some(style) = highlight_id.style(syntax_theme) {
10188// style
10189// } else {
10190// return Default::default();
10191// };
10192// let mut muted_style = style;
10193// muted_style.highlight(fade_out);
10194
10195// let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10196// if range.start >= label.filter_range.end {
10197// if range.start > prev_end {
10198// runs.push((prev_end..range.start, fade_out));
10199// }
10200// runs.push((range.clone(), muted_style));
10201// } else if range.end <= label.filter_range.end {
10202// runs.push((range.clone(), style));
10203// } else {
10204// runs.push((range.start..label.filter_range.end, style));
10205// runs.push((label.filter_range.end..range.end, muted_style));
10206// }
10207// prev_end = cmp::max(prev_end, range.end);
10208
10209// if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10210// runs.push((prev_end..label.text.len(), fade_out));
10211// }
10212
10213// runs
10214// })
10215
10216pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10217 let mut index = 0;
10218 let mut codepoints = text.char_indices().peekable();
10219
10220 std::iter::from_fn(move || {
10221 let start_index = index;
10222 while let Some((new_index, codepoint)) = codepoints.next() {
10223 index = new_index + codepoint.len_utf8();
10224 let current_upper = codepoint.is_uppercase();
10225 let next_upper = codepoints
10226 .peek()
10227 .map(|(_, c)| c.is_uppercase())
10228 .unwrap_or(false);
10229
10230 if !current_upper && next_upper {
10231 return Some(&text[start_index..index]);
10232 }
10233 }
10234
10235 index = text.len();
10236 if start_index < text.len() {
10237 return Some(&text[start_index..]);
10238 }
10239 None
10240 })
10241 .flat_map(|word| word.split_inclusive('_'))
10242 .flat_map(|word| word.split_inclusive('-'))
10243}
10244
10245trait RangeToAnchorExt {
10246 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10247}
10248
10249impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10250 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10251 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10252 }
10253}