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