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