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