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