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