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