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