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