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