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