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