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