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.excerpts_for_inlay_hints_query(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 excerpts_for_inlay_hints_query(
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 Some(project) = self.project.as_ref() else {
3468 return HashMap::default();
3469 };
3470 let project = project.read(cx);
3471 let multi_buffer = self.buffer().read(cx);
3472 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3473 let multi_buffer_visible_start = self
3474 .scroll_manager
3475 .anchor()
3476 .anchor
3477 .to_point(&multi_buffer_snapshot);
3478 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3479 multi_buffer_visible_start
3480 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3481 Bias::Left,
3482 );
3483 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3484 multi_buffer
3485 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3486 .into_iter()
3487 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3488 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3489 let buffer = buffer_handle.read(cx);
3490 let buffer_file = project::worktree::File::from_dyn(buffer.file())?;
3491 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3492 let worktree_entry = buffer_worktree
3493 .read(cx)
3494 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3495 if worktree_entry.is_ignored {
3496 return None;
3497 }
3498
3499 let language = buffer.language()?;
3500 if let Some(restrict_to_languages) = restrict_to_languages {
3501 if !restrict_to_languages.contains(language) {
3502 return None;
3503 }
3504 }
3505 Some((
3506 excerpt_id,
3507 (
3508 buffer_handle,
3509 buffer.version().clone(),
3510 excerpt_visible_range,
3511 ),
3512 ))
3513 })
3514 .collect()
3515 }
3516
3517 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3518 TextLayoutDetails {
3519 text_system: cx.text_system().clone(),
3520 editor_style: self.style.clone().unwrap(),
3521 rem_size: cx.rem_size(),
3522 }
3523 }
3524
3525 fn splice_inlay_hints(
3526 &self,
3527 to_remove: Vec<InlayId>,
3528 to_insert: Vec<Inlay>,
3529 cx: &mut ViewContext<Self>,
3530 ) {
3531 self.display_map.update(cx, |display_map, cx| {
3532 display_map.splice_inlays(to_remove, to_insert, cx);
3533 });
3534 cx.notify();
3535 }
3536
3537 fn trigger_on_type_formatting(
3538 &self,
3539 input: String,
3540 cx: &mut ViewContext<Self>,
3541 ) -> Option<Task<Result<()>>> {
3542 if input.len() != 1 {
3543 return None;
3544 }
3545
3546 let project = self.project.as_ref()?;
3547 let position = self.selections.newest_anchor().head();
3548 let (buffer, buffer_position) = self
3549 .buffer
3550 .read(cx)
3551 .text_anchor_for_position(position.clone(), cx)?;
3552
3553 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3554 // hence we do LSP request & edit on host side only — add formats to host's history.
3555 let push_to_lsp_host_history = true;
3556 // If this is not the host, append its history with new edits.
3557 let push_to_client_history = project.read(cx).is_remote();
3558
3559 let on_type_formatting = project.update(cx, |project, cx| {
3560 project.on_type_format(
3561 buffer.clone(),
3562 buffer_position,
3563 input,
3564 push_to_lsp_host_history,
3565 cx,
3566 )
3567 });
3568 Some(cx.spawn(|editor, mut cx| async move {
3569 if let Some(transaction) = on_type_formatting.await? {
3570 if push_to_client_history {
3571 buffer.update(&mut cx, |buffer, _| {
3572 buffer.push_transaction(transaction, Instant::now());
3573 });
3574 }
3575 editor.update(&mut cx, |editor, cx| {
3576 editor.refresh_document_highlights(cx);
3577 })?;
3578 }
3579 Ok(())
3580 }))
3581 }
3582
3583 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3584 if self.pending_rename.is_some() {
3585 return;
3586 }
3587
3588 let project = if let Some(project) = self.project.clone() {
3589 project
3590 } else {
3591 return;
3592 };
3593
3594 let position = self.selections.newest_anchor().head();
3595 let (buffer, buffer_position) = if let Some(output) = self
3596 .buffer
3597 .read(cx)
3598 .text_anchor_for_position(position.clone(), cx)
3599 {
3600 output
3601 } else {
3602 return;
3603 };
3604
3605 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3606 let completions = project.update(cx, |project, cx| {
3607 project.completions(&buffer, buffer_position, cx)
3608 });
3609
3610 let id = post_inc(&mut self.next_completion_id);
3611 let task = cx.spawn(|this, mut cx| {
3612 async move {
3613 let completions = completions.await.log_err();
3614 let (menu, pre_resolve_task) = if let Some(completions) = completions {
3615 let mut menu = CompletionsMenu {
3616 id,
3617 initial_position: position,
3618 match_candidates: completions
3619 .iter()
3620 .enumerate()
3621 .map(|(id, completion)| {
3622 StringMatchCandidate::new(
3623 id,
3624 completion.label.text[completion.label.filter_range.clone()]
3625 .into(),
3626 )
3627 })
3628 .collect(),
3629 buffer,
3630 completions: Arc::new(RwLock::new(completions.into())),
3631 matches: Vec::new().into(),
3632 selected_item: 0,
3633 scroll_handle: UniformListScrollHandle::new(),
3634 };
3635 menu.filter(query.as_deref(), cx.background_executor().clone())
3636 .await;
3637
3638 if menu.matches.is_empty() {
3639 (None, None)
3640 } else {
3641 let pre_resolve_task = this
3642 .update(&mut cx, |editor, cx| {
3643 menu.pre_resolve_completion_documentation(editor, cx)
3644 })
3645 .ok()
3646 .flatten();
3647 (Some(menu), pre_resolve_task)
3648 }
3649 } else {
3650 (None, None)
3651 };
3652
3653 this.update(&mut cx, |this, cx| {
3654 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3655
3656 let mut context_menu = this.context_menu.write();
3657 match context_menu.as_ref() {
3658 None => {}
3659
3660 Some(ContextMenu::Completions(prev_menu)) => {
3661 if prev_menu.id > id {
3662 return;
3663 }
3664 }
3665
3666 _ => return,
3667 }
3668
3669 if this.focus_handle.is_focused(cx) && menu.is_some() {
3670 let menu = menu.unwrap();
3671 *context_menu = Some(ContextMenu::Completions(menu));
3672 drop(context_menu);
3673 this.discard_copilot_suggestion(cx);
3674 cx.notify();
3675 } else if this.completion_tasks.is_empty() {
3676 // If there are no more completion tasks and the last menu was
3677 // empty, we should hide it. If it was already hidden, we should
3678 // also show the copilot suggestion when available.
3679 drop(context_menu);
3680 if this.hide_context_menu(cx).is_none() {
3681 this.update_visible_copilot_suggestion(cx);
3682 }
3683 }
3684 })?;
3685
3686 if let Some(pre_resolve_task) = pre_resolve_task {
3687 pre_resolve_task.await;
3688 }
3689
3690 Ok::<_, anyhow::Error>(())
3691 }
3692 .log_err()
3693 });
3694
3695 self.completion_tasks.push((id, task));
3696 }
3697
3698 // pub fn confirm_completion(
3699 // &mut self,
3700 // action: &ConfirmCompletion,
3701 // cx: &mut ViewContext<Self>,
3702 // ) -> Option<Task<Result<()>>> {
3703 // use language::ToOffset as _;
3704
3705 // let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3706 // menu
3707 // } else {
3708 // return None;
3709 // };
3710
3711 // let mat = completions_menu
3712 // .matches
3713 // .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3714 // let buffer_handle = completions_menu.buffer;
3715 // let completions = completions_menu.completions.read();
3716 // let completion = completions.get(mat.candidate_id)?;
3717
3718 // let snippet;
3719 // let text;
3720 // if completion.is_snippet() {
3721 // snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3722 // text = snippet.as_ref().unwrap().text.clone();
3723 // } else {
3724 // snippet = None;
3725 // text = completion.new_text.clone();
3726 // };
3727 // let selections = self.selections.all::<usize>(cx);
3728 // let buffer = buffer_handle.read(cx);
3729 // let old_range = completion.old_range.to_offset(buffer);
3730 // let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3731
3732 // let newest_selection = self.selections.newest_anchor();
3733 // if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3734 // return None;
3735 // }
3736
3737 // let lookbehind = newest_selection
3738 // .start
3739 // .text_anchor
3740 // .to_offset(buffer)
3741 // .saturating_sub(old_range.start);
3742 // let lookahead = old_range
3743 // .end
3744 // .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3745 // let mut common_prefix_len = old_text
3746 // .bytes()
3747 // .zip(text.bytes())
3748 // .take_while(|(a, b)| a == b)
3749 // .count();
3750
3751 // let snapshot = self.buffer.read(cx).snapshot(cx);
3752 // let mut range_to_replace: Option<Range<isize>> = None;
3753 // let mut ranges = Vec::new();
3754 // for selection in &selections {
3755 // if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3756 // let start = selection.start.saturating_sub(lookbehind);
3757 // let end = selection.end + lookahead;
3758 // if selection.id == newest_selection.id {
3759 // range_to_replace = Some(
3760 // ((start + common_prefix_len) as isize - selection.start as isize)
3761 // ..(end as isize - selection.start as isize),
3762 // );
3763 // }
3764 // ranges.push(start + common_prefix_len..end);
3765 // } else {
3766 // common_prefix_len = 0;
3767 // ranges.clear();
3768 // ranges.extend(selections.iter().map(|s| {
3769 // if s.id == newest_selection.id {
3770 // range_to_replace = Some(
3771 // old_range.start.to_offset_utf16(&snapshot).0 as isize
3772 // - selection.start as isize
3773 // ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3774 // - selection.start as isize,
3775 // );
3776 // old_range.clone()
3777 // } else {
3778 // s.start..s.end
3779 // }
3780 // }));
3781 // break;
3782 // }
3783 // }
3784 // let text = &text[common_prefix_len..];
3785
3786 // cx.emit(Event::InputHandled {
3787 // utf16_range_to_replace: range_to_replace,
3788 // text: text.into(),
3789 // });
3790
3791 // self.transact(cx, |this, cx| {
3792 // if let Some(mut snippet) = snippet {
3793 // snippet.text = text.to_string();
3794 // for tabstop in snippet.tabstops.iter_mut().flatten() {
3795 // tabstop.start -= common_prefix_len as isize;
3796 // tabstop.end -= common_prefix_len as isize;
3797 // }
3798
3799 // this.insert_snippet(&ranges, snippet, cx).log_err();
3800 // } else {
3801 // this.buffer.update(cx, |buffer, cx| {
3802 // buffer.edit(
3803 // ranges.iter().map(|range| (range.clone(), text)),
3804 // this.autoindent_mode.clone(),
3805 // cx,
3806 // );
3807 // });
3808 // }
3809
3810 // this.refresh_copilot_suggestions(true, cx);
3811 // });
3812
3813 // let project = self.project.clone()?;
3814 // let apply_edits = project.update(cx, |project, cx| {
3815 // project.apply_additional_edits_for_completion(
3816 // buffer_handle,
3817 // completion.clone(),
3818 // true,
3819 // cx,
3820 // )
3821 // });
3822 // Some(cx.foreground().spawn(async move {
3823 // apply_edits.await?;
3824 // Ok(())
3825 // }))
3826 // }
3827
3828 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3829 let mut context_menu = self.context_menu.write();
3830 if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
3831 *context_menu = None;
3832 cx.notify();
3833 return;
3834 }
3835 drop(context_menu);
3836
3837 let deployed_from_indicator = action.deployed_from_indicator;
3838 let mut task = self.code_actions_task.take();
3839 cx.spawn(|this, mut cx| async move {
3840 while let Some(prev_task) = task {
3841 prev_task.await;
3842 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3843 }
3844
3845 this.update(&mut cx, |this, cx| {
3846 if this.focus_handle.is_focused(cx) {
3847 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3848 this.completion_tasks.clear();
3849 this.discard_copilot_suggestion(cx);
3850 *this.context_menu.write() =
3851 Some(ContextMenu::CodeActions(CodeActionsMenu {
3852 buffer,
3853 actions,
3854 selected_item: Default::default(),
3855 scroll_handle: UniformListScrollHandle::default(),
3856 deployed_from_indicator,
3857 }));
3858 cx.notify();
3859 }
3860 }
3861 })?;
3862
3863 Ok::<_, anyhow::Error>(())
3864 })
3865 .detach_and_log_err(cx);
3866 }
3867
3868 pub fn confirm_code_action(
3869 &mut self,
3870 action: &ConfirmCodeAction,
3871 cx: &mut ViewContext<Self>,
3872 ) -> Option<Task<Result<()>>> {
3873 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
3874 menu
3875 } else {
3876 return None;
3877 };
3878 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3879 let action = actions_menu.actions.get(action_ix)?.clone();
3880 let title = action.lsp_action.title.clone();
3881 let buffer = actions_menu.buffer;
3882 let workspace = self.workspace()?;
3883
3884 let apply_code_actions = workspace
3885 .read(cx)
3886 .project()
3887 .clone()
3888 .update(cx, |project, cx| {
3889 project.apply_code_action(buffer, action, true, cx)
3890 });
3891 let workspace = workspace.downgrade();
3892 Some(cx.spawn(|editor, cx| async move {
3893 let project_transaction = apply_code_actions.await?;
3894 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3895 }))
3896 }
3897
3898 async fn open_project_transaction(
3899 this: &WeakView<Editor>,
3900 workspace: WeakView<Workspace>,
3901 transaction: ProjectTransaction,
3902 title: String,
3903 mut cx: AsyncWindowContext,
3904 ) -> Result<()> {
3905 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
3906
3907 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3908 cx.update(|_, cx| {
3909 entries.sort_unstable_by_key(|(buffer, _)| {
3910 buffer.read(cx).file().map(|f| f.path().clone())
3911 });
3912 })?;
3913
3914 // If the project transaction's edits are all contained within this editor, then
3915 // avoid opening a new editor to display them.
3916
3917 if let Some((buffer, transaction)) = entries.first() {
3918 if entries.len() == 1 {
3919 let excerpt = this.update(&mut cx, |editor, cx| {
3920 editor
3921 .buffer()
3922 .read(cx)
3923 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3924 })?;
3925 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3926 if excerpted_buffer == *buffer {
3927 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3928 let excerpt_range = excerpt_range.to_offset(buffer);
3929 buffer
3930 .edited_ranges_for_transaction::<usize>(transaction)
3931 .all(|range| {
3932 excerpt_range.start <= range.start
3933 && excerpt_range.end >= range.end
3934 })
3935 })?;
3936
3937 if all_edits_within_excerpt {
3938 return Ok(());
3939 }
3940 }
3941 }
3942 }
3943 } else {
3944 return Ok(());
3945 }
3946
3947 let mut ranges_to_highlight = Vec::new();
3948 let excerpt_buffer = cx.build_model(|cx| {
3949 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3950 for (buffer_handle, transaction) in &entries {
3951 let buffer = buffer_handle.read(cx);
3952 ranges_to_highlight.extend(
3953 multibuffer.push_excerpts_with_context_lines(
3954 buffer_handle.clone(),
3955 buffer
3956 .edited_ranges_for_transaction::<usize>(transaction)
3957 .collect(),
3958 1,
3959 cx,
3960 ),
3961 );
3962 }
3963 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3964 multibuffer
3965 })?;
3966
3967 workspace.update(&mut cx, |workspace, cx| {
3968 let project = workspace.project().clone();
3969 let editor =
3970 cx.build_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3971 workspace.add_item(Box::new(editor.clone()), cx);
3972 editor.update(cx, |editor, cx| {
3973 editor.highlight_background::<Self>(
3974 ranges_to_highlight,
3975 |theme| theme.editor_highlighted_line_background,
3976 cx,
3977 );
3978 });
3979 })?;
3980
3981 Ok(())
3982 }
3983
3984 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3985 let project = self.project.clone()?;
3986 let buffer = self.buffer.read(cx);
3987 let newest_selection = self.selections.newest_anchor().clone();
3988 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3989 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3990 if start_buffer != end_buffer {
3991 return None;
3992 }
3993
3994 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3995 cx.background_executor()
3996 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
3997 .await;
3998
3999 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4000 project.code_actions(&start_buffer, start..end, cx)
4001 }) {
4002 code_actions.await.log_err()
4003 } else {
4004 None
4005 };
4006
4007 this.update(&mut cx, |this, cx| {
4008 this.available_code_actions = actions.and_then(|actions| {
4009 if actions.is_empty() {
4010 None
4011 } else {
4012 Some((start_buffer, actions.into()))
4013 }
4014 });
4015 cx.notify();
4016 })
4017 .log_err();
4018 }));
4019 None
4020 }
4021
4022 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4023 if self.pending_rename.is_some() {
4024 return None;
4025 }
4026
4027 let project = self.project.clone()?;
4028 let buffer = self.buffer.read(cx);
4029 let newest_selection = self.selections.newest_anchor().clone();
4030 let cursor_position = newest_selection.head();
4031 let (cursor_buffer, cursor_buffer_position) =
4032 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
4033 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4034 if cursor_buffer != tail_buffer {
4035 return None;
4036 }
4037
4038 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4039 cx.background_executor()
4040 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4041 .await;
4042
4043 let highlights = if let Some(highlights) = project
4044 .update(&mut cx, |project, cx| {
4045 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4046 })
4047 .log_err()
4048 {
4049 highlights.await.log_err()
4050 } else {
4051 None
4052 };
4053
4054 if let Some(highlights) = highlights {
4055 this.update(&mut cx, |this, cx| {
4056 if this.pending_rename.is_some() {
4057 return;
4058 }
4059
4060 let buffer_id = cursor_position.buffer_id;
4061 let buffer = this.buffer.read(cx);
4062 if !buffer
4063 .text_anchor_for_position(cursor_position, cx)
4064 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4065 {
4066 return;
4067 }
4068
4069 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4070 let mut write_ranges = Vec::new();
4071 let mut read_ranges = Vec::new();
4072 for highlight in highlights {
4073 for (excerpt_id, excerpt_range) in
4074 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4075 {
4076 let start = highlight
4077 .range
4078 .start
4079 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4080 let end = highlight
4081 .range
4082 .end
4083 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4084 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4085 continue;
4086 }
4087
4088 let range = Anchor {
4089 buffer_id,
4090 excerpt_id: excerpt_id.clone(),
4091 text_anchor: start,
4092 }..Anchor {
4093 buffer_id,
4094 excerpt_id,
4095 text_anchor: end,
4096 };
4097 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4098 write_ranges.push(range);
4099 } else {
4100 read_ranges.push(range);
4101 }
4102 }
4103 }
4104
4105 this.highlight_background::<DocumentHighlightRead>(
4106 read_ranges,
4107 |theme| theme.editor_document_highlight_read_background,
4108 cx,
4109 );
4110 this.highlight_background::<DocumentHighlightWrite>(
4111 write_ranges,
4112 |theme| theme.editor_document_highlight_write_background,
4113 cx,
4114 );
4115 cx.notify();
4116 })
4117 .log_err();
4118 }
4119 }));
4120 None
4121 }
4122
4123 fn refresh_copilot_suggestions(
4124 &mut self,
4125 debounce: bool,
4126 cx: &mut ViewContext<Self>,
4127 ) -> Option<()> {
4128 let copilot = Copilot::global(cx)?;
4129 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4130 self.clear_copilot_suggestions(cx);
4131 return None;
4132 }
4133 self.update_visible_copilot_suggestion(cx);
4134
4135 let snapshot = self.buffer.read(cx).snapshot(cx);
4136 let cursor = self.selections.newest_anchor().head();
4137 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
4138 self.clear_copilot_suggestions(cx);
4139 return None;
4140 }
4141
4142 let (buffer, buffer_position) =
4143 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4144 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
4145 if debounce {
4146 cx.background_executor()
4147 .timer(COPILOT_DEBOUNCE_TIMEOUT)
4148 .await;
4149 }
4150
4151 let completions = copilot
4152 .update(&mut cx, |copilot, cx| {
4153 copilot.completions(&buffer, buffer_position, cx)
4154 })
4155 .log_err()
4156 .unwrap_or(Task::ready(Ok(Vec::new())))
4157 .await
4158 .log_err()
4159 .into_iter()
4160 .flatten()
4161 .collect_vec();
4162
4163 this.update(&mut cx, |this, cx| {
4164 if !completions.is_empty() {
4165 this.copilot_state.cycled = false;
4166 this.copilot_state.pending_cycling_refresh = Task::ready(None);
4167 this.copilot_state.completions.clear();
4168 this.copilot_state.active_completion_index = 0;
4169 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
4170 for completion in completions {
4171 this.copilot_state.push_completion(completion);
4172 }
4173 this.update_visible_copilot_suggestion(cx);
4174 }
4175 })
4176 .log_err()?;
4177 Some(())
4178 });
4179
4180 Some(())
4181 }
4182
4183 fn cycle_copilot_suggestions(
4184 &mut self,
4185 direction: Direction,
4186 cx: &mut ViewContext<Self>,
4187 ) -> Option<()> {
4188 let copilot = Copilot::global(cx)?;
4189 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4190 return None;
4191 }
4192
4193 if self.copilot_state.cycled {
4194 self.copilot_state.cycle_completions(direction);
4195 self.update_visible_copilot_suggestion(cx);
4196 } else {
4197 let cursor = self.selections.newest_anchor().head();
4198 let (buffer, buffer_position) =
4199 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4200 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
4201 let completions = copilot
4202 .update(&mut cx, |copilot, cx| {
4203 copilot.completions_cycling(&buffer, buffer_position, cx)
4204 })
4205 .log_err()?
4206 .await;
4207
4208 this.update(&mut cx, |this, cx| {
4209 this.copilot_state.cycled = true;
4210 for completion in completions.log_err().into_iter().flatten() {
4211 this.copilot_state.push_completion(completion);
4212 }
4213 this.copilot_state.cycle_completions(direction);
4214 this.update_visible_copilot_suggestion(cx);
4215 })
4216 .log_err()?;
4217
4218 Some(())
4219 });
4220 }
4221
4222 Some(())
4223 }
4224
4225 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
4226 if !self.has_active_copilot_suggestion(cx) {
4227 self.refresh_copilot_suggestions(false, cx);
4228 return;
4229 }
4230
4231 self.update_visible_copilot_suggestion(cx);
4232 }
4233
4234 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
4235 if self.has_active_copilot_suggestion(cx) {
4236 self.cycle_copilot_suggestions(Direction::Next, cx);
4237 } else {
4238 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
4239 if is_copilot_disabled {
4240 cx.propagate();
4241 }
4242 }
4243 }
4244
4245 fn previous_copilot_suggestion(
4246 &mut self,
4247 _: &copilot::PreviousSuggestion,
4248 cx: &mut ViewContext<Self>,
4249 ) {
4250 if self.has_active_copilot_suggestion(cx) {
4251 self.cycle_copilot_suggestions(Direction::Prev, cx);
4252 } else {
4253 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
4254 if is_copilot_disabled {
4255 cx.propagate();
4256 }
4257 }
4258 }
4259
4260 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4261 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4262 if let Some((copilot, completion)) =
4263 Copilot::global(cx).zip(self.copilot_state.active_completion())
4264 {
4265 copilot
4266 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
4267 .detach_and_log_err(cx);
4268
4269 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
4270 }
4271 cx.emit(EditorEvent::InputHandled {
4272 utf16_range_to_replace: None,
4273 text: suggestion.text.to_string().into(),
4274 });
4275 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
4276 cx.notify();
4277 true
4278 } else {
4279 false
4280 }
4281 }
4282
4283 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4284 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4285 if let Some(copilot) = Copilot::global(cx) {
4286 copilot
4287 .update(cx, |copilot, cx| {
4288 copilot.discard_completions(&self.copilot_state.completions, cx)
4289 })
4290 .detach_and_log_err(cx);
4291
4292 self.report_copilot_event(None, false, cx)
4293 }
4294
4295 self.display_map.update(cx, |map, cx| {
4296 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
4297 });
4298 cx.notify();
4299 true
4300 } else {
4301 false
4302 }
4303 }
4304
4305 fn is_copilot_enabled_at(
4306 &self,
4307 location: Anchor,
4308 snapshot: &MultiBufferSnapshot,
4309 cx: &mut ViewContext<Self>,
4310 ) -> bool {
4311 let file = snapshot.file_at(location);
4312 let language = snapshot.language_at(location);
4313 let settings = all_language_settings(file, cx);
4314 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
4315 }
4316
4317 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
4318 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
4319 let buffer = self.buffer.read(cx).read(cx);
4320 suggestion.position.is_valid(&buffer)
4321 } else {
4322 false
4323 }
4324 }
4325
4326 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4327 let suggestion = self.copilot_state.suggestion.take()?;
4328 self.display_map.update(cx, |map, cx| {
4329 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
4330 });
4331 let buffer = self.buffer.read(cx).read(cx);
4332
4333 if suggestion.position.is_valid(&buffer) {
4334 Some(suggestion)
4335 } else {
4336 None
4337 }
4338 }
4339
4340 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
4341 let snapshot = self.buffer.read(cx).snapshot(cx);
4342 let selection = self.selections.newest_anchor();
4343 let cursor = selection.head();
4344
4345 if self.context_menu.read().is_some()
4346 || !self.completion_tasks.is_empty()
4347 || selection.start != selection.end
4348 {
4349 self.discard_copilot_suggestion(cx);
4350 } else if let Some(text) = self
4351 .copilot_state
4352 .text_for_active_completion(cursor, &snapshot)
4353 {
4354 let text = Rope::from(text);
4355 let mut to_remove = Vec::new();
4356 if let Some(suggestion) = self.copilot_state.suggestion.take() {
4357 to_remove.push(suggestion.id);
4358 }
4359
4360 let suggestion_inlay =
4361 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4362 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
4363 self.display_map.update(cx, move |map, cx| {
4364 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
4365 });
4366 cx.notify();
4367 } else {
4368 self.discard_copilot_suggestion(cx);
4369 }
4370 }
4371
4372 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
4373 self.copilot_state = Default::default();
4374 self.discard_copilot_suggestion(cx);
4375 }
4376
4377 pub fn render_code_actions_indicator(
4378 &self,
4379 style: &EditorStyle,
4380 is_active: bool,
4381 cx: &mut ViewContext<Self>,
4382 ) -> Option<IconButton> {
4383 if self.available_code_actions.is_some() {
4384 Some(
4385 IconButton::new("code_actions_indicator", ui::Icon::Bolt).on_click(cx.listener(
4386 |editor, e, cx| {
4387 editor.toggle_code_actions(
4388 &ToggleCodeActions {
4389 deployed_from_indicator: true,
4390 },
4391 cx,
4392 );
4393 },
4394 )),
4395 )
4396 } else {
4397 None
4398 }
4399 }
4400
4401 pub fn render_fold_indicators(
4402 &self,
4403 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
4404 style: &EditorStyle,
4405 gutter_hovered: bool,
4406 line_height: Pixels,
4407 gutter_margin: Pixels,
4408 cx: &mut ViewContext<Self>,
4409 ) -> Vec<Option<IconButton>> {
4410 fold_data
4411 .iter()
4412 .enumerate()
4413 .map(|(ix, fold_data)| {
4414 fold_data
4415 .map(|(fold_status, buffer_row, active)| {
4416 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4417 let icon = match fold_status {
4418 FoldStatus::Folded => ui::Icon::ChevronRight,
4419 FoldStatus::Foldable => ui::Icon::ChevronDown,
4420 };
4421 IconButton::new(ix as usize, icon)
4422 .on_click(cx.listener(move |editor, e, cx| match fold_status {
4423 FoldStatus::Folded => {
4424 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
4425 }
4426 FoldStatus::Foldable => {
4427 editor.fold_at(&FoldAt { buffer_row }, cx);
4428 }
4429 }))
4430 .color(ui::Color::Muted)
4431 })
4432 })
4433 .flatten()
4434 })
4435 .collect()
4436 }
4437
4438 pub fn context_menu_visible(&self) -> bool {
4439 self.context_menu
4440 .read()
4441 .as_ref()
4442 .map_or(false, |menu| menu.visible())
4443 }
4444
4445 pub fn render_context_menu(
4446 &self,
4447 cursor_position: DisplayPoint,
4448 style: &EditorStyle,
4449 cx: &mut ViewContext<Editor>,
4450 ) -> Option<(DisplayPoint, AnyElement)> {
4451 self.context_menu.read().as_ref().map(|menu| {
4452 menu.render(
4453 cursor_position,
4454 style,
4455 self.workspace.as_ref().map(|(w, _)| w.clone()),
4456 cx,
4457 )
4458 })
4459 }
4460
4461 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4462 cx.notify();
4463 self.completion_tasks.clear();
4464 let context_menu = self.context_menu.write().take();
4465 if context_menu.is_some() {
4466 self.update_visible_copilot_suggestion(cx);
4467 }
4468 context_menu
4469 }
4470
4471 pub fn insert_snippet(
4472 &mut self,
4473 insertion_ranges: &[Range<usize>],
4474 snippet: Snippet,
4475 cx: &mut ViewContext<Self>,
4476 ) -> Result<()> {
4477 let tabstops = self.buffer.update(cx, |buffer, cx| {
4478 let snippet_text: Arc<str> = snippet.text.clone().into();
4479 buffer.edit(
4480 insertion_ranges
4481 .iter()
4482 .cloned()
4483 .map(|range| (range, snippet_text.clone())),
4484 Some(AutoindentMode::EachLine),
4485 cx,
4486 );
4487
4488 let snapshot = &*buffer.read(cx);
4489 let snippet = &snippet;
4490 snippet
4491 .tabstops
4492 .iter()
4493 .map(|tabstop| {
4494 let mut tabstop_ranges = tabstop
4495 .iter()
4496 .flat_map(|tabstop_range| {
4497 let mut delta = 0_isize;
4498 insertion_ranges.iter().map(move |insertion_range| {
4499 let insertion_start = insertion_range.start as isize + delta;
4500 delta +=
4501 snippet.text.len() as isize - insertion_range.len() as isize;
4502
4503 let start = snapshot.anchor_before(
4504 (insertion_start + tabstop_range.start) as usize,
4505 );
4506 let end = snapshot
4507 .anchor_after((insertion_start + tabstop_range.end) as usize);
4508 start..end
4509 })
4510 })
4511 .collect::<Vec<_>>();
4512 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4513 tabstop_ranges
4514 })
4515 .collect::<Vec<_>>()
4516 });
4517
4518 if let Some(tabstop) = tabstops.first() {
4519 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4520 s.select_ranges(tabstop.iter().cloned());
4521 });
4522 self.snippet_stack.push(SnippetState {
4523 active_index: 0,
4524 ranges: tabstops,
4525 });
4526 }
4527
4528 Ok(())
4529 }
4530
4531 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4532 self.move_to_snippet_tabstop(Bias::Right, cx)
4533 }
4534
4535 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4536 self.move_to_snippet_tabstop(Bias::Left, cx)
4537 }
4538
4539 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4540 if let Some(mut snippet) = self.snippet_stack.pop() {
4541 match bias {
4542 Bias::Left => {
4543 if snippet.active_index > 0 {
4544 snippet.active_index -= 1;
4545 } else {
4546 self.snippet_stack.push(snippet);
4547 return false;
4548 }
4549 }
4550 Bias::Right => {
4551 if snippet.active_index + 1 < snippet.ranges.len() {
4552 snippet.active_index += 1;
4553 } else {
4554 self.snippet_stack.push(snippet);
4555 return false;
4556 }
4557 }
4558 }
4559 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4560 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4561 s.select_anchor_ranges(current_ranges.iter().cloned())
4562 });
4563 // If snippet state is not at the last tabstop, push it back on the stack
4564 if snippet.active_index + 1 < snippet.ranges.len() {
4565 self.snippet_stack.push(snippet);
4566 }
4567 return true;
4568 }
4569 }
4570
4571 false
4572 }
4573
4574 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4575 self.transact(cx, |this, cx| {
4576 this.select_all(&SelectAll, cx);
4577 this.insert("", cx);
4578 });
4579 }
4580
4581 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4582 self.transact(cx, |this, cx| {
4583 this.select_autoclose_pair(cx);
4584 let mut selections = this.selections.all::<Point>(cx);
4585 if !this.selections.line_mode {
4586 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4587 for selection in &mut selections {
4588 if selection.is_empty() {
4589 let old_head = selection.head();
4590 let mut new_head =
4591 movement::left(&display_map, old_head.to_display_point(&display_map))
4592 .to_point(&display_map);
4593 if let Some((buffer, line_buffer_range)) = display_map
4594 .buffer_snapshot
4595 .buffer_line_for_row(old_head.row)
4596 {
4597 let indent_size =
4598 buffer.indent_size_for_line(line_buffer_range.start.row);
4599 let indent_len = match indent_size.kind {
4600 IndentKind::Space => {
4601 buffer.settings_at(line_buffer_range.start, cx).tab_size
4602 }
4603 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4604 };
4605 if old_head.column <= indent_size.len && old_head.column > 0 {
4606 let indent_len = indent_len.get();
4607 new_head = cmp::min(
4608 new_head,
4609 Point::new(
4610 old_head.row,
4611 ((old_head.column - 1) / indent_len) * indent_len,
4612 ),
4613 );
4614 }
4615 }
4616
4617 selection.set_head(new_head, SelectionGoal::None);
4618 }
4619 }
4620 }
4621
4622 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4623 this.insert("", cx);
4624 this.refresh_copilot_suggestions(true, cx);
4625 });
4626 }
4627
4628 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4629 self.transact(cx, |this, cx| {
4630 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4631 let line_mode = s.line_mode;
4632 s.move_with(|map, selection| {
4633 if selection.is_empty() && !line_mode {
4634 let cursor = movement::right(map, selection.head());
4635 selection.end = cursor;
4636 selection.reversed = true;
4637 selection.goal = SelectionGoal::None;
4638 }
4639 })
4640 });
4641 this.insert("", cx);
4642 this.refresh_copilot_suggestions(true, cx);
4643 });
4644 }
4645
4646 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4647 if self.move_to_prev_snippet_tabstop(cx) {
4648 return;
4649 }
4650
4651 self.outdent(&Outdent, cx);
4652 }
4653
4654 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4655 if self.move_to_next_snippet_tabstop(cx) {
4656 return;
4657 }
4658
4659 let mut selections = self.selections.all_adjusted(cx);
4660 let buffer = self.buffer.read(cx);
4661 let snapshot = buffer.snapshot(cx);
4662 let rows_iter = selections.iter().map(|s| s.head().row);
4663 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4664
4665 let mut edits = Vec::new();
4666 let mut prev_edited_row = 0;
4667 let mut row_delta = 0;
4668 for selection in &mut selections {
4669 if selection.start.row != prev_edited_row {
4670 row_delta = 0;
4671 }
4672 prev_edited_row = selection.end.row;
4673
4674 // If the selection is non-empty, then increase the indentation of the selected lines.
4675 if !selection.is_empty() {
4676 row_delta =
4677 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4678 continue;
4679 }
4680
4681 // If the selection is empty and the cursor is in the leading whitespace before the
4682 // suggested indentation, then auto-indent the line.
4683 let cursor = selection.head();
4684 let current_indent = snapshot.indent_size_for_line(cursor.row);
4685 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4686 if cursor.column < suggested_indent.len
4687 && cursor.column <= current_indent.len
4688 && current_indent.len <= suggested_indent.len
4689 {
4690 selection.start = Point::new(cursor.row, suggested_indent.len);
4691 selection.end = selection.start;
4692 if row_delta == 0 {
4693 edits.extend(Buffer::edit_for_indent_size_adjustment(
4694 cursor.row,
4695 current_indent,
4696 suggested_indent,
4697 ));
4698 row_delta = suggested_indent.len - current_indent.len;
4699 }
4700 continue;
4701 }
4702 }
4703
4704 // Accept copilot suggestion if there is only one selection and the cursor is not
4705 // in the leading whitespace.
4706 if self.selections.count() == 1
4707 && cursor.column >= current_indent.len
4708 && self.has_active_copilot_suggestion(cx)
4709 {
4710 self.accept_copilot_suggestion(cx);
4711 return;
4712 }
4713
4714 // Otherwise, insert a hard or soft tab.
4715 let settings = buffer.settings_at(cursor, cx);
4716 let tab_size = if settings.hard_tabs {
4717 IndentSize::tab()
4718 } else {
4719 let tab_size = settings.tab_size.get();
4720 let char_column = snapshot
4721 .text_for_range(Point::new(cursor.row, 0)..cursor)
4722 .flat_map(str::chars)
4723 .count()
4724 + row_delta as usize;
4725 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4726 IndentSize::spaces(chars_to_next_tab_stop)
4727 };
4728 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4729 selection.end = selection.start;
4730 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4731 row_delta += tab_size.len;
4732 }
4733
4734 self.transact(cx, |this, cx| {
4735 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4736 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4737 this.refresh_copilot_suggestions(true, cx);
4738 });
4739 }
4740
4741 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4742 let mut selections = self.selections.all::<Point>(cx);
4743 let mut prev_edited_row = 0;
4744 let mut row_delta = 0;
4745 let mut edits = Vec::new();
4746 let buffer = self.buffer.read(cx);
4747 let snapshot = buffer.snapshot(cx);
4748 for selection in &mut selections {
4749 if selection.start.row != prev_edited_row {
4750 row_delta = 0;
4751 }
4752 prev_edited_row = selection.end.row;
4753
4754 row_delta =
4755 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4756 }
4757
4758 self.transact(cx, |this, cx| {
4759 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4760 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4761 });
4762 }
4763
4764 fn indent_selection(
4765 buffer: &MultiBuffer,
4766 snapshot: &MultiBufferSnapshot,
4767 selection: &mut Selection<Point>,
4768 edits: &mut Vec<(Range<Point>, String)>,
4769 delta_for_start_row: u32,
4770 cx: &AppContext,
4771 ) -> u32 {
4772 let settings = buffer.settings_at(selection.start, cx);
4773 let tab_size = settings.tab_size.get();
4774 let indent_kind = if settings.hard_tabs {
4775 IndentKind::Tab
4776 } else {
4777 IndentKind::Space
4778 };
4779 let mut start_row = selection.start.row;
4780 let mut end_row = selection.end.row + 1;
4781
4782 // If a selection ends at the beginning of a line, don't indent
4783 // that last line.
4784 if selection.end.column == 0 {
4785 end_row -= 1;
4786 }
4787
4788 // Avoid re-indenting a row that has already been indented by a
4789 // previous selection, but still update this selection's column
4790 // to reflect that indentation.
4791 if delta_for_start_row > 0 {
4792 start_row += 1;
4793 selection.start.column += delta_for_start_row;
4794 if selection.end.row == selection.start.row {
4795 selection.end.column += delta_for_start_row;
4796 }
4797 }
4798
4799 let mut delta_for_end_row = 0;
4800 for row in start_row..end_row {
4801 let current_indent = snapshot.indent_size_for_line(row);
4802 let indent_delta = match (current_indent.kind, indent_kind) {
4803 (IndentKind::Space, IndentKind::Space) => {
4804 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4805 IndentSize::spaces(columns_to_next_tab_stop)
4806 }
4807 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4808 (_, IndentKind::Tab) => IndentSize::tab(),
4809 };
4810
4811 let row_start = Point::new(row, 0);
4812 edits.push((
4813 row_start..row_start,
4814 indent_delta.chars().collect::<String>(),
4815 ));
4816
4817 // Update this selection's endpoints to reflect the indentation.
4818 if row == selection.start.row {
4819 selection.start.column += indent_delta.len;
4820 }
4821 if row == selection.end.row {
4822 selection.end.column += indent_delta.len;
4823 delta_for_end_row = indent_delta.len;
4824 }
4825 }
4826
4827 if selection.start.row == selection.end.row {
4828 delta_for_start_row + delta_for_end_row
4829 } else {
4830 delta_for_end_row
4831 }
4832 }
4833
4834 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4835 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4836 let selections = self.selections.all::<Point>(cx);
4837 let mut deletion_ranges = Vec::new();
4838 let mut last_outdent = None;
4839 {
4840 let buffer = self.buffer.read(cx);
4841 let snapshot = buffer.snapshot(cx);
4842 for selection in &selections {
4843 let settings = buffer.settings_at(selection.start, cx);
4844 let tab_size = settings.tab_size.get();
4845 let mut rows = selection.spanned_rows(false, &display_map);
4846
4847 // Avoid re-outdenting a row that has already been outdented by a
4848 // previous selection.
4849 if let Some(last_row) = last_outdent {
4850 if last_row == rows.start {
4851 rows.start += 1;
4852 }
4853 }
4854
4855 for row in rows {
4856 let indent_size = snapshot.indent_size_for_line(row);
4857 if indent_size.len > 0 {
4858 let deletion_len = match indent_size.kind {
4859 IndentKind::Space => {
4860 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4861 if columns_to_prev_tab_stop == 0 {
4862 tab_size
4863 } else {
4864 columns_to_prev_tab_stop
4865 }
4866 }
4867 IndentKind::Tab => 1,
4868 };
4869 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4870 last_outdent = Some(row);
4871 }
4872 }
4873 }
4874 }
4875
4876 self.transact(cx, |this, cx| {
4877 this.buffer.update(cx, |buffer, cx| {
4878 let empty_str: Arc<str> = "".into();
4879 buffer.edit(
4880 deletion_ranges
4881 .into_iter()
4882 .map(|range| (range, empty_str.clone())),
4883 None,
4884 cx,
4885 );
4886 });
4887 let selections = this.selections.all::<usize>(cx);
4888 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4889 });
4890 }
4891
4892 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4893 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4894 let selections = self.selections.all::<Point>(cx);
4895
4896 let mut new_cursors = Vec::new();
4897 let mut edit_ranges = Vec::new();
4898 let mut selections = selections.iter().peekable();
4899 while let Some(selection) = selections.next() {
4900 let mut rows = selection.spanned_rows(false, &display_map);
4901 let goal_display_column = selection.head().to_display_point(&display_map).column();
4902
4903 // Accumulate contiguous regions of rows that we want to delete.
4904 while let Some(next_selection) = selections.peek() {
4905 let next_rows = next_selection.spanned_rows(false, &display_map);
4906 if next_rows.start <= rows.end {
4907 rows.end = next_rows.end;
4908 selections.next().unwrap();
4909 } else {
4910 break;
4911 }
4912 }
4913
4914 let buffer = &display_map.buffer_snapshot;
4915 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4916 let edit_end;
4917 let cursor_buffer_row;
4918 if buffer.max_point().row >= rows.end {
4919 // If there's a line after the range, delete the \n from the end of the row range
4920 // and position the cursor on the next line.
4921 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4922 cursor_buffer_row = rows.end;
4923 } else {
4924 // If there isn't a line after the range, delete the \n from the line before the
4925 // start of the row range and position the cursor there.
4926 edit_start = edit_start.saturating_sub(1);
4927 edit_end = buffer.len();
4928 cursor_buffer_row = rows.start.saturating_sub(1);
4929 }
4930
4931 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4932 *cursor.column_mut() =
4933 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4934
4935 new_cursors.push((
4936 selection.id,
4937 buffer.anchor_after(cursor.to_point(&display_map)),
4938 ));
4939 edit_ranges.push(edit_start..edit_end);
4940 }
4941
4942 self.transact(cx, |this, cx| {
4943 let buffer = this.buffer.update(cx, |buffer, cx| {
4944 let empty_str: Arc<str> = "".into();
4945 buffer.edit(
4946 edit_ranges
4947 .into_iter()
4948 .map(|range| (range, empty_str.clone())),
4949 None,
4950 cx,
4951 );
4952 buffer.snapshot(cx)
4953 });
4954 let new_selections = new_cursors
4955 .into_iter()
4956 .map(|(id, cursor)| {
4957 let cursor = cursor.to_point(&buffer);
4958 Selection {
4959 id,
4960 start: cursor,
4961 end: cursor,
4962 reversed: false,
4963 goal: SelectionGoal::None,
4964 }
4965 })
4966 .collect();
4967
4968 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4969 s.select(new_selections);
4970 });
4971 });
4972 }
4973
4974 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4975 let mut row_ranges = Vec::<Range<u32>>::new();
4976 for selection in self.selections.all::<Point>(cx) {
4977 let start = selection.start.row;
4978 let end = if selection.start.row == selection.end.row {
4979 selection.start.row + 1
4980 } else {
4981 selection.end.row
4982 };
4983
4984 if let Some(last_row_range) = row_ranges.last_mut() {
4985 if start <= last_row_range.end {
4986 last_row_range.end = end;
4987 continue;
4988 }
4989 }
4990 row_ranges.push(start..end);
4991 }
4992
4993 let snapshot = self.buffer.read(cx).snapshot(cx);
4994 let mut cursor_positions = Vec::new();
4995 for row_range in &row_ranges {
4996 let anchor = snapshot.anchor_before(Point::new(
4997 row_range.end - 1,
4998 snapshot.line_len(row_range.end - 1),
4999 ));
5000 cursor_positions.push(anchor.clone()..anchor);
5001 }
5002
5003 self.transact(cx, |this, cx| {
5004 for row_range in row_ranges.into_iter().rev() {
5005 for row in row_range.rev() {
5006 let end_of_line = Point::new(row, snapshot.line_len(row));
5007 let indent = snapshot.indent_size_for_line(row + 1);
5008 let start_of_next_line = Point::new(row + 1, indent.len);
5009
5010 let replace = if snapshot.line_len(row + 1) > indent.len {
5011 " "
5012 } else {
5013 ""
5014 };
5015
5016 this.buffer.update(cx, |buffer, cx| {
5017 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5018 });
5019 }
5020 }
5021
5022 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5023 s.select_anchor_ranges(cursor_positions)
5024 });
5025 });
5026 }
5027
5028 pub fn sort_lines_case_sensitive(
5029 &mut self,
5030 _: &SortLinesCaseSensitive,
5031 cx: &mut ViewContext<Self>,
5032 ) {
5033 self.manipulate_lines(cx, |lines| lines.sort())
5034 }
5035
5036 pub fn sort_lines_case_insensitive(
5037 &mut self,
5038 _: &SortLinesCaseInsensitive,
5039 cx: &mut ViewContext<Self>,
5040 ) {
5041 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5042 }
5043
5044 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5045 self.manipulate_lines(cx, |lines| lines.reverse())
5046 }
5047
5048 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5049 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5050 }
5051
5052 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5053 where
5054 Fn: FnMut(&mut [&str]),
5055 {
5056 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5057 let buffer = self.buffer.read(cx).snapshot(cx);
5058
5059 let mut edits = Vec::new();
5060
5061 let selections = self.selections.all::<Point>(cx);
5062 let mut selections = selections.iter().peekable();
5063 let mut contiguous_row_selections = Vec::new();
5064 let mut new_selections = Vec::new();
5065
5066 while let Some(selection) = selections.next() {
5067 let (start_row, end_row) = consume_contiguous_rows(
5068 &mut contiguous_row_selections,
5069 selection,
5070 &display_map,
5071 &mut selections,
5072 );
5073
5074 let start_point = Point::new(start_row, 0);
5075 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
5076 let text = buffer
5077 .text_for_range(start_point..end_point)
5078 .collect::<String>();
5079 let mut lines = text.split("\n").collect_vec();
5080
5081 let lines_len = lines.len();
5082 callback(&mut lines);
5083
5084 // This is a current limitation with selections.
5085 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
5086 debug_assert!(
5087 lines.len() == lines_len,
5088 "callback should not change the number of lines"
5089 );
5090
5091 edits.push((start_point..end_point, lines.join("\n")));
5092 let start_anchor = buffer.anchor_after(start_point);
5093 let end_anchor = buffer.anchor_before(end_point);
5094
5095 // Make selection and push
5096 new_selections.push(Selection {
5097 id: selection.id,
5098 start: start_anchor.to_offset(&buffer),
5099 end: end_anchor.to_offset(&buffer),
5100 goal: SelectionGoal::None,
5101 reversed: selection.reversed,
5102 });
5103 }
5104
5105 self.transact(cx, |this, cx| {
5106 this.buffer.update(cx, |buffer, cx| {
5107 buffer.edit(edits, None, cx);
5108 });
5109
5110 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5111 s.select(new_selections);
5112 });
5113
5114 this.request_autoscroll(Autoscroll::fit(), cx);
5115 });
5116 }
5117
5118 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5119 self.manipulate_text(cx, |text| text.to_uppercase())
5120 }
5121
5122 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5123 self.manipulate_text(cx, |text| text.to_lowercase())
5124 }
5125
5126 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5127 self.manipulate_text(cx, |text| {
5128 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5129 // https://github.com/rutrum/convert-case/issues/16
5130 text.split("\n")
5131 .map(|line| line.to_case(Case::Title))
5132 .join("\n")
5133 })
5134 }
5135
5136 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5137 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5138 }
5139
5140 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5141 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5142 }
5143
5144 pub fn convert_to_upper_camel_case(
5145 &mut self,
5146 _: &ConvertToUpperCamelCase,
5147 cx: &mut ViewContext<Self>,
5148 ) {
5149 self.manipulate_text(cx, |text| {
5150 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5151 // https://github.com/rutrum/convert-case/issues/16
5152 text.split("\n")
5153 .map(|line| line.to_case(Case::UpperCamel))
5154 .join("\n")
5155 })
5156 }
5157
5158 pub fn convert_to_lower_camel_case(
5159 &mut self,
5160 _: &ConvertToLowerCamelCase,
5161 cx: &mut ViewContext<Self>,
5162 ) {
5163 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5164 }
5165
5166 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5167 where
5168 Fn: FnMut(&str) -> String,
5169 {
5170 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5171 let buffer = self.buffer.read(cx).snapshot(cx);
5172
5173 let mut new_selections = Vec::new();
5174 let mut edits = Vec::new();
5175 let mut selection_adjustment = 0i32;
5176
5177 for selection in self.selections.all::<usize>(cx) {
5178 let selection_is_empty = selection.is_empty();
5179
5180 let (start, end) = if selection_is_empty {
5181 let word_range = movement::surrounding_word(
5182 &display_map,
5183 selection.start.to_display_point(&display_map),
5184 );
5185 let start = word_range.start.to_offset(&display_map, Bias::Left);
5186 let end = word_range.end.to_offset(&display_map, Bias::Left);
5187 (start, end)
5188 } else {
5189 (selection.start, selection.end)
5190 };
5191
5192 let text = buffer.text_for_range(start..end).collect::<String>();
5193 let old_length = text.len() as i32;
5194 let text = callback(&text);
5195
5196 new_selections.push(Selection {
5197 start: (start as i32 - selection_adjustment) as usize,
5198 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
5199 goal: SelectionGoal::None,
5200 ..selection
5201 });
5202
5203 selection_adjustment += old_length - text.len() as i32;
5204
5205 edits.push((start..end, text));
5206 }
5207
5208 self.transact(cx, |this, cx| {
5209 this.buffer.update(cx, |buffer, cx| {
5210 buffer.edit(edits, None, cx);
5211 });
5212
5213 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5214 s.select(new_selections);
5215 });
5216
5217 this.request_autoscroll(Autoscroll::fit(), cx);
5218 });
5219 }
5220
5221 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
5222 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5223 let buffer = &display_map.buffer_snapshot;
5224 let selections = self.selections.all::<Point>(cx);
5225
5226 let mut edits = Vec::new();
5227 let mut selections_iter = selections.iter().peekable();
5228 while let Some(selection) = selections_iter.next() {
5229 // Avoid duplicating the same lines twice.
5230 let mut rows = selection.spanned_rows(false, &display_map);
5231
5232 while let Some(next_selection) = selections_iter.peek() {
5233 let next_rows = next_selection.spanned_rows(false, &display_map);
5234 if next_rows.start < rows.end {
5235 rows.end = next_rows.end;
5236 selections_iter.next().unwrap();
5237 } else {
5238 break;
5239 }
5240 }
5241
5242 // Copy the text from the selected row region and splice it at the start of the region.
5243 let start = Point::new(rows.start, 0);
5244 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
5245 let text = buffer
5246 .text_for_range(start..end)
5247 .chain(Some("\n"))
5248 .collect::<String>();
5249 edits.push((start..start, text));
5250 }
5251
5252 self.transact(cx, |this, cx| {
5253 this.buffer.update(cx, |buffer, cx| {
5254 buffer.edit(edits, None, cx);
5255 });
5256
5257 this.request_autoscroll(Autoscroll::fit(), cx);
5258 });
5259 }
5260
5261 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5263 let buffer = self.buffer.read(cx).snapshot(cx);
5264
5265 let mut edits = Vec::new();
5266 let mut unfold_ranges = Vec::new();
5267 let mut refold_ranges = Vec::new();
5268
5269 let selections = self.selections.all::<Point>(cx);
5270 let mut selections = selections.iter().peekable();
5271 let mut contiguous_row_selections = Vec::new();
5272 let mut new_selections = Vec::new();
5273
5274 while let Some(selection) = selections.next() {
5275 // Find all the selections that span a contiguous row range
5276 let (start_row, end_row) = consume_contiguous_rows(
5277 &mut contiguous_row_selections,
5278 selection,
5279 &display_map,
5280 &mut selections,
5281 );
5282
5283 // Move the text spanned by the row range to be before the line preceding the row range
5284 if start_row > 0 {
5285 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
5286 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
5287 let insertion_point = display_map
5288 .prev_line_boundary(Point::new(start_row - 1, 0))
5289 .0;
5290
5291 // Don't move lines across excerpts
5292 if buffer
5293 .excerpt_boundaries_in_range((
5294 Bound::Excluded(insertion_point),
5295 Bound::Included(range_to_move.end),
5296 ))
5297 .next()
5298 .is_none()
5299 {
5300 let text = buffer
5301 .text_for_range(range_to_move.clone())
5302 .flat_map(|s| s.chars())
5303 .skip(1)
5304 .chain(['\n'])
5305 .collect::<String>();
5306
5307 edits.push((
5308 buffer.anchor_after(range_to_move.start)
5309 ..buffer.anchor_before(range_to_move.end),
5310 String::new(),
5311 ));
5312 let insertion_anchor = buffer.anchor_after(insertion_point);
5313 edits.push((insertion_anchor..insertion_anchor, text));
5314
5315 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5316
5317 // Move selections up
5318 new_selections.extend(contiguous_row_selections.drain(..).map(
5319 |mut selection| {
5320 selection.start.row -= row_delta;
5321 selection.end.row -= row_delta;
5322 selection
5323 },
5324 ));
5325
5326 // Move folds up
5327 unfold_ranges.push(range_to_move.clone());
5328 for fold in display_map.folds_in_range(
5329 buffer.anchor_before(range_to_move.start)
5330 ..buffer.anchor_after(range_to_move.end),
5331 ) {
5332 let mut start = fold.range.start.to_point(&buffer);
5333 let mut end = fold.range.end.to_point(&buffer);
5334 start.row -= row_delta;
5335 end.row -= row_delta;
5336 refold_ranges.push(start..end);
5337 }
5338 }
5339 }
5340
5341 // If we didn't move line(s), preserve the existing selections
5342 new_selections.append(&mut contiguous_row_selections);
5343 }
5344
5345 self.transact(cx, |this, cx| {
5346 this.unfold_ranges(unfold_ranges, true, true, cx);
5347 this.buffer.update(cx, |buffer, cx| {
5348 for (range, text) in edits {
5349 buffer.edit([(range, text)], None, cx);
5350 }
5351 });
5352 this.fold_ranges(refold_ranges, true, cx);
5353 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5354 s.select(new_selections);
5355 })
5356 });
5357 }
5358
5359 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5360 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5361 let buffer = self.buffer.read(cx).snapshot(cx);
5362
5363 let mut edits = Vec::new();
5364 let mut unfold_ranges = Vec::new();
5365 let mut refold_ranges = Vec::new();
5366
5367 let selections = self.selections.all::<Point>(cx);
5368 let mut selections = selections.iter().peekable();
5369 let mut contiguous_row_selections = Vec::new();
5370 let mut new_selections = Vec::new();
5371
5372 while let Some(selection) = selections.next() {
5373 // Find all the selections that span a contiguous row range
5374 let (start_row, end_row) = consume_contiguous_rows(
5375 &mut contiguous_row_selections,
5376 selection,
5377 &display_map,
5378 &mut selections,
5379 );
5380
5381 // Move the text spanned by the row range to be after the last line of the row range
5382 if end_row <= buffer.max_point().row {
5383 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
5384 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
5385
5386 // Don't move lines across excerpt boundaries
5387 if buffer
5388 .excerpt_boundaries_in_range((
5389 Bound::Excluded(range_to_move.start),
5390 Bound::Included(insertion_point),
5391 ))
5392 .next()
5393 .is_none()
5394 {
5395 let mut text = String::from("\n");
5396 text.extend(buffer.text_for_range(range_to_move.clone()));
5397 text.pop(); // Drop trailing newline
5398 edits.push((
5399 buffer.anchor_after(range_to_move.start)
5400 ..buffer.anchor_before(range_to_move.end),
5401 String::new(),
5402 ));
5403 let insertion_anchor = buffer.anchor_after(insertion_point);
5404 edits.push((insertion_anchor..insertion_anchor, text));
5405
5406 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5407
5408 // Move selections down
5409 new_selections.extend(contiguous_row_selections.drain(..).map(
5410 |mut selection| {
5411 selection.start.row += row_delta;
5412 selection.end.row += row_delta;
5413 selection
5414 },
5415 ));
5416
5417 // Move folds down
5418 unfold_ranges.push(range_to_move.clone());
5419 for fold in display_map.folds_in_range(
5420 buffer.anchor_before(range_to_move.start)
5421 ..buffer.anchor_after(range_to_move.end),
5422 ) {
5423 let mut start = fold.range.start.to_point(&buffer);
5424 let mut end = fold.range.end.to_point(&buffer);
5425 start.row += row_delta;
5426 end.row += row_delta;
5427 refold_ranges.push(start..end);
5428 }
5429 }
5430 }
5431
5432 // If we didn't move line(s), preserve the existing selections
5433 new_selections.append(&mut contiguous_row_selections);
5434 }
5435
5436 self.transact(cx, |this, cx| {
5437 this.unfold_ranges(unfold_ranges, true, true, cx);
5438 this.buffer.update(cx, |buffer, cx| {
5439 for (range, text) in edits {
5440 buffer.edit([(range, text)], None, cx);
5441 }
5442 });
5443 this.fold_ranges(refold_ranges, true, cx);
5444 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5445 });
5446 }
5447
5448 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5449 let text_layout_details = &self.text_layout_details(cx);
5450 self.transact(cx, |this, cx| {
5451 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5452 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5453 let line_mode = s.line_mode;
5454 s.move_with(|display_map, selection| {
5455 if !selection.is_empty() || line_mode {
5456 return;
5457 }
5458
5459 let mut head = selection.head();
5460 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5461 if head.column() == display_map.line_len(head.row()) {
5462 transpose_offset = display_map
5463 .buffer_snapshot
5464 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5465 }
5466
5467 if transpose_offset == 0 {
5468 return;
5469 }
5470
5471 *head.column_mut() += 1;
5472 head = display_map.clip_point(head, Bias::Right);
5473 let goal = SelectionGoal::HorizontalPosition(
5474 display_map
5475 .x_for_display_point(head, &text_layout_details)
5476 .into(),
5477 );
5478 selection.collapse_to(head, goal);
5479
5480 let transpose_start = display_map
5481 .buffer_snapshot
5482 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5483 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5484 let transpose_end = display_map
5485 .buffer_snapshot
5486 .clip_offset(transpose_offset + 1, Bias::Right);
5487 if let Some(ch) =
5488 display_map.buffer_snapshot.chars_at(transpose_start).next()
5489 {
5490 edits.push((transpose_start..transpose_offset, String::new()));
5491 edits.push((transpose_end..transpose_end, ch.to_string()));
5492 }
5493 }
5494 });
5495 edits
5496 });
5497 this.buffer
5498 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5499 let selections = this.selections.all::<usize>(cx);
5500 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5501 s.select(selections);
5502 });
5503 });
5504 }
5505
5506 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5507 let mut text = String::new();
5508 let buffer = self.buffer.read(cx).snapshot(cx);
5509 let mut selections = self.selections.all::<Point>(cx);
5510 let mut clipboard_selections = Vec::with_capacity(selections.len());
5511 {
5512 let max_point = buffer.max_point();
5513 let mut is_first = true;
5514 for selection in &mut selections {
5515 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5516 if is_entire_line {
5517 selection.start = Point::new(selection.start.row, 0);
5518 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5519 selection.goal = SelectionGoal::None;
5520 }
5521 if is_first {
5522 is_first = false;
5523 } else {
5524 text += "\n";
5525 }
5526 let mut len = 0;
5527 for chunk in buffer.text_for_range(selection.start..selection.end) {
5528 text.push_str(chunk);
5529 len += chunk.len();
5530 }
5531 clipboard_selections.push(ClipboardSelection {
5532 len,
5533 is_entire_line,
5534 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5535 });
5536 }
5537 }
5538
5539 self.transact(cx, |this, cx| {
5540 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5541 s.select(selections);
5542 });
5543 this.insert("", cx);
5544 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5545 });
5546 }
5547
5548 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5549 let selections = self.selections.all::<Point>(cx);
5550 let buffer = self.buffer.read(cx).read(cx);
5551 let mut text = String::new();
5552
5553 let mut clipboard_selections = Vec::with_capacity(selections.len());
5554 {
5555 let max_point = buffer.max_point();
5556 let mut is_first = true;
5557 for selection in selections.iter() {
5558 let mut start = selection.start;
5559 let mut end = selection.end;
5560 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5561 if is_entire_line {
5562 start = Point::new(start.row, 0);
5563 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5564 }
5565 if is_first {
5566 is_first = false;
5567 } else {
5568 text += "\n";
5569 }
5570 let mut len = 0;
5571 for chunk in buffer.text_for_range(start..end) {
5572 text.push_str(chunk);
5573 len += chunk.len();
5574 }
5575 clipboard_selections.push(ClipboardSelection {
5576 len,
5577 is_entire_line,
5578 first_line_indent: buffer.indent_size_for_line(start.row).len,
5579 });
5580 }
5581 }
5582
5583 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5584 }
5585
5586 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5587 self.transact(cx, |this, cx| {
5588 if let Some(item) = cx.read_from_clipboard() {
5589 let clipboard_text = Cow::Borrowed(item.text());
5590 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5591 let old_selections = this.selections.all::<usize>(cx);
5592 let all_selections_were_entire_line =
5593 clipboard_selections.iter().all(|s| s.is_entire_line);
5594 let first_selection_indent_column =
5595 clipboard_selections.first().map(|s| s.first_line_indent);
5596 if clipboard_selections.len() != old_selections.len() {
5597 clipboard_selections.drain(..);
5598 }
5599
5600 this.buffer.update(cx, |buffer, cx| {
5601 let snapshot = buffer.read(cx);
5602 let mut start_offset = 0;
5603 let mut edits = Vec::new();
5604 let mut original_indent_columns = Vec::new();
5605 let line_mode = this.selections.line_mode;
5606 for (ix, selection) in old_selections.iter().enumerate() {
5607 let to_insert;
5608 let entire_line;
5609 let original_indent_column;
5610 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5611 let end_offset = start_offset + clipboard_selection.len;
5612 to_insert = &clipboard_text[start_offset..end_offset];
5613 entire_line = clipboard_selection.is_entire_line;
5614 start_offset = end_offset + 1;
5615 original_indent_column =
5616 Some(clipboard_selection.first_line_indent);
5617 } else {
5618 to_insert = clipboard_text.as_str();
5619 entire_line = all_selections_were_entire_line;
5620 original_indent_column = first_selection_indent_column
5621 }
5622
5623 // If the corresponding selection was empty when this slice of the
5624 // clipboard text was written, then the entire line containing the
5625 // selection was copied. If this selection is also currently empty,
5626 // then paste the line before the current line of the buffer.
5627 let range = if selection.is_empty() && !line_mode && entire_line {
5628 let column = selection.start.to_point(&snapshot).column as usize;
5629 let line_start = selection.start - column;
5630 line_start..line_start
5631 } else {
5632 selection.range()
5633 };
5634
5635 edits.push((range, to_insert));
5636 original_indent_columns.extend(original_indent_column);
5637 }
5638 drop(snapshot);
5639
5640 buffer.edit(
5641 edits,
5642 Some(AutoindentMode::Block {
5643 original_indent_columns,
5644 }),
5645 cx,
5646 );
5647 });
5648
5649 let selections = this.selections.all::<usize>(cx);
5650 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5651 } else {
5652 this.insert(&clipboard_text, cx);
5653 }
5654 }
5655 });
5656 }
5657
5658 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5659 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5660 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5661 self.change_selections(None, cx, |s| {
5662 s.select_anchors(selections.to_vec());
5663 });
5664 }
5665 self.request_autoscroll(Autoscroll::fit(), cx);
5666 self.unmark_text(cx);
5667 self.refresh_copilot_suggestions(true, cx);
5668 cx.emit(EditorEvent::Edited);
5669 }
5670 }
5671
5672 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5673 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5674 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5675 {
5676 self.change_selections(None, cx, |s| {
5677 s.select_anchors(selections.to_vec());
5678 });
5679 }
5680 self.request_autoscroll(Autoscroll::fit(), cx);
5681 self.unmark_text(cx);
5682 self.refresh_copilot_suggestions(true, cx);
5683 cx.emit(EditorEvent::Edited);
5684 }
5685 }
5686
5687 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5688 self.buffer
5689 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5690 }
5691
5692 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5693 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5694 let line_mode = s.line_mode;
5695 s.move_with(|map, selection| {
5696 let cursor = if selection.is_empty() && !line_mode {
5697 movement::left(map, selection.start)
5698 } else {
5699 selection.start
5700 };
5701 selection.collapse_to(cursor, SelectionGoal::None);
5702 });
5703 })
5704 }
5705
5706 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5707 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5708 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5709 })
5710 }
5711
5712 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5713 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5714 let line_mode = s.line_mode;
5715 s.move_with(|map, selection| {
5716 let cursor = if selection.is_empty() && !line_mode {
5717 movement::right(map, selection.end)
5718 } else {
5719 selection.end
5720 };
5721 selection.collapse_to(cursor, SelectionGoal::None)
5722 });
5723 })
5724 }
5725
5726 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5727 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5728 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5729 })
5730 }
5731
5732 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5733 if self.take_rename(true, cx).is_some() {
5734 return;
5735 }
5736
5737 if matches!(self.mode, EditorMode::SingleLine) {
5738 cx.propagate();
5739 return;
5740 }
5741
5742 let text_layout_details = &self.text_layout_details(cx);
5743
5744 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5745 let line_mode = s.line_mode;
5746 s.move_with(|map, selection| {
5747 if !selection.is_empty() && !line_mode {
5748 selection.goal = SelectionGoal::None;
5749 }
5750 let (cursor, goal) = movement::up(
5751 map,
5752 selection.start,
5753 selection.goal,
5754 false,
5755 &text_layout_details,
5756 );
5757 selection.collapse_to(cursor, goal);
5758 });
5759 })
5760 }
5761
5762 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5763 if self.take_rename(true, cx).is_some() {
5764 return;
5765 }
5766
5767 if matches!(self.mode, EditorMode::SingleLine) {
5768 cx.propagate();
5769 return;
5770 }
5771
5772 let row_count = if let Some(row_count) = self.visible_line_count() {
5773 row_count as u32 - 1
5774 } else {
5775 return;
5776 };
5777
5778 let autoscroll = if action.center_cursor {
5779 Autoscroll::center()
5780 } else {
5781 Autoscroll::fit()
5782 };
5783
5784 let text_layout_details = &self.text_layout_details(cx);
5785
5786 self.change_selections(Some(autoscroll), cx, |s| {
5787 let line_mode = s.line_mode;
5788 s.move_with(|map, selection| {
5789 if !selection.is_empty() && !line_mode {
5790 selection.goal = SelectionGoal::None;
5791 }
5792 let (cursor, goal) = movement::up_by_rows(
5793 map,
5794 selection.end,
5795 row_count,
5796 selection.goal,
5797 false,
5798 &text_layout_details,
5799 );
5800 selection.collapse_to(cursor, goal);
5801 });
5802 });
5803 }
5804
5805 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5806 let text_layout_details = &self.text_layout_details(cx);
5807 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5808 s.move_heads_with(|map, head, goal| {
5809 movement::up(map, head, goal, false, &text_layout_details)
5810 })
5811 })
5812 }
5813
5814 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5815 self.take_rename(true, cx);
5816
5817 if self.mode == EditorMode::SingleLine {
5818 cx.propagate();
5819 return;
5820 }
5821
5822 let text_layout_details = &self.text_layout_details(cx);
5823 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5824 let line_mode = s.line_mode;
5825 s.move_with(|map, selection| {
5826 if !selection.is_empty() && !line_mode {
5827 selection.goal = SelectionGoal::None;
5828 }
5829 let (cursor, goal) = movement::down(
5830 map,
5831 selection.end,
5832 selection.goal,
5833 false,
5834 &text_layout_details,
5835 );
5836 selection.collapse_to(cursor, goal);
5837 });
5838 });
5839 }
5840
5841 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5842 if self.take_rename(true, cx).is_some() {
5843 return;
5844 }
5845
5846 if self
5847 .context_menu
5848 .write()
5849 .as_mut()
5850 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5851 .unwrap_or(false)
5852 {
5853 return;
5854 }
5855
5856 if matches!(self.mode, EditorMode::SingleLine) {
5857 cx.propagate();
5858 return;
5859 }
5860
5861 let row_count = if let Some(row_count) = self.visible_line_count() {
5862 row_count as u32 - 1
5863 } else {
5864 return;
5865 };
5866
5867 let autoscroll = if action.center_cursor {
5868 Autoscroll::center()
5869 } else {
5870 Autoscroll::fit()
5871 };
5872
5873 let text_layout_details = &self.text_layout_details(cx);
5874 self.change_selections(Some(autoscroll), cx, |s| {
5875 let line_mode = s.line_mode;
5876 s.move_with(|map, selection| {
5877 if !selection.is_empty() && !line_mode {
5878 selection.goal = SelectionGoal::None;
5879 }
5880 let (cursor, goal) = movement::down_by_rows(
5881 map,
5882 selection.end,
5883 row_count,
5884 selection.goal,
5885 false,
5886 &text_layout_details,
5887 );
5888 selection.collapse_to(cursor, goal);
5889 });
5890 });
5891 }
5892
5893 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5894 let text_layout_details = &self.text_layout_details(cx);
5895 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5896 s.move_heads_with(|map, head, goal| {
5897 movement::down(map, head, goal, false, &text_layout_details)
5898 })
5899 });
5900 }
5901
5902 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5903 if let Some(context_menu) = self.context_menu.write().as_mut() {
5904 context_menu.select_first(self.project.as_ref(), cx);
5905 }
5906 }
5907
5908 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5909 if let Some(context_menu) = self.context_menu.write().as_mut() {
5910 context_menu.select_prev(self.project.as_ref(), cx);
5911 }
5912 }
5913
5914 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5915 if let Some(context_menu) = self.context_menu.write().as_mut() {
5916 context_menu.select_next(self.project.as_ref(), cx);
5917 }
5918 }
5919
5920 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5921 if let Some(context_menu) = self.context_menu.write().as_mut() {
5922 context_menu.select_last(self.project.as_ref(), cx);
5923 }
5924 }
5925
5926 pub fn move_to_previous_word_start(
5927 &mut self,
5928 _: &MoveToPreviousWordStart,
5929 cx: &mut ViewContext<Self>,
5930 ) {
5931 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5932 s.move_cursors_with(|map, head, _| {
5933 (
5934 movement::previous_word_start(map, head),
5935 SelectionGoal::None,
5936 )
5937 });
5938 })
5939 }
5940
5941 pub fn move_to_previous_subword_start(
5942 &mut self,
5943 _: &MoveToPreviousSubwordStart,
5944 cx: &mut ViewContext<Self>,
5945 ) {
5946 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5947 s.move_cursors_with(|map, head, _| {
5948 (
5949 movement::previous_subword_start(map, head),
5950 SelectionGoal::None,
5951 )
5952 });
5953 })
5954 }
5955
5956 pub fn select_to_previous_word_start(
5957 &mut self,
5958 _: &SelectToPreviousWordStart,
5959 cx: &mut ViewContext<Self>,
5960 ) {
5961 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5962 s.move_heads_with(|map, head, _| {
5963 (
5964 movement::previous_word_start(map, head),
5965 SelectionGoal::None,
5966 )
5967 });
5968 })
5969 }
5970
5971 pub fn select_to_previous_subword_start(
5972 &mut self,
5973 _: &SelectToPreviousSubwordStart,
5974 cx: &mut ViewContext<Self>,
5975 ) {
5976 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5977 s.move_heads_with(|map, head, _| {
5978 (
5979 movement::previous_subword_start(map, head),
5980 SelectionGoal::None,
5981 )
5982 });
5983 })
5984 }
5985
5986 pub fn delete_to_previous_word_start(
5987 &mut self,
5988 _: &DeleteToPreviousWordStart,
5989 cx: &mut ViewContext<Self>,
5990 ) {
5991 self.transact(cx, |this, cx| {
5992 this.select_autoclose_pair(cx);
5993 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5994 let line_mode = s.line_mode;
5995 s.move_with(|map, selection| {
5996 if selection.is_empty() && !line_mode {
5997 let cursor = movement::previous_word_start(map, selection.head());
5998 selection.set_head(cursor, SelectionGoal::None);
5999 }
6000 });
6001 });
6002 this.insert("", cx);
6003 });
6004 }
6005
6006 pub fn delete_to_previous_subword_start(
6007 &mut self,
6008 _: &DeleteToPreviousSubwordStart,
6009 cx: &mut ViewContext<Self>,
6010 ) {
6011 self.transact(cx, |this, cx| {
6012 this.select_autoclose_pair(cx);
6013 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6014 let line_mode = s.line_mode;
6015 s.move_with(|map, selection| {
6016 if selection.is_empty() && !line_mode {
6017 let cursor = movement::previous_subword_start(map, selection.head());
6018 selection.set_head(cursor, SelectionGoal::None);
6019 }
6020 });
6021 });
6022 this.insert("", cx);
6023 });
6024 }
6025
6026 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
6027 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6028 s.move_cursors_with(|map, head, _| {
6029 (movement::next_word_end(map, head), SelectionGoal::None)
6030 });
6031 })
6032 }
6033
6034 pub fn move_to_next_subword_end(
6035 &mut self,
6036 _: &MoveToNextSubwordEnd,
6037 cx: &mut ViewContext<Self>,
6038 ) {
6039 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6040 s.move_cursors_with(|map, head, _| {
6041 (movement::next_subword_end(map, head), SelectionGoal::None)
6042 });
6043 })
6044 }
6045
6046 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
6047 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6048 s.move_heads_with(|map, head, _| {
6049 (movement::next_word_end(map, head), SelectionGoal::None)
6050 });
6051 })
6052 }
6053
6054 pub fn select_to_next_subword_end(
6055 &mut self,
6056 _: &SelectToNextSubwordEnd,
6057 cx: &mut ViewContext<Self>,
6058 ) {
6059 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6060 s.move_heads_with(|map, head, _| {
6061 (movement::next_subword_end(map, head), SelectionGoal::None)
6062 });
6063 })
6064 }
6065
6066 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
6067 self.transact(cx, |this, cx| {
6068 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6069 let line_mode = s.line_mode;
6070 s.move_with(|map, selection| {
6071 if selection.is_empty() && !line_mode {
6072 let cursor = movement::next_word_end(map, selection.head());
6073 selection.set_head(cursor, SelectionGoal::None);
6074 }
6075 });
6076 });
6077 this.insert("", cx);
6078 });
6079 }
6080
6081 pub fn delete_to_next_subword_end(
6082 &mut self,
6083 _: &DeleteToNextSubwordEnd,
6084 cx: &mut ViewContext<Self>,
6085 ) {
6086 self.transact(cx, |this, cx| {
6087 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6088 s.move_with(|map, selection| {
6089 if selection.is_empty() {
6090 let cursor = movement::next_subword_end(map, selection.head());
6091 selection.set_head(cursor, SelectionGoal::None);
6092 }
6093 });
6094 });
6095 this.insert("", cx);
6096 });
6097 }
6098
6099 pub fn move_to_beginning_of_line(
6100 &mut self,
6101 _: &MoveToBeginningOfLine,
6102 cx: &mut ViewContext<Self>,
6103 ) {
6104 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6105 s.move_cursors_with(|map, head, _| {
6106 (
6107 movement::indented_line_beginning(map, head, true),
6108 SelectionGoal::None,
6109 )
6110 });
6111 })
6112 }
6113
6114 pub fn select_to_beginning_of_line(
6115 &mut self,
6116 action: &SelectToBeginningOfLine,
6117 cx: &mut ViewContext<Self>,
6118 ) {
6119 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6120 s.move_heads_with(|map, head, _| {
6121 (
6122 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6123 SelectionGoal::None,
6124 )
6125 });
6126 });
6127 }
6128
6129 pub fn delete_to_beginning_of_line(
6130 &mut self,
6131 _: &DeleteToBeginningOfLine,
6132 cx: &mut ViewContext<Self>,
6133 ) {
6134 self.transact(cx, |this, cx| {
6135 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6136 s.move_with(|_, selection| {
6137 selection.reversed = true;
6138 });
6139 });
6140
6141 this.select_to_beginning_of_line(
6142 &SelectToBeginningOfLine {
6143 stop_at_soft_wraps: false,
6144 },
6145 cx,
6146 );
6147 this.backspace(&Backspace, cx);
6148 });
6149 }
6150
6151 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6152 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6153 s.move_cursors_with(|map, head, _| {
6154 (movement::line_end(map, head, true), SelectionGoal::None)
6155 });
6156 })
6157 }
6158
6159 pub fn select_to_end_of_line(
6160 &mut self,
6161 action: &SelectToEndOfLine,
6162 cx: &mut ViewContext<Self>,
6163 ) {
6164 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6165 s.move_heads_with(|map, head, _| {
6166 (
6167 movement::line_end(map, head, action.stop_at_soft_wraps),
6168 SelectionGoal::None,
6169 )
6170 });
6171 })
6172 }
6173
6174 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6175 self.transact(cx, |this, cx| {
6176 this.select_to_end_of_line(
6177 &SelectToEndOfLine {
6178 stop_at_soft_wraps: false,
6179 },
6180 cx,
6181 );
6182 this.delete(&Delete, cx);
6183 });
6184 }
6185
6186 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6187 self.transact(cx, |this, cx| {
6188 this.select_to_end_of_line(
6189 &SelectToEndOfLine {
6190 stop_at_soft_wraps: false,
6191 },
6192 cx,
6193 );
6194 this.cut(&Cut, cx);
6195 });
6196 }
6197
6198 pub fn move_to_start_of_paragraph(
6199 &mut self,
6200 _: &MoveToStartOfParagraph,
6201 cx: &mut ViewContext<Self>,
6202 ) {
6203 if matches!(self.mode, EditorMode::SingleLine) {
6204 cx.propagate();
6205 return;
6206 }
6207
6208 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6209 s.move_with(|map, selection| {
6210 selection.collapse_to(
6211 movement::start_of_paragraph(map, selection.head(), 1),
6212 SelectionGoal::None,
6213 )
6214 });
6215 })
6216 }
6217
6218 pub fn move_to_end_of_paragraph(
6219 &mut self,
6220 _: &MoveToEndOfParagraph,
6221 cx: &mut ViewContext<Self>,
6222 ) {
6223 if matches!(self.mode, EditorMode::SingleLine) {
6224 cx.propagate();
6225 return;
6226 }
6227
6228 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6229 s.move_with(|map, selection| {
6230 selection.collapse_to(
6231 movement::end_of_paragraph(map, selection.head(), 1),
6232 SelectionGoal::None,
6233 )
6234 });
6235 })
6236 }
6237
6238 pub fn select_to_start_of_paragraph(
6239 &mut self,
6240 _: &SelectToStartOfParagraph,
6241 cx: &mut ViewContext<Self>,
6242 ) {
6243 if matches!(self.mode, EditorMode::SingleLine) {
6244 cx.propagate();
6245 return;
6246 }
6247
6248 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6249 s.move_heads_with(|map, head, _| {
6250 (
6251 movement::start_of_paragraph(map, head, 1),
6252 SelectionGoal::None,
6253 )
6254 });
6255 })
6256 }
6257
6258 pub fn select_to_end_of_paragraph(
6259 &mut self,
6260 _: &SelectToEndOfParagraph,
6261 cx: &mut ViewContext<Self>,
6262 ) {
6263 if matches!(self.mode, EditorMode::SingleLine) {
6264 cx.propagate();
6265 return;
6266 }
6267
6268 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6269 s.move_heads_with(|map, head, _| {
6270 (
6271 movement::end_of_paragraph(map, head, 1),
6272 SelectionGoal::None,
6273 )
6274 });
6275 })
6276 }
6277
6278 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6279 if matches!(self.mode, EditorMode::SingleLine) {
6280 cx.propagate();
6281 return;
6282 }
6283
6284 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6285 s.select_ranges(vec![0..0]);
6286 });
6287 }
6288
6289 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6290 let mut selection = self.selections.last::<Point>(cx);
6291 selection.set_head(Point::zero(), SelectionGoal::None);
6292
6293 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6294 s.select(vec![selection]);
6295 });
6296 }
6297
6298 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6299 if matches!(self.mode, EditorMode::SingleLine) {
6300 cx.propagate();
6301 return;
6302 }
6303
6304 let cursor = self.buffer.read(cx).read(cx).len();
6305 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6306 s.select_ranges(vec![cursor..cursor])
6307 });
6308 }
6309
6310 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6311 self.nav_history = nav_history;
6312 }
6313
6314 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6315 self.nav_history.as_ref()
6316 }
6317
6318 fn push_to_nav_history(
6319 &mut self,
6320 cursor_anchor: Anchor,
6321 new_position: Option<Point>,
6322 cx: &mut ViewContext<Self>,
6323 ) {
6324 if let Some(nav_history) = self.nav_history.as_mut() {
6325 let buffer = self.buffer.read(cx).read(cx);
6326 let cursor_position = cursor_anchor.to_point(&buffer);
6327 let scroll_state = self.scroll_manager.anchor();
6328 let scroll_top_row = scroll_state.top_row(&buffer);
6329 drop(buffer);
6330
6331 if let Some(new_position) = new_position {
6332 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6333 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6334 return;
6335 }
6336 }
6337
6338 nav_history.push(
6339 Some(NavigationData {
6340 cursor_anchor,
6341 cursor_position,
6342 scroll_anchor: scroll_state,
6343 scroll_top_row,
6344 }),
6345 cx,
6346 );
6347 }
6348 }
6349
6350 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6351 let buffer = self.buffer.read(cx).snapshot(cx);
6352 let mut selection = self.selections.first::<usize>(cx);
6353 selection.set_head(buffer.len(), SelectionGoal::None);
6354 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6355 s.select(vec![selection]);
6356 });
6357 }
6358
6359 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6360 let end = self.buffer.read(cx).read(cx).len();
6361 self.change_selections(None, cx, |s| {
6362 s.select_ranges(vec![0..end]);
6363 });
6364 }
6365
6366 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6367 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6368 let mut selections = self.selections.all::<Point>(cx);
6369 let max_point = display_map.buffer_snapshot.max_point();
6370 for selection in &mut selections {
6371 let rows = selection.spanned_rows(true, &display_map);
6372 selection.start = Point::new(rows.start, 0);
6373 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
6374 selection.reversed = false;
6375 }
6376 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6377 s.select(selections);
6378 });
6379 }
6380
6381 pub fn split_selection_into_lines(
6382 &mut self,
6383 _: &SplitSelectionIntoLines,
6384 cx: &mut ViewContext<Self>,
6385 ) {
6386 let mut to_unfold = Vec::new();
6387 let mut new_selection_ranges = Vec::new();
6388 {
6389 let selections = self.selections.all::<Point>(cx);
6390 let buffer = self.buffer.read(cx).read(cx);
6391 for selection in selections {
6392 for row in selection.start.row..selection.end.row {
6393 let cursor = Point::new(row, buffer.line_len(row));
6394 new_selection_ranges.push(cursor..cursor);
6395 }
6396 new_selection_ranges.push(selection.end..selection.end);
6397 to_unfold.push(selection.start..selection.end);
6398 }
6399 }
6400 self.unfold_ranges(to_unfold, true, true, cx);
6401 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6402 s.select_ranges(new_selection_ranges);
6403 });
6404 }
6405
6406 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6407 self.add_selection(true, cx);
6408 }
6409
6410 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6411 self.add_selection(false, cx);
6412 }
6413
6414 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6415 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6416 let mut selections = self.selections.all::<Point>(cx);
6417 let text_layout_details = self.text_layout_details(cx);
6418 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6419 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6420 let range = oldest_selection.display_range(&display_map).sorted();
6421
6422 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
6423 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
6424 let positions = start_x.min(end_x)..start_x.max(end_x);
6425
6426 selections.clear();
6427 let mut stack = Vec::new();
6428 for row in range.start.row()..=range.end.row() {
6429 if let Some(selection) = self.selections.build_columnar_selection(
6430 &display_map,
6431 row,
6432 &positions,
6433 oldest_selection.reversed,
6434 &text_layout_details,
6435 ) {
6436 stack.push(selection.id);
6437 selections.push(selection);
6438 }
6439 }
6440
6441 if above {
6442 stack.reverse();
6443 }
6444
6445 AddSelectionsState { above, stack }
6446 });
6447
6448 let last_added_selection = *state.stack.last().unwrap();
6449 let mut new_selections = Vec::new();
6450 if above == state.above {
6451 let end_row = if above {
6452 0
6453 } else {
6454 display_map.max_point().row()
6455 };
6456
6457 'outer: for selection in selections {
6458 if selection.id == last_added_selection {
6459 let range = selection.display_range(&display_map).sorted();
6460 debug_assert_eq!(range.start.row(), range.end.row());
6461 let mut row = range.start.row();
6462 let positions =
6463 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
6464 px(start)..px(end)
6465 } else {
6466 let start_x =
6467 display_map.x_for_display_point(range.start, &text_layout_details);
6468 let end_x =
6469 display_map.x_for_display_point(range.end, &text_layout_details);
6470 start_x.min(end_x)..start_x.max(end_x)
6471 };
6472
6473 while row != end_row {
6474 if above {
6475 row -= 1;
6476 } else {
6477 row += 1;
6478 }
6479
6480 if let Some(new_selection) = self.selections.build_columnar_selection(
6481 &display_map,
6482 row,
6483 &positions,
6484 selection.reversed,
6485 &text_layout_details,
6486 ) {
6487 state.stack.push(new_selection.id);
6488 if above {
6489 new_selections.push(new_selection);
6490 new_selections.push(selection);
6491 } else {
6492 new_selections.push(selection);
6493 new_selections.push(new_selection);
6494 }
6495
6496 continue 'outer;
6497 }
6498 }
6499 }
6500
6501 new_selections.push(selection);
6502 }
6503 } else {
6504 new_selections = selections;
6505 new_selections.retain(|s| s.id != last_added_selection);
6506 state.stack.pop();
6507 }
6508
6509 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6510 s.select(new_selections);
6511 });
6512 if state.stack.len() > 1 {
6513 self.add_selections_state = Some(state);
6514 }
6515 }
6516
6517 pub fn select_next_match_internal(
6518 &mut self,
6519 display_map: &DisplaySnapshot,
6520 replace_newest: bool,
6521 autoscroll: Option<Autoscroll>,
6522 cx: &mut ViewContext<Self>,
6523 ) -> Result<()> {
6524 fn select_next_match_ranges(
6525 this: &mut Editor,
6526 range: Range<usize>,
6527 replace_newest: bool,
6528 auto_scroll: Option<Autoscroll>,
6529 cx: &mut ViewContext<Editor>,
6530 ) {
6531 this.unfold_ranges([range.clone()], false, true, cx);
6532 this.change_selections(auto_scroll, cx, |s| {
6533 if replace_newest {
6534 s.delete(s.newest_anchor().id);
6535 }
6536 s.insert_range(range.clone());
6537 });
6538 }
6539
6540 let buffer = &display_map.buffer_snapshot;
6541 let mut selections = self.selections.all::<usize>(cx);
6542 if let Some(mut select_next_state) = self.select_next_state.take() {
6543 let query = &select_next_state.query;
6544 if !select_next_state.done {
6545 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6546 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6547 let mut next_selected_range = None;
6548
6549 let bytes_after_last_selection =
6550 buffer.bytes_in_range(last_selection.end..buffer.len());
6551 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6552 let query_matches = query
6553 .stream_find_iter(bytes_after_last_selection)
6554 .map(|result| (last_selection.end, result))
6555 .chain(
6556 query
6557 .stream_find_iter(bytes_before_first_selection)
6558 .map(|result| (0, result)),
6559 );
6560
6561 for (start_offset, query_match) in query_matches {
6562 let query_match = query_match.unwrap(); // can only fail due to I/O
6563 let offset_range =
6564 start_offset + query_match.start()..start_offset + query_match.end();
6565 let display_range = offset_range.start.to_display_point(&display_map)
6566 ..offset_range.end.to_display_point(&display_map);
6567
6568 if !select_next_state.wordwise
6569 || (!movement::is_inside_word(&display_map, display_range.start)
6570 && !movement::is_inside_word(&display_map, display_range.end))
6571 {
6572 if selections
6573 .iter()
6574 .find(|selection| selection.range().overlaps(&offset_range))
6575 .is_none()
6576 {
6577 next_selected_range = Some(offset_range);
6578 break;
6579 }
6580 }
6581 }
6582
6583 if let Some(next_selected_range) = next_selected_range {
6584 select_next_match_ranges(
6585 self,
6586 next_selected_range,
6587 replace_newest,
6588 autoscroll,
6589 cx,
6590 );
6591 } else {
6592 select_next_state.done = true;
6593 }
6594 }
6595
6596 self.select_next_state = Some(select_next_state);
6597 } else if selections.len() == 1 {
6598 let selection = selections.last_mut().unwrap();
6599 if selection.start == selection.end {
6600 let word_range = movement::surrounding_word(
6601 &display_map,
6602 selection.start.to_display_point(&display_map),
6603 );
6604 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6605 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6606 selection.goal = SelectionGoal::None;
6607 selection.reversed = false;
6608
6609 let query = buffer
6610 .text_for_range(selection.start..selection.end)
6611 .collect::<String>();
6612
6613 let is_empty = query.is_empty();
6614 let select_state = SelectNextState {
6615 query: AhoCorasick::new(&[query])?,
6616 wordwise: true,
6617 done: is_empty,
6618 };
6619 select_next_match_ranges(
6620 self,
6621 selection.start..selection.end,
6622 replace_newest,
6623 autoscroll,
6624 cx,
6625 );
6626 self.select_next_state = Some(select_state);
6627 } else {
6628 let query = buffer
6629 .text_for_range(selection.start..selection.end)
6630 .collect::<String>();
6631 self.select_next_state = Some(SelectNextState {
6632 query: AhoCorasick::new(&[query])?,
6633 wordwise: false,
6634 done: false,
6635 });
6636 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6637 }
6638 }
6639 Ok(())
6640 }
6641
6642 pub fn select_all_matches(
6643 &mut self,
6644 action: &SelectAllMatches,
6645 cx: &mut ViewContext<Self>,
6646 ) -> Result<()> {
6647 self.push_to_selection_history();
6648 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6649
6650 loop {
6651 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6652
6653 if self
6654 .select_next_state
6655 .as_ref()
6656 .map(|selection_state| selection_state.done)
6657 .unwrap_or(true)
6658 {
6659 break;
6660 }
6661 }
6662
6663 Ok(())
6664 }
6665
6666 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6667 self.push_to_selection_history();
6668 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6669 self.select_next_match_internal(
6670 &display_map,
6671 action.replace_newest,
6672 Some(Autoscroll::newest()),
6673 cx,
6674 )?;
6675 Ok(())
6676 }
6677
6678 pub fn select_previous(
6679 &mut self,
6680 action: &SelectPrevious,
6681 cx: &mut ViewContext<Self>,
6682 ) -> Result<()> {
6683 self.push_to_selection_history();
6684 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6685 let buffer = &display_map.buffer_snapshot;
6686 let mut selections = self.selections.all::<usize>(cx);
6687 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6688 let query = &select_prev_state.query;
6689 if !select_prev_state.done {
6690 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6691 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6692 let mut next_selected_range = None;
6693 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6694 let bytes_before_last_selection =
6695 buffer.reversed_bytes_in_range(0..last_selection.start);
6696 let bytes_after_first_selection =
6697 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6698 let query_matches = query
6699 .stream_find_iter(bytes_before_last_selection)
6700 .map(|result| (last_selection.start, result))
6701 .chain(
6702 query
6703 .stream_find_iter(bytes_after_first_selection)
6704 .map(|result| (buffer.len(), result)),
6705 );
6706 for (end_offset, query_match) in query_matches {
6707 let query_match = query_match.unwrap(); // can only fail due to I/O
6708 let offset_range =
6709 end_offset - query_match.end()..end_offset - query_match.start();
6710 let display_range = offset_range.start.to_display_point(&display_map)
6711 ..offset_range.end.to_display_point(&display_map);
6712
6713 if !select_prev_state.wordwise
6714 || (!movement::is_inside_word(&display_map, display_range.start)
6715 && !movement::is_inside_word(&display_map, display_range.end))
6716 {
6717 next_selected_range = Some(offset_range);
6718 break;
6719 }
6720 }
6721
6722 if let Some(next_selected_range) = next_selected_range {
6723 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6724 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6725 if action.replace_newest {
6726 s.delete(s.newest_anchor().id);
6727 }
6728 s.insert_range(next_selected_range);
6729 });
6730 } else {
6731 select_prev_state.done = true;
6732 }
6733 }
6734
6735 self.select_prev_state = Some(select_prev_state);
6736 } else if selections.len() == 1 {
6737 let selection = selections.last_mut().unwrap();
6738 if selection.start == selection.end {
6739 let word_range = movement::surrounding_word(
6740 &display_map,
6741 selection.start.to_display_point(&display_map),
6742 );
6743 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6744 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6745 selection.goal = SelectionGoal::None;
6746 selection.reversed = false;
6747
6748 let query = buffer
6749 .text_for_range(selection.start..selection.end)
6750 .collect::<String>();
6751 let query = query.chars().rev().collect::<String>();
6752 let select_state = SelectNextState {
6753 query: AhoCorasick::new(&[query])?,
6754 wordwise: true,
6755 done: false,
6756 };
6757 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6758 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6759 s.select(selections);
6760 });
6761 self.select_prev_state = Some(select_state);
6762 } else {
6763 let query = buffer
6764 .text_for_range(selection.start..selection.end)
6765 .collect::<String>();
6766 let query = query.chars().rev().collect::<String>();
6767 self.select_prev_state = Some(SelectNextState {
6768 query: AhoCorasick::new(&[query])?,
6769 wordwise: false,
6770 done: false,
6771 });
6772 self.select_previous(action, cx)?;
6773 }
6774 }
6775 Ok(())
6776 }
6777
6778 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6779 let text_layout_details = &self.text_layout_details(cx);
6780 self.transact(cx, |this, cx| {
6781 let mut selections = this.selections.all::<Point>(cx);
6782 let mut edits = Vec::new();
6783 let mut selection_edit_ranges = Vec::new();
6784 let mut last_toggled_row = None;
6785 let snapshot = this.buffer.read(cx).read(cx);
6786 let empty_str: Arc<str> = "".into();
6787 let mut suffixes_inserted = Vec::new();
6788
6789 fn comment_prefix_range(
6790 snapshot: &MultiBufferSnapshot,
6791 row: u32,
6792 comment_prefix: &str,
6793 comment_prefix_whitespace: &str,
6794 ) -> Range<Point> {
6795 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6796
6797 let mut line_bytes = snapshot
6798 .bytes_in_range(start..snapshot.max_point())
6799 .flatten()
6800 .copied();
6801
6802 // If this line currently begins with the line comment prefix, then record
6803 // the range containing the prefix.
6804 if line_bytes
6805 .by_ref()
6806 .take(comment_prefix.len())
6807 .eq(comment_prefix.bytes())
6808 {
6809 // Include any whitespace that matches the comment prefix.
6810 let matching_whitespace_len = line_bytes
6811 .zip(comment_prefix_whitespace.bytes())
6812 .take_while(|(a, b)| a == b)
6813 .count() as u32;
6814 let end = Point::new(
6815 start.row,
6816 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6817 );
6818 start..end
6819 } else {
6820 start..start
6821 }
6822 }
6823
6824 fn comment_suffix_range(
6825 snapshot: &MultiBufferSnapshot,
6826 row: u32,
6827 comment_suffix: &str,
6828 comment_suffix_has_leading_space: bool,
6829 ) -> Range<Point> {
6830 let end = Point::new(row, snapshot.line_len(row));
6831 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6832
6833 let mut line_end_bytes = snapshot
6834 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6835 .flatten()
6836 .copied();
6837
6838 let leading_space_len = if suffix_start_column > 0
6839 && line_end_bytes.next() == Some(b' ')
6840 && comment_suffix_has_leading_space
6841 {
6842 1
6843 } else {
6844 0
6845 };
6846
6847 // If this line currently begins with the line comment prefix, then record
6848 // the range containing the prefix.
6849 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6850 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6851 start..end
6852 } else {
6853 end..end
6854 }
6855 }
6856
6857 // TODO: Handle selections that cross excerpts
6858 for selection in &mut selections {
6859 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6860 let language = if let Some(language) =
6861 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6862 {
6863 language
6864 } else {
6865 continue;
6866 };
6867
6868 selection_edit_ranges.clear();
6869
6870 // If multiple selections contain a given row, avoid processing that
6871 // row more than once.
6872 let mut start_row = selection.start.row;
6873 if last_toggled_row == Some(start_row) {
6874 start_row += 1;
6875 }
6876 let end_row =
6877 if selection.end.row > selection.start.row && selection.end.column == 0 {
6878 selection.end.row - 1
6879 } else {
6880 selection.end.row
6881 };
6882 last_toggled_row = Some(end_row);
6883
6884 if start_row > end_row {
6885 continue;
6886 }
6887
6888 // If the language has line comments, toggle those.
6889 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6890 // Split the comment prefix's trailing whitespace into a separate string,
6891 // as that portion won't be used for detecting if a line is a comment.
6892 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6893 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6894 let mut all_selection_lines_are_comments = true;
6895
6896 for row in start_row..=end_row {
6897 if snapshot.is_line_blank(row) && start_row < end_row {
6898 continue;
6899 }
6900
6901 let prefix_range = comment_prefix_range(
6902 snapshot.deref(),
6903 row,
6904 comment_prefix,
6905 comment_prefix_whitespace,
6906 );
6907 if prefix_range.is_empty() {
6908 all_selection_lines_are_comments = false;
6909 }
6910 selection_edit_ranges.push(prefix_range);
6911 }
6912
6913 if all_selection_lines_are_comments {
6914 edits.extend(
6915 selection_edit_ranges
6916 .iter()
6917 .cloned()
6918 .map(|range| (range, empty_str.clone())),
6919 );
6920 } else {
6921 let min_column = selection_edit_ranges
6922 .iter()
6923 .map(|r| r.start.column)
6924 .min()
6925 .unwrap_or(0);
6926 edits.extend(selection_edit_ranges.iter().map(|range| {
6927 let position = Point::new(range.start.row, min_column);
6928 (position..position, full_comment_prefix.clone())
6929 }));
6930 }
6931 } else if let Some((full_comment_prefix, comment_suffix)) =
6932 language.block_comment_delimiters()
6933 {
6934 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6935 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6936 let prefix_range = comment_prefix_range(
6937 snapshot.deref(),
6938 start_row,
6939 comment_prefix,
6940 comment_prefix_whitespace,
6941 );
6942 let suffix_range = comment_suffix_range(
6943 snapshot.deref(),
6944 end_row,
6945 comment_suffix.trim_start_matches(' '),
6946 comment_suffix.starts_with(' '),
6947 );
6948
6949 if prefix_range.is_empty() || suffix_range.is_empty() {
6950 edits.push((
6951 prefix_range.start..prefix_range.start,
6952 full_comment_prefix.clone(),
6953 ));
6954 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6955 suffixes_inserted.push((end_row, comment_suffix.len()));
6956 } else {
6957 edits.push((prefix_range, empty_str.clone()));
6958 edits.push((suffix_range, empty_str.clone()));
6959 }
6960 } else {
6961 continue;
6962 }
6963 }
6964
6965 drop(snapshot);
6966 this.buffer.update(cx, |buffer, cx| {
6967 buffer.edit(edits, None, cx);
6968 });
6969
6970 // Adjust selections so that they end before any comment suffixes that
6971 // were inserted.
6972 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6973 let mut selections = this.selections.all::<Point>(cx);
6974 let snapshot = this.buffer.read(cx).read(cx);
6975 for selection in &mut selections {
6976 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6977 match row.cmp(&selection.end.row) {
6978 Ordering::Less => {
6979 suffixes_inserted.next();
6980 continue;
6981 }
6982 Ordering::Greater => break,
6983 Ordering::Equal => {
6984 if selection.end.column == snapshot.line_len(row) {
6985 if selection.is_empty() {
6986 selection.start.column -= suffix_len as u32;
6987 }
6988 selection.end.column -= suffix_len as u32;
6989 }
6990 break;
6991 }
6992 }
6993 }
6994 }
6995
6996 drop(snapshot);
6997 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6998
6999 let selections = this.selections.all::<Point>(cx);
7000 let selections_on_single_row = selections.windows(2).all(|selections| {
7001 selections[0].start.row == selections[1].start.row
7002 && selections[0].end.row == selections[1].end.row
7003 && selections[0].start.row == selections[0].end.row
7004 });
7005 let selections_selecting = selections
7006 .iter()
7007 .any(|selection| selection.start != selection.end);
7008 let advance_downwards = action.advance_downwards
7009 && selections_on_single_row
7010 && !selections_selecting
7011 && this.mode != EditorMode::SingleLine;
7012
7013 if advance_downwards {
7014 let snapshot = this.buffer.read(cx).snapshot(cx);
7015
7016 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7017 s.move_cursors_with(|display_snapshot, display_point, _| {
7018 let mut point = display_point.to_point(display_snapshot);
7019 point.row += 1;
7020 point = snapshot.clip_point(point, Bias::Left);
7021 let display_point = point.to_display_point(display_snapshot);
7022 let goal = SelectionGoal::HorizontalPosition(
7023 display_snapshot
7024 .x_for_display_point(display_point, &text_layout_details)
7025 .into(),
7026 );
7027 (display_point, goal)
7028 })
7029 });
7030 }
7031 });
7032 }
7033
7034 pub fn select_larger_syntax_node(
7035 &mut self,
7036 _: &SelectLargerSyntaxNode,
7037 cx: &mut ViewContext<Self>,
7038 ) {
7039 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7040 let buffer = self.buffer.read(cx).snapshot(cx);
7041 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
7042
7043 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7044 let mut selected_larger_node = false;
7045 let new_selections = old_selections
7046 .iter()
7047 .map(|selection| {
7048 let old_range = selection.start..selection.end;
7049 let mut new_range = old_range.clone();
7050 while let Some(containing_range) =
7051 buffer.range_for_syntax_ancestor(new_range.clone())
7052 {
7053 new_range = containing_range;
7054 if !display_map.intersects_fold(new_range.start)
7055 && !display_map.intersects_fold(new_range.end)
7056 {
7057 break;
7058 }
7059 }
7060
7061 selected_larger_node |= new_range != old_range;
7062 Selection {
7063 id: selection.id,
7064 start: new_range.start,
7065 end: new_range.end,
7066 goal: SelectionGoal::None,
7067 reversed: selection.reversed,
7068 }
7069 })
7070 .collect::<Vec<_>>();
7071
7072 if selected_larger_node {
7073 stack.push(old_selections);
7074 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7075 s.select(new_selections);
7076 });
7077 }
7078 self.select_larger_syntax_node_stack = stack;
7079 }
7080
7081 pub fn select_smaller_syntax_node(
7082 &mut self,
7083 _: &SelectSmallerSyntaxNode,
7084 cx: &mut ViewContext<Self>,
7085 ) {
7086 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7087 if let Some(selections) = stack.pop() {
7088 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7089 s.select(selections.to_vec());
7090 });
7091 }
7092 self.select_larger_syntax_node_stack = stack;
7093 }
7094
7095 pub fn move_to_enclosing_bracket(
7096 &mut self,
7097 _: &MoveToEnclosingBracket,
7098 cx: &mut ViewContext<Self>,
7099 ) {
7100 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7101 s.move_offsets_with(|snapshot, selection| {
7102 let Some(enclosing_bracket_ranges) =
7103 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
7104 else {
7105 return;
7106 };
7107
7108 let mut best_length = usize::MAX;
7109 let mut best_inside = false;
7110 let mut best_in_bracket_range = false;
7111 let mut best_destination = None;
7112 for (open, close) in enclosing_bracket_ranges {
7113 let close = close.to_inclusive();
7114 let length = close.end() - open.start;
7115 let inside = selection.start >= open.end && selection.end <= *close.start();
7116 let in_bracket_range = open.to_inclusive().contains(&selection.head())
7117 || close.contains(&selection.head());
7118
7119 // If best is next to a bracket and current isn't, skip
7120 if !in_bracket_range && best_in_bracket_range {
7121 continue;
7122 }
7123
7124 // Prefer smaller lengths unless best is inside and current isn't
7125 if length > best_length && (best_inside || !inside) {
7126 continue;
7127 }
7128
7129 best_length = length;
7130 best_inside = inside;
7131 best_in_bracket_range = in_bracket_range;
7132 best_destination = Some(
7133 if close.contains(&selection.start) && close.contains(&selection.end) {
7134 if inside {
7135 open.end
7136 } else {
7137 open.start
7138 }
7139 } else {
7140 if inside {
7141 *close.start()
7142 } else {
7143 *close.end()
7144 }
7145 },
7146 );
7147 }
7148
7149 if let Some(destination) = best_destination {
7150 selection.collapse_to(destination, SelectionGoal::None);
7151 }
7152 })
7153 });
7154 }
7155
7156 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7157 self.end_selection(cx);
7158 self.selection_history.mode = SelectionHistoryMode::Undoing;
7159 if let Some(entry) = self.selection_history.undo_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 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7170 self.end_selection(cx);
7171 self.selection_history.mode = SelectionHistoryMode::Redoing;
7172 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7173 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7174 self.select_next_state = entry.select_next_state;
7175 self.select_prev_state = entry.select_prev_state;
7176 self.add_selections_state = entry.add_selections_state;
7177 self.request_autoscroll(Autoscroll::newest(), cx);
7178 }
7179 self.selection_history.mode = SelectionHistoryMode::Normal;
7180 }
7181
7182 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
7183 self.go_to_diagnostic_impl(Direction::Next, cx)
7184 }
7185
7186 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
7187 self.go_to_diagnostic_impl(Direction::Prev, cx)
7188 }
7189
7190 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
7191 let buffer = self.buffer.read(cx).snapshot(cx);
7192 let selection = self.selections.newest::<usize>(cx);
7193
7194 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
7195 if direction == Direction::Next {
7196 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
7197 let (group_id, jump_to) = popover.activation_info();
7198 if self.activate_diagnostics(group_id, cx) {
7199 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7200 let mut new_selection = s.newest_anchor().clone();
7201 new_selection.collapse_to(jump_to, SelectionGoal::None);
7202 s.select_anchors(vec![new_selection.clone()]);
7203 });
7204 }
7205 return;
7206 }
7207 }
7208
7209 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
7210 active_diagnostics
7211 .primary_range
7212 .to_offset(&buffer)
7213 .to_inclusive()
7214 });
7215 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
7216 if active_primary_range.contains(&selection.head()) {
7217 *active_primary_range.end()
7218 } else {
7219 selection.head()
7220 }
7221 } else {
7222 selection.head()
7223 };
7224
7225 loop {
7226 let mut diagnostics = if direction == Direction::Prev {
7227 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
7228 } else {
7229 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
7230 };
7231 let group = diagnostics.find_map(|entry| {
7232 if entry.diagnostic.is_primary
7233 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
7234 && !entry.range.is_empty()
7235 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
7236 && !entry.range.contains(&search_start)
7237 {
7238 Some((entry.range, entry.diagnostic.group_id))
7239 } else {
7240 None
7241 }
7242 });
7243
7244 if let Some((primary_range, group_id)) = group {
7245 if self.activate_diagnostics(group_id, cx) {
7246 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7247 s.select(vec![Selection {
7248 id: selection.id,
7249 start: primary_range.start,
7250 end: primary_range.start,
7251 reversed: false,
7252 goal: SelectionGoal::None,
7253 }]);
7254 });
7255 }
7256 break;
7257 } else {
7258 // Cycle around to the start of the buffer, potentially moving back to the start of
7259 // the currently active diagnostic.
7260 active_primary_range.take();
7261 if direction == Direction::Prev {
7262 if search_start == buffer.len() {
7263 break;
7264 } else {
7265 search_start = buffer.len();
7266 }
7267 } else if search_start == 0 {
7268 break;
7269 } else {
7270 search_start = 0;
7271 }
7272 }
7273 }
7274 }
7275
7276 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7277 let snapshot = self
7278 .display_map
7279 .update(cx, |display_map, cx| display_map.snapshot(cx));
7280 let selection = self.selections.newest::<Point>(cx);
7281
7282 if !self.seek_in_direction(
7283 &snapshot,
7284 selection.head(),
7285 false,
7286 snapshot
7287 .buffer_snapshot
7288 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7289 cx,
7290 ) {
7291 let wrapped_point = Point::zero();
7292 self.seek_in_direction(
7293 &snapshot,
7294 wrapped_point,
7295 true,
7296 snapshot
7297 .buffer_snapshot
7298 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7299 cx,
7300 );
7301 }
7302 }
7303
7304 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7305 let snapshot = self
7306 .display_map
7307 .update(cx, |display_map, cx| display_map.snapshot(cx));
7308 let selection = self.selections.newest::<Point>(cx);
7309
7310 if !self.seek_in_direction(
7311 &snapshot,
7312 selection.head(),
7313 false,
7314 snapshot
7315 .buffer_snapshot
7316 .git_diff_hunks_in_range_rev(0..selection.head().row),
7317 cx,
7318 ) {
7319 let wrapped_point = snapshot.buffer_snapshot.max_point();
7320 self.seek_in_direction(
7321 &snapshot,
7322 wrapped_point,
7323 true,
7324 snapshot
7325 .buffer_snapshot
7326 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7327 cx,
7328 );
7329 }
7330 }
7331
7332 fn seek_in_direction(
7333 &mut self,
7334 snapshot: &DisplaySnapshot,
7335 initial_point: Point,
7336 is_wrapped: bool,
7337 hunks: impl Iterator<Item = DiffHunk<u32>>,
7338 cx: &mut ViewContext<Editor>,
7339 ) -> bool {
7340 let display_point = initial_point.to_display_point(snapshot);
7341 let mut hunks = hunks
7342 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7343 .filter(|hunk| {
7344 if is_wrapped {
7345 true
7346 } else {
7347 !hunk.contains_display_row(display_point.row())
7348 }
7349 })
7350 .dedup();
7351
7352 if let Some(hunk) = hunks.next() {
7353 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7354 let row = hunk.start_display_row();
7355 let point = DisplayPoint::new(row, 0);
7356 s.select_display_ranges([point..point]);
7357 });
7358
7359 true
7360 } else {
7361 false
7362 }
7363 }
7364
7365 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7366 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7367 }
7368
7369 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7370 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7371 }
7372
7373 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7374 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7375 }
7376
7377 pub fn go_to_type_definition_split(
7378 &mut self,
7379 _: &GoToTypeDefinitionSplit,
7380 cx: &mut ViewContext<Self>,
7381 ) {
7382 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7383 }
7384
7385 fn go_to_definition_of_kind(
7386 &mut self,
7387 kind: GotoDefinitionKind,
7388 split: bool,
7389 cx: &mut ViewContext<Self>,
7390 ) {
7391 let Some(workspace) = self.workspace() else {
7392 return;
7393 };
7394 let buffer = self.buffer.read(cx);
7395 let head = self.selections.newest::<usize>(cx).head();
7396 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7397 text_anchor
7398 } else {
7399 return;
7400 };
7401
7402 let project = workspace.read(cx).project().clone();
7403 let definitions = project.update(cx, |project, cx| match kind {
7404 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7405 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7406 });
7407
7408 cx.spawn(|editor, mut cx| async move {
7409 let definitions = definitions.await?;
7410 editor.update(&mut cx, |editor, cx| {
7411 editor.navigate_to_definitions(
7412 definitions
7413 .into_iter()
7414 .map(GoToDefinitionLink::Text)
7415 .collect(),
7416 split,
7417 cx,
7418 );
7419 })?;
7420 Ok::<(), anyhow::Error>(())
7421 })
7422 .detach_and_log_err(cx);
7423 }
7424
7425 pub fn navigate_to_definitions(
7426 &mut self,
7427 mut definitions: Vec<GoToDefinitionLink>,
7428 split: bool,
7429 cx: &mut ViewContext<Editor>,
7430 ) {
7431 let Some(workspace) = self.workspace() else {
7432 return;
7433 };
7434 let pane = workspace.read(cx).active_pane().clone();
7435 // If there is one definition, just open it directly
7436 if definitions.len() == 1 {
7437 let definition = definitions.pop().unwrap();
7438 let target_task = match definition {
7439 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7440 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7441 self.compute_target_location(lsp_location, server_id, cx)
7442 }
7443 };
7444 cx.spawn(|editor, mut cx| async move {
7445 let target = target_task.await.context("target resolution task")?;
7446 if let Some(target) = target {
7447 editor.update(&mut cx, |editor, cx| {
7448 let range = target.range.to_offset(target.buffer.read(cx));
7449 let range = editor.range_for_match(&range);
7450 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7451 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7452 s.select_ranges([range]);
7453 });
7454 } else {
7455 cx.window_context().defer(move |cx| {
7456 let target_editor: View<Self> =
7457 workspace.update(cx, |workspace, cx| {
7458 if split {
7459 workspace.split_project_item(target.buffer.clone(), cx)
7460 } else {
7461 workspace.open_project_item(target.buffer.clone(), cx)
7462 }
7463 });
7464 target_editor.update(cx, |target_editor, cx| {
7465 // When selecting a definition in a different buffer, disable the nav history
7466 // to avoid creating a history entry at the previous cursor location.
7467 pane.update(cx, |pane, _| pane.disable_history());
7468 target_editor.change_selections(
7469 Some(Autoscroll::fit()),
7470 cx,
7471 |s| {
7472 s.select_ranges([range]);
7473 },
7474 );
7475 pane.update(cx, |pane, _| pane.enable_history());
7476 });
7477 });
7478 }
7479 })
7480 } else {
7481 Ok(())
7482 }
7483 })
7484 .detach_and_log_err(cx);
7485 } else if !definitions.is_empty() {
7486 let replica_id = self.replica_id(cx);
7487 cx.spawn(|editor, mut cx| async move {
7488 let (title, location_tasks) = editor
7489 .update(&mut cx, |editor, cx| {
7490 let title = definitions
7491 .iter()
7492 .find_map(|definition| match definition {
7493 GoToDefinitionLink::Text(link) => {
7494 link.origin.as_ref().map(|origin| {
7495 let buffer = origin.buffer.read(cx);
7496 format!(
7497 "Definitions for {}",
7498 buffer
7499 .text_for_range(origin.range.clone())
7500 .collect::<String>()
7501 )
7502 })
7503 }
7504 GoToDefinitionLink::InlayHint(_, _) => None,
7505 })
7506 .unwrap_or("Definitions".to_string());
7507 let location_tasks = definitions
7508 .into_iter()
7509 .map(|definition| match definition {
7510 GoToDefinitionLink::Text(link) => {
7511 Task::Ready(Some(Ok(Some(link.target))))
7512 }
7513 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7514 editor.compute_target_location(lsp_location, server_id, cx)
7515 }
7516 })
7517 .collect::<Vec<_>>();
7518 (title, location_tasks)
7519 })
7520 .context("location tasks preparation")?;
7521
7522 let locations = futures::future::join_all(location_tasks)
7523 .await
7524 .into_iter()
7525 .filter_map(|location| location.transpose())
7526 .collect::<Result<_>>()
7527 .context("location tasks")?;
7528 workspace.update(&mut cx, |workspace, cx| {
7529 Self::open_locations_in_multibuffer(
7530 workspace, locations, replica_id, title, split, cx,
7531 )
7532 });
7533
7534 anyhow::Ok(())
7535 })
7536 .detach_and_log_err(cx);
7537 }
7538 }
7539
7540 fn compute_target_location(
7541 &self,
7542 lsp_location: lsp::Location,
7543 server_id: LanguageServerId,
7544 cx: &mut ViewContext<Editor>,
7545 ) -> Task<anyhow::Result<Option<Location>>> {
7546 let Some(project) = self.project.clone() else {
7547 return Task::Ready(Some(Ok(None)));
7548 };
7549
7550 cx.spawn(move |editor, mut cx| async move {
7551 let location_task = editor.update(&mut cx, |editor, cx| {
7552 project.update(cx, |project, cx| {
7553 let language_server_name =
7554 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7555 project
7556 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7557 .map(|(_, lsp_adapter)| {
7558 LanguageServerName(Arc::from(lsp_adapter.name()))
7559 })
7560 });
7561 language_server_name.map(|language_server_name| {
7562 project.open_local_buffer_via_lsp(
7563 lsp_location.uri.clone(),
7564 server_id,
7565 language_server_name,
7566 cx,
7567 )
7568 })
7569 })
7570 })?;
7571 let location = match location_task {
7572 Some(task) => Some({
7573 let target_buffer_handle = task.await.context("open local buffer")?;
7574 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
7575 let target_start = target_buffer
7576 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
7577 let target_end = target_buffer
7578 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
7579 target_buffer.anchor_after(target_start)
7580 ..target_buffer.anchor_before(target_end)
7581 })?;
7582 Location {
7583 buffer: target_buffer_handle,
7584 range,
7585 }
7586 }),
7587 None => None,
7588 };
7589 Ok(location)
7590 })
7591 }
7592
7593 pub fn find_all_references(
7594 &mut self,
7595 _: &FindAllReferences,
7596 cx: &mut ViewContext<Self>,
7597 ) -> Option<Task<Result<()>>> {
7598 let buffer = self.buffer.read(cx);
7599 let head = self.selections.newest::<usize>(cx).head();
7600 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7601 let replica_id = self.replica_id(cx);
7602
7603 let workspace = self.workspace()?;
7604 let project = workspace.read(cx).project().clone();
7605 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7606 Some(cx.spawn(|_, mut cx| async move {
7607 let locations = references.await?;
7608 if locations.is_empty() {
7609 return Ok(());
7610 }
7611
7612 workspace.update(&mut cx, |workspace, cx| {
7613 let title = locations
7614 .first()
7615 .as_ref()
7616 .map(|location| {
7617 let buffer = location.buffer.read(cx);
7618 format!(
7619 "References to `{}`",
7620 buffer
7621 .text_for_range(location.range.clone())
7622 .collect::<String>()
7623 )
7624 })
7625 .unwrap();
7626 Self::open_locations_in_multibuffer(
7627 workspace, locations, replica_id, title, false, cx,
7628 );
7629 })?;
7630
7631 Ok(())
7632 }))
7633 }
7634
7635 /// Opens a multibuffer with the given project locations in it
7636 pub fn open_locations_in_multibuffer(
7637 workspace: &mut Workspace,
7638 mut locations: Vec<Location>,
7639 replica_id: ReplicaId,
7640 title: String,
7641 split: bool,
7642 cx: &mut ViewContext<Workspace>,
7643 ) {
7644 // If there are multiple definitions, open them in a multibuffer
7645 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7646 let mut locations = locations.into_iter().peekable();
7647 let mut ranges_to_highlight = Vec::new();
7648
7649 let excerpt_buffer = cx.build_model(|cx| {
7650 let mut multibuffer = MultiBuffer::new(replica_id);
7651 while let Some(location) = locations.next() {
7652 let buffer = location.buffer.read(cx);
7653 let mut ranges_for_buffer = Vec::new();
7654 let range = location.range.to_offset(buffer);
7655 ranges_for_buffer.push(range.clone());
7656
7657 while let Some(next_location) = locations.peek() {
7658 if next_location.buffer == location.buffer {
7659 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7660 locations.next();
7661 } else {
7662 break;
7663 }
7664 }
7665
7666 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7667 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7668 location.buffer.clone(),
7669 ranges_for_buffer,
7670 1,
7671 cx,
7672 ))
7673 }
7674
7675 multibuffer.with_title(title)
7676 });
7677
7678 let editor = cx.build_view(|cx| {
7679 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7680 });
7681 editor.update(cx, |editor, cx| {
7682 editor.highlight_background::<Self>(
7683 ranges_to_highlight,
7684 |theme| theme.editor_highlighted_line_background,
7685 cx,
7686 );
7687 });
7688 if split {
7689 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7690 } else {
7691 workspace.add_item(Box::new(editor), cx);
7692 }
7693 }
7694
7695 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7696 use language::ToOffset as _;
7697
7698 let project = self.project.clone()?;
7699 let selection = self.selections.newest_anchor().clone();
7700 let (cursor_buffer, cursor_buffer_position) = self
7701 .buffer
7702 .read(cx)
7703 .text_anchor_for_position(selection.head(), cx)?;
7704 let (tail_buffer, _) = self
7705 .buffer
7706 .read(cx)
7707 .text_anchor_for_position(selection.tail(), cx)?;
7708 if tail_buffer != cursor_buffer {
7709 return None;
7710 }
7711
7712 let snapshot = cursor_buffer.read(cx).snapshot();
7713 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7714 let prepare_rename = project.update(cx, |project, cx| {
7715 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7716 });
7717
7718 Some(cx.spawn(|this, mut cx| async move {
7719 let rename_range = if let Some(range) = prepare_rename.await? {
7720 Some(range)
7721 } else {
7722 this.update(&mut cx, |this, cx| {
7723 let buffer = this.buffer.read(cx).snapshot(cx);
7724 let mut buffer_highlights = this
7725 .document_highlights_for_position(selection.head(), &buffer)
7726 .filter(|highlight| {
7727 highlight.start.excerpt_id == selection.head().excerpt_id
7728 && highlight.end.excerpt_id == selection.head().excerpt_id
7729 });
7730 buffer_highlights
7731 .next()
7732 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7733 })?
7734 };
7735 if let Some(rename_range) = rename_range {
7736 let rename_buffer_range = rename_range.to_offset(&snapshot);
7737 let cursor_offset_in_rename_range =
7738 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7739
7740 this.update(&mut cx, |this, cx| {
7741 this.take_rename(false, cx);
7742 let buffer = this.buffer.read(cx).read(cx);
7743 let cursor_offset = selection.head().to_offset(&buffer);
7744 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7745 let rename_end = rename_start + rename_buffer_range.len();
7746 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7747 let mut old_highlight_id = None;
7748 let old_name: Arc<str> = buffer
7749 .chunks(rename_start..rename_end, true)
7750 .map(|chunk| {
7751 if old_highlight_id.is_none() {
7752 old_highlight_id = chunk.syntax_highlight_id;
7753 }
7754 chunk.text
7755 })
7756 .collect::<String>()
7757 .into();
7758
7759 drop(buffer);
7760
7761 // Position the selection in the rename editor so that it matches the current selection.
7762 this.show_local_selections = false;
7763 let rename_editor = cx.build_view(|cx| {
7764 let mut editor = Editor::single_line(cx);
7765 editor.buffer.update(cx, |buffer, cx| {
7766 buffer.edit([(0..0, old_name.clone())], None, cx)
7767 });
7768 editor.select_all(&SelectAll, cx);
7769 editor
7770 });
7771
7772 let ranges = this
7773 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7774 .into_iter()
7775 .flat_map(|(_, ranges)| ranges.into_iter())
7776 .chain(
7777 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7778 .into_iter()
7779 .flat_map(|(_, ranges)| ranges.into_iter()),
7780 )
7781 .collect();
7782
7783 this.highlight_text::<Rename>(
7784 ranges,
7785 HighlightStyle {
7786 fade_out: Some(0.6),
7787 ..Default::default()
7788 },
7789 cx,
7790 );
7791 let rename_focus_handle = rename_editor.focus_handle(cx);
7792 cx.focus(&rename_focus_handle);
7793 let block_id = this.insert_blocks(
7794 [BlockProperties {
7795 style: BlockStyle::Flex,
7796 position: range.start.clone(),
7797 height: 1,
7798 render: Arc::new({
7799 let rename_editor = rename_editor.clone();
7800 move |cx: &mut BlockContext| {
7801 let mut text_style = cx.editor_style.text.clone();
7802 if let Some(highlight_style) = old_highlight_id
7803 .and_then(|h| h.style(&cx.editor_style.syntax))
7804 {
7805 text_style = text_style.highlight(highlight_style);
7806 }
7807 div()
7808 .pl(cx.anchor_x)
7809 .child(EditorElement::new(
7810 &rename_editor,
7811 EditorStyle {
7812 background: cx.theme().system().transparent,
7813 local_player: cx.editor_style.local_player,
7814 text: text_style,
7815 scrollbar_width: cx.editor_style.scrollbar_width,
7816 syntax: cx.editor_style.syntax.clone(),
7817 diagnostic_style: cx
7818 .editor_style
7819 .diagnostic_style
7820 .clone(),
7821 },
7822 ))
7823 .into_any_element()
7824 }
7825 }),
7826 disposition: BlockDisposition::Below,
7827 }],
7828 Some(Autoscroll::fit()),
7829 cx,
7830 )[0];
7831 this.pending_rename = Some(RenameState {
7832 range,
7833 old_name,
7834 editor: rename_editor,
7835 block_id,
7836 });
7837 })?;
7838 }
7839
7840 Ok(())
7841 }))
7842 }
7843
7844 pub fn confirm_rename(
7845 &mut self,
7846 _: &ConfirmRename,
7847 cx: &mut ViewContext<Self>,
7848 ) -> Option<Task<Result<()>>> {
7849 let rename = self.take_rename(false, cx)?;
7850 let workspace = self.workspace()?;
7851 let (start_buffer, start) = self
7852 .buffer
7853 .read(cx)
7854 .text_anchor_for_position(rename.range.start.clone(), cx)?;
7855 let (end_buffer, end) = self
7856 .buffer
7857 .read(cx)
7858 .text_anchor_for_position(rename.range.end.clone(), cx)?;
7859 if start_buffer != end_buffer {
7860 return None;
7861 }
7862
7863 let buffer = start_buffer;
7864 let range = start..end;
7865 let old_name = rename.old_name;
7866 let new_name = rename.editor.read(cx).text(cx);
7867
7868 let rename = workspace
7869 .read(cx)
7870 .project()
7871 .clone()
7872 .update(cx, |project, cx| {
7873 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7874 });
7875 let workspace = workspace.downgrade();
7876
7877 Some(cx.spawn(|editor, mut cx| async move {
7878 let project_transaction = rename.await?;
7879 Self::open_project_transaction(
7880 &editor,
7881 workspace,
7882 project_transaction,
7883 format!("Rename: {} → {}", old_name, new_name),
7884 cx.clone(),
7885 )
7886 .await?;
7887
7888 editor.update(&mut cx, |editor, cx| {
7889 editor.refresh_document_highlights(cx);
7890 })?;
7891 Ok(())
7892 }))
7893 }
7894
7895 fn take_rename(
7896 &mut self,
7897 moving_cursor: bool,
7898 cx: &mut ViewContext<Self>,
7899 ) -> Option<RenameState> {
7900 let rename = self.pending_rename.take()?;
7901 if rename.editor.focus_handle(cx).is_focused(cx) {
7902 cx.focus(&self.focus_handle);
7903 }
7904
7905 self.remove_blocks(
7906 [rename.block_id].into_iter().collect(),
7907 Some(Autoscroll::fit()),
7908 cx,
7909 );
7910 self.clear_highlights::<Rename>(cx);
7911 self.show_local_selections = true;
7912
7913 if moving_cursor {
7914 let rename_editor = rename.editor.read(cx);
7915 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7916
7917 // Update the selection to match the position of the selection inside
7918 // the rename editor.
7919 let snapshot = self.buffer.read(cx).read(cx);
7920 let rename_range = rename.range.to_offset(&snapshot);
7921 let cursor_in_editor = snapshot
7922 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7923 .min(rename_range.end);
7924 drop(snapshot);
7925
7926 self.change_selections(None, cx, |s| {
7927 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7928 });
7929 } else {
7930 self.refresh_document_highlights(cx);
7931 }
7932
7933 Some(rename)
7934 }
7935
7936 #[cfg(any(test, feature = "test-support"))]
7937 pub fn pending_rename(&self) -> Option<&RenameState> {
7938 self.pending_rename.as_ref()
7939 }
7940
7941 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7942 let project = match &self.project {
7943 Some(project) => project.clone(),
7944 None => return None,
7945 };
7946
7947 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7948 }
7949
7950 fn perform_format(
7951 &mut self,
7952 project: Model<Project>,
7953 trigger: FormatTrigger,
7954 cx: &mut ViewContext<Self>,
7955 ) -> Task<Result<()>> {
7956 let buffer = self.buffer().clone();
7957 let buffers = buffer.read(cx).all_buffers();
7958
7959 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
7960 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7961
7962 cx.spawn(|_, mut cx| async move {
7963 let transaction = futures::select_biased! {
7964 _ = timeout => {
7965 log::warn!("timed out waiting for formatting");
7966 None
7967 }
7968 transaction = format.log_err().fuse() => transaction,
7969 };
7970
7971 buffer.update(&mut cx, |buffer, cx| {
7972 if let Some(transaction) = transaction {
7973 if !buffer.is_singleton() {
7974 buffer.push_transaction(&transaction.0, cx);
7975 }
7976 }
7977
7978 cx.notify();
7979 });
7980
7981 Ok(())
7982 })
7983 }
7984
7985 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7986 if let Some(project) = self.project.clone() {
7987 self.buffer.update(cx, |multi_buffer, cx| {
7988 project.update(cx, |project, cx| {
7989 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7990 });
7991 })
7992 }
7993 }
7994
7995 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7996 cx.show_character_palette();
7997 }
7998
7999 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
8000 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
8001 let buffer = self.buffer.read(cx).snapshot(cx);
8002 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
8003 let is_valid = buffer
8004 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
8005 .any(|entry| {
8006 entry.diagnostic.is_primary
8007 && !entry.range.is_empty()
8008 && entry.range.start == primary_range_start
8009 && entry.diagnostic.message == active_diagnostics.primary_message
8010 });
8011
8012 if is_valid != active_diagnostics.is_valid {
8013 active_diagnostics.is_valid = is_valid;
8014 let mut new_styles = HashMap::default();
8015 for (block_id, diagnostic) in &active_diagnostics.blocks {
8016 new_styles.insert(
8017 *block_id,
8018 diagnostic_block_renderer(diagnostic.clone(), is_valid),
8019 );
8020 }
8021 self.display_map
8022 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
8023 }
8024 }
8025 }
8026
8027 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
8028 self.dismiss_diagnostics(cx);
8029 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
8030 let buffer = self.buffer.read(cx).snapshot(cx);
8031
8032 let mut primary_range = None;
8033 let mut primary_message = None;
8034 let mut group_end = Point::zero();
8035 let diagnostic_group = buffer
8036 .diagnostic_group::<Point>(group_id)
8037 .map(|entry| {
8038 if entry.range.end > group_end {
8039 group_end = entry.range.end;
8040 }
8041 if entry.diagnostic.is_primary {
8042 primary_range = Some(entry.range.clone());
8043 primary_message = Some(entry.diagnostic.message.clone());
8044 }
8045 entry
8046 })
8047 .collect::<Vec<_>>();
8048 let primary_range = primary_range?;
8049 let primary_message = primary_message?;
8050 let primary_range =
8051 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8052
8053 let blocks = display_map
8054 .insert_blocks(
8055 diagnostic_group.iter().map(|entry| {
8056 let diagnostic = entry.diagnostic.clone();
8057 let message_height = diagnostic.message.lines().count() as u8;
8058 BlockProperties {
8059 style: BlockStyle::Fixed,
8060 position: buffer.anchor_after(entry.range.start),
8061 height: message_height,
8062 render: diagnostic_block_renderer(diagnostic, true),
8063 disposition: BlockDisposition::Below,
8064 }
8065 }),
8066 cx,
8067 )
8068 .into_iter()
8069 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8070 .collect();
8071
8072 Some(ActiveDiagnosticGroup {
8073 primary_range,
8074 primary_message,
8075 blocks,
8076 is_valid: true,
8077 })
8078 });
8079 self.active_diagnostics.is_some()
8080 }
8081
8082 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8083 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8084 self.display_map.update(cx, |display_map, cx| {
8085 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8086 });
8087 cx.notify();
8088 }
8089 }
8090
8091 pub fn set_selections_from_remote(
8092 &mut self,
8093 selections: Vec<Selection<Anchor>>,
8094 pending_selection: Option<Selection<Anchor>>,
8095 cx: &mut ViewContext<Self>,
8096 ) {
8097 let old_cursor_position = self.selections.newest_anchor().head();
8098 self.selections.change_with(cx, |s| {
8099 s.select_anchors(selections);
8100 if let Some(pending_selection) = pending_selection {
8101 s.set_pending(pending_selection, SelectMode::Character);
8102 } else {
8103 s.clear_pending();
8104 }
8105 });
8106 self.selections_did_change(false, &old_cursor_position, cx);
8107 }
8108
8109 fn push_to_selection_history(&mut self) {
8110 self.selection_history.push(SelectionHistoryEntry {
8111 selections: self.selections.disjoint_anchors(),
8112 select_next_state: self.select_next_state.clone(),
8113 select_prev_state: self.select_prev_state.clone(),
8114 add_selections_state: self.add_selections_state.clone(),
8115 });
8116 }
8117
8118 pub fn transact(
8119 &mut self,
8120 cx: &mut ViewContext<Self>,
8121 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8122 ) -> Option<TransactionId> {
8123 self.start_transaction_at(Instant::now(), cx);
8124 update(self, cx);
8125 self.end_transaction_at(Instant::now(), cx)
8126 }
8127
8128 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8129 self.end_selection(cx);
8130 if let Some(tx_id) = self
8131 .buffer
8132 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8133 {
8134 self.selection_history
8135 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8136 }
8137 }
8138
8139 fn end_transaction_at(
8140 &mut self,
8141 now: Instant,
8142 cx: &mut ViewContext<Self>,
8143 ) -> Option<TransactionId> {
8144 if let Some(tx_id) = self
8145 .buffer
8146 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8147 {
8148 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8149 *end_selections = Some(self.selections.disjoint_anchors());
8150 } else {
8151 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8152 }
8153
8154 cx.emit(EditorEvent::Edited);
8155 Some(tx_id)
8156 } else {
8157 None
8158 }
8159 }
8160
8161 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8162 let mut fold_ranges = Vec::new();
8163
8164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8165
8166 let selections = self.selections.all_adjusted(cx);
8167 for selection in selections {
8168 let range = selection.range().sorted();
8169 let buffer_start_row = range.start.row;
8170
8171 for row in (0..=range.end.row).rev() {
8172 let fold_range = display_map.foldable_range(row);
8173
8174 if let Some(fold_range) = fold_range {
8175 if fold_range.end.row >= buffer_start_row {
8176 fold_ranges.push(fold_range);
8177 if row <= range.start.row {
8178 break;
8179 }
8180 }
8181 }
8182 }
8183 }
8184
8185 self.fold_ranges(fold_ranges, true, cx);
8186 }
8187
8188 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8189 let buffer_row = fold_at.buffer_row;
8190 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8191
8192 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8193 let autoscroll = self
8194 .selections
8195 .all::<Point>(cx)
8196 .iter()
8197 .any(|selection| fold_range.overlaps(&selection.range()));
8198
8199 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8200 }
8201 }
8202
8203 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8204 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8205 let buffer = &display_map.buffer_snapshot;
8206 let selections = self.selections.all::<Point>(cx);
8207 let ranges = selections
8208 .iter()
8209 .map(|s| {
8210 let range = s.display_range(&display_map).sorted();
8211 let mut start = range.start.to_point(&display_map);
8212 let mut end = range.end.to_point(&display_map);
8213 start.column = 0;
8214 end.column = buffer.line_len(end.row);
8215 start..end
8216 })
8217 .collect::<Vec<_>>();
8218
8219 self.unfold_ranges(ranges, true, true, cx);
8220 }
8221
8222 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8223 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8224
8225 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8226 ..Point::new(
8227 unfold_at.buffer_row,
8228 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8229 );
8230
8231 let autoscroll = self
8232 .selections
8233 .all::<Point>(cx)
8234 .iter()
8235 .any(|selection| selection.range().overlaps(&intersection_range));
8236
8237 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8238 }
8239
8240 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8241 let selections = self.selections.all::<Point>(cx);
8242 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8243 let line_mode = self.selections.line_mode;
8244 let ranges = selections.into_iter().map(|s| {
8245 if line_mode {
8246 let start = Point::new(s.start.row, 0);
8247 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8248 start..end
8249 } else {
8250 s.start..s.end
8251 }
8252 });
8253 self.fold_ranges(ranges, true, cx);
8254 }
8255
8256 pub fn fold_ranges<T: ToOffset + Clone>(
8257 &mut self,
8258 ranges: impl IntoIterator<Item = Range<T>>,
8259 auto_scroll: bool,
8260 cx: &mut ViewContext<Self>,
8261 ) {
8262 let mut ranges = ranges.into_iter().peekable();
8263 if ranges.peek().is_some() {
8264 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8265
8266 if auto_scroll {
8267 self.request_autoscroll(Autoscroll::fit(), cx);
8268 }
8269
8270 cx.notify();
8271 }
8272 }
8273
8274 pub fn unfold_ranges<T: ToOffset + Clone>(
8275 &mut self,
8276 ranges: impl IntoIterator<Item = Range<T>>,
8277 inclusive: bool,
8278 auto_scroll: bool,
8279 cx: &mut ViewContext<Self>,
8280 ) {
8281 let mut ranges = ranges.into_iter().peekable();
8282 if ranges.peek().is_some() {
8283 self.display_map
8284 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8285 if auto_scroll {
8286 self.request_autoscroll(Autoscroll::fit(), cx);
8287 }
8288
8289 cx.notify();
8290 }
8291 }
8292
8293 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
8294 if hovered != self.gutter_hovered {
8295 self.gutter_hovered = hovered;
8296 cx.notify();
8297 }
8298 }
8299
8300 pub fn insert_blocks(
8301 &mut self,
8302 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8303 autoscroll: Option<Autoscroll>,
8304 cx: &mut ViewContext<Self>,
8305 ) -> Vec<BlockId> {
8306 let blocks = self
8307 .display_map
8308 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8309 if let Some(autoscroll) = autoscroll {
8310 self.request_autoscroll(autoscroll, cx);
8311 }
8312 blocks
8313 }
8314
8315 pub fn replace_blocks(
8316 &mut self,
8317 blocks: HashMap<BlockId, RenderBlock>,
8318 autoscroll: Option<Autoscroll>,
8319 cx: &mut ViewContext<Self>,
8320 ) {
8321 self.display_map
8322 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8323 if let Some(autoscroll) = autoscroll {
8324 self.request_autoscroll(autoscroll, cx);
8325 }
8326 }
8327
8328 pub fn remove_blocks(
8329 &mut self,
8330 block_ids: HashSet<BlockId>,
8331 autoscroll: Option<Autoscroll>,
8332 cx: &mut ViewContext<Self>,
8333 ) {
8334 self.display_map.update(cx, |display_map, cx| {
8335 display_map.remove_blocks(block_ids, cx)
8336 });
8337 if let Some(autoscroll) = autoscroll {
8338 self.request_autoscroll(autoscroll, cx);
8339 }
8340 }
8341
8342 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8343 self.display_map
8344 .update(cx, |map, cx| map.snapshot(cx))
8345 .longest_row()
8346 }
8347
8348 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8349 self.display_map
8350 .update(cx, |map, cx| map.snapshot(cx))
8351 .max_point()
8352 }
8353
8354 pub fn text(&self, cx: &AppContext) -> String {
8355 self.buffer.read(cx).read(cx).text()
8356 }
8357
8358 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8359 self.transact(cx, |this, cx| {
8360 this.buffer
8361 .read(cx)
8362 .as_singleton()
8363 .expect("you can only call set_text on editors for singleton buffers")
8364 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8365 });
8366 }
8367
8368 pub fn display_text(&self, cx: &mut AppContext) -> String {
8369 self.display_map
8370 .update(cx, |map, cx| map.snapshot(cx))
8371 .text()
8372 }
8373
8374 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8375 let mut wrap_guides = smallvec::smallvec![];
8376
8377 if self.show_wrap_guides == Some(false) {
8378 return wrap_guides;
8379 }
8380
8381 let settings = self.buffer.read(cx).settings_at(0, cx);
8382 if settings.show_wrap_guides {
8383 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8384 wrap_guides.push((soft_wrap as usize, true));
8385 }
8386 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8387 }
8388
8389 wrap_guides
8390 }
8391
8392 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8393 let settings = self.buffer.read(cx).settings_at(0, cx);
8394 let mode = self
8395 .soft_wrap_mode_override
8396 .unwrap_or_else(|| settings.soft_wrap);
8397 match mode {
8398 language_settings::SoftWrap::None => SoftWrap::None,
8399 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8400 language_settings::SoftWrap::PreferredLineLength => {
8401 SoftWrap::Column(settings.preferred_line_length)
8402 }
8403 }
8404 }
8405
8406 pub fn set_soft_wrap_mode(
8407 &mut self,
8408 mode: language_settings::SoftWrap,
8409 cx: &mut ViewContext<Self>,
8410 ) {
8411 self.soft_wrap_mode_override = Some(mode);
8412 cx.notify();
8413 }
8414
8415 pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8416 self.display_map
8417 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8418 }
8419
8420 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8421 if self.soft_wrap_mode_override.is_some() {
8422 self.soft_wrap_mode_override.take();
8423 } else {
8424 let soft_wrap = match self.soft_wrap_mode(cx) {
8425 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8426 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8427 };
8428 self.soft_wrap_mode_override = Some(soft_wrap);
8429 }
8430 cx.notify();
8431 }
8432
8433 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8434 self.show_gutter = show_gutter;
8435 cx.notify();
8436 }
8437
8438 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8439 self.show_wrap_guides = Some(show_gutter);
8440 cx.notify();
8441 }
8442
8443 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8444 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8445 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8446 cx.reveal_path(&file.abs_path(cx));
8447 }
8448 }
8449 }
8450
8451 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8452 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8453 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8454 if let Some(path) = file.abs_path(cx).to_str() {
8455 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8456 }
8457 }
8458 }
8459 }
8460
8461 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8462 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8463 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8464 if let Some(path) = file.path().to_str() {
8465 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8466 }
8467 }
8468 }
8469 }
8470
8471 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8472 self.highlighted_rows = rows;
8473 }
8474
8475 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8476 self.highlighted_rows.clone()
8477 }
8478
8479 pub fn highlight_background<T: 'static>(
8480 &mut self,
8481 ranges: Vec<Range<Anchor>>,
8482 color_fetcher: fn(&ThemeColors) -> Hsla,
8483 cx: &mut ViewContext<Self>,
8484 ) {
8485 self.background_highlights
8486 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8487 cx.notify();
8488 }
8489
8490 pub fn highlight_inlay_background<T: 'static>(
8491 &mut self,
8492 ranges: Vec<InlayHighlight>,
8493 color_fetcher: fn(&ThemeColors) -> Hsla,
8494 cx: &mut ViewContext<Self>,
8495 ) {
8496 // TODO: no actual highlights happen for inlays currently, find a way to do that
8497 self.inlay_background_highlights
8498 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8499 cx.notify();
8500 }
8501
8502 pub fn clear_background_highlights<T: 'static>(
8503 &mut self,
8504 cx: &mut ViewContext<Self>,
8505 ) -> Option<BackgroundHighlight> {
8506 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8507 let inlay_highlights = self
8508 .inlay_background_highlights
8509 .remove(&Some(TypeId::of::<T>()));
8510 if text_highlights.is_some() || inlay_highlights.is_some() {
8511 cx.notify();
8512 }
8513 text_highlights
8514 }
8515
8516 #[cfg(feature = "test-support")]
8517 pub fn all_text_background_highlights(
8518 &mut self,
8519 cx: &mut ViewContext<Self>,
8520 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8521 let snapshot = self.snapshot(cx);
8522 let buffer = &snapshot.buffer_snapshot;
8523 let start = buffer.anchor_before(0);
8524 let end = buffer.anchor_after(buffer.len());
8525 let theme = cx.theme().colors();
8526 self.background_highlights_in_range(start..end, &snapshot, theme)
8527 }
8528
8529 fn document_highlights_for_position<'a>(
8530 &'a self,
8531 position: Anchor,
8532 buffer: &'a MultiBufferSnapshot,
8533 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8534 let read_highlights = self
8535 .background_highlights
8536 .get(&TypeId::of::<DocumentHighlightRead>())
8537 .map(|h| &h.1);
8538 let write_highlights = self
8539 .background_highlights
8540 .get(&TypeId::of::<DocumentHighlightWrite>())
8541 .map(|h| &h.1);
8542 let left_position = position.bias_left(buffer);
8543 let right_position = position.bias_right(buffer);
8544 read_highlights
8545 .into_iter()
8546 .chain(write_highlights)
8547 .flat_map(move |ranges| {
8548 let start_ix = match ranges.binary_search_by(|probe| {
8549 let cmp = probe.end.cmp(&left_position, buffer);
8550 if cmp.is_ge() {
8551 Ordering::Greater
8552 } else {
8553 Ordering::Less
8554 }
8555 }) {
8556 Ok(i) | Err(i) => i,
8557 };
8558
8559 let right_position = right_position.clone();
8560 ranges[start_ix..]
8561 .iter()
8562 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8563 })
8564 }
8565
8566 pub fn background_highlights_in_range(
8567 &self,
8568 search_range: Range<Anchor>,
8569 display_snapshot: &DisplaySnapshot,
8570 theme: &ThemeColors,
8571 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8572 let mut results = Vec::new();
8573 for (color_fetcher, ranges) in self.background_highlights.values() {
8574 let color = color_fetcher(theme);
8575 let start_ix = match ranges.binary_search_by(|probe| {
8576 let cmp = probe
8577 .end
8578 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8579 if cmp.is_gt() {
8580 Ordering::Greater
8581 } else {
8582 Ordering::Less
8583 }
8584 }) {
8585 Ok(i) | Err(i) => i,
8586 };
8587 for range in &ranges[start_ix..] {
8588 if range
8589 .start
8590 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8591 .is_ge()
8592 {
8593 break;
8594 }
8595
8596 let start = range.start.to_display_point(&display_snapshot);
8597 let end = range.end.to_display_point(&display_snapshot);
8598 results.push((start..end, color))
8599 }
8600 }
8601 results
8602 }
8603
8604 pub fn background_highlight_row_ranges<T: 'static>(
8605 &self,
8606 search_range: Range<Anchor>,
8607 display_snapshot: &DisplaySnapshot,
8608 count: usize,
8609 ) -> Vec<RangeInclusive<DisplayPoint>> {
8610 let mut results = Vec::new();
8611 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8612 return vec![];
8613 };
8614
8615 let start_ix = match ranges.binary_search_by(|probe| {
8616 let cmp = probe
8617 .end
8618 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8619 if cmp.is_gt() {
8620 Ordering::Greater
8621 } else {
8622 Ordering::Less
8623 }
8624 }) {
8625 Ok(i) | Err(i) => i,
8626 };
8627 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8628 if let (Some(start_display), Some(end_display)) = (start, end) {
8629 results.push(
8630 start_display.to_display_point(display_snapshot)
8631 ..=end_display.to_display_point(display_snapshot),
8632 );
8633 }
8634 };
8635 let mut start_row: Option<Point> = None;
8636 let mut end_row: Option<Point> = None;
8637 if ranges.len() > count {
8638 return Vec::new();
8639 }
8640 for range in &ranges[start_ix..] {
8641 if range
8642 .start
8643 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8644 .is_ge()
8645 {
8646 break;
8647 }
8648 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8649 if let Some(current_row) = &end_row {
8650 if end.row == current_row.row {
8651 continue;
8652 }
8653 }
8654 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8655 if start_row.is_none() {
8656 assert_eq!(end_row, None);
8657 start_row = Some(start);
8658 end_row = Some(end);
8659 continue;
8660 }
8661 if let Some(current_end) = end_row.as_mut() {
8662 if start.row > current_end.row + 1 {
8663 push_region(start_row, end_row);
8664 start_row = Some(start);
8665 end_row = Some(end);
8666 } else {
8667 // Merge two hunks.
8668 *current_end = end;
8669 }
8670 } else {
8671 unreachable!();
8672 }
8673 }
8674 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8675 push_region(start_row, end_row);
8676 results
8677 }
8678
8679 pub fn highlight_text<T: 'static>(
8680 &mut self,
8681 ranges: Vec<Range<Anchor>>,
8682 style: HighlightStyle,
8683 cx: &mut ViewContext<Self>,
8684 ) {
8685 self.display_map.update(cx, |map, _| {
8686 map.highlight_text(TypeId::of::<T>(), ranges, style)
8687 });
8688 cx.notify();
8689 }
8690
8691 pub fn highlight_inlays<T: 'static>(
8692 &mut self,
8693 highlights: Vec<InlayHighlight>,
8694 style: HighlightStyle,
8695 cx: &mut ViewContext<Self>,
8696 ) {
8697 self.display_map.update(cx, |map, _| {
8698 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8699 });
8700 cx.notify();
8701 }
8702
8703 pub fn text_highlights<'a, T: 'static>(
8704 &'a self,
8705 cx: &'a AppContext,
8706 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8707 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8708 }
8709
8710 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8711 let cleared = self
8712 .display_map
8713 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8714 if cleared {
8715 cx.notify();
8716 }
8717 }
8718
8719 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8720 self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
8721 }
8722
8723 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8724 cx.notify();
8725 }
8726
8727 fn on_buffer_event(
8728 &mut self,
8729 multibuffer: Model<MultiBuffer>,
8730 event: &multi_buffer::Event,
8731 cx: &mut ViewContext<Self>,
8732 ) {
8733 match event {
8734 multi_buffer::Event::Edited {
8735 sigleton_buffer_edited,
8736 } => {
8737 self.refresh_active_diagnostics(cx);
8738 self.refresh_code_actions(cx);
8739 if self.has_active_copilot_suggestion(cx) {
8740 self.update_visible_copilot_suggestion(cx);
8741 }
8742 cx.emit(EditorEvent::BufferEdited);
8743 cx.emit(ItemEvent::Edit);
8744 cx.emit(ItemEvent::UpdateBreadcrumbs);
8745 cx.emit(SearchEvent::MatchesInvalidated);
8746
8747 if *sigleton_buffer_edited {
8748 if let Some(project) = &self.project {
8749 let project = project.read(cx);
8750 let languages_affected = multibuffer
8751 .read(cx)
8752 .all_buffers()
8753 .into_iter()
8754 .filter_map(|buffer| {
8755 let buffer = buffer.read(cx);
8756 let language = buffer.language()?;
8757 if project.is_local()
8758 && project.language_servers_for_buffer(buffer, cx).count() == 0
8759 {
8760 None
8761 } else {
8762 Some(language)
8763 }
8764 })
8765 .cloned()
8766 .collect::<HashSet<_>>();
8767 if !languages_affected.is_empty() {
8768 self.refresh_inlay_hints(
8769 InlayHintRefreshReason::BufferEdited(languages_affected),
8770 cx,
8771 );
8772 }
8773 }
8774 }
8775 }
8776 multi_buffer::Event::ExcerptsAdded {
8777 buffer,
8778 predecessor,
8779 excerpts,
8780 } => {
8781 cx.emit(EditorEvent::ExcerptsAdded {
8782 buffer: buffer.clone(),
8783 predecessor: *predecessor,
8784 excerpts: excerpts.clone(),
8785 });
8786 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8787 }
8788 multi_buffer::Event::ExcerptsRemoved { ids } => {
8789 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8790 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
8791 }
8792 multi_buffer::Event::Reparsed => {
8793 cx.emit(ItemEvent::UpdateBreadcrumbs);
8794 }
8795 multi_buffer::Event::DirtyChanged => {
8796 cx.emit(ItemEvent::UpdateTab);
8797 }
8798 multi_buffer::Event::Saved
8799 | multi_buffer::Event::FileHandleChanged
8800 | multi_buffer::Event::Reloaded => {
8801 cx.emit(ItemEvent::UpdateTab);
8802 cx.emit(ItemEvent::UpdateBreadcrumbs);
8803 }
8804 multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
8805 multi_buffer::Event::Closed => cx.emit(ItemEvent::CloseItem),
8806 multi_buffer::Event::DiagnosticsUpdated => {
8807 self.refresh_active_diagnostics(cx);
8808 }
8809 _ => {}
8810 };
8811 }
8812
8813 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
8814 cx.notify();
8815 }
8816
8817 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8818 self.refresh_copilot_suggestions(true, cx);
8819 self.refresh_inlay_hints(
8820 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8821 self.selections.newest_anchor().head(),
8822 &self.buffer.read(cx).snapshot(cx),
8823 cx,
8824 )),
8825 cx,
8826 );
8827 }
8828
8829 // pub fn set_searchable(&mut self, searchable: bool) {
8830 // self.searchable = searchable;
8831 // }
8832
8833 // pub fn searchable(&self) -> bool {
8834 // self.searchable
8835 // }
8836
8837 // fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8838 // let active_item = workspace.active_item(cx);
8839 // let editor_handle = if let Some(editor) = active_item
8840 // .as_ref()
8841 // .and_then(|item| item.act_as::<Self>(cx))
8842 // {
8843 // editor
8844 // } else {
8845 // cx.propagate();
8846 // return;
8847 // };
8848
8849 // let editor = editor_handle.read(cx);
8850 // let buffer = editor.buffer.read(cx);
8851 // if buffer.is_singleton() {
8852 // cx.propagate();
8853 // return;
8854 // }
8855
8856 // let mut new_selections_by_buffer = HashMap::default();
8857 // for selection in editor.selections.all::<usize>(cx) {
8858 // for (buffer, mut range, _) in
8859 // buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8860 // {
8861 // if selection.reversed {
8862 // mem::swap(&mut range.start, &mut range.end);
8863 // }
8864 // new_selections_by_buffer
8865 // .entry(buffer)
8866 // .or_insert(Vec::new())
8867 // .push(range)
8868 // }
8869 // }
8870
8871 // editor_handle.update(cx, |editor, cx| {
8872 // editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8873 // });
8874 // let pane = workspace.active_pane().clone();
8875 // pane.update(cx, |pane, _| pane.disable_history());
8876
8877 // // We defer the pane interaction because we ourselves are a workspace item
8878 // // and activating a new item causes the pane to call a method on us reentrantly,
8879 // // which panics if we're on the stack.
8880 // cx.defer(move |workspace, cx| {
8881 // for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8882 // let editor = workspace.open_project_item::<Self>(buffer, cx);
8883 // editor.update(cx, |editor, cx| {
8884 // editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8885 // s.select_ranges(ranges);
8886 // });
8887 // });
8888 // }
8889
8890 // pane.update(cx, |pane, _| pane.enable_history());
8891 // });
8892 // }
8893
8894 fn jump(
8895 &mut self,
8896 path: ProjectPath,
8897 position: Point,
8898 anchor: language::Anchor,
8899 cx: &mut ViewContext<Self>,
8900 ) {
8901 let workspace = self.workspace();
8902 cx.spawn(|_, mut cx| async move {
8903 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
8904 let editor = workspace.update(&mut cx, |workspace, cx| {
8905 workspace.open_path(path, None, true, cx)
8906 })?;
8907 let editor = editor
8908 .await?
8909 .downcast::<Editor>()
8910 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8911 .downgrade();
8912 editor.update(&mut cx, |editor, cx| {
8913 let buffer = editor
8914 .buffer()
8915 .read(cx)
8916 .as_singleton()
8917 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8918 let buffer = buffer.read(cx);
8919 let cursor = if buffer.can_resolve(&anchor) {
8920 language::ToPoint::to_point(&anchor, buffer)
8921 } else {
8922 buffer.clip_point(position, Bias::Left)
8923 };
8924
8925 let nav_history = editor.nav_history.take();
8926 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8927 s.select_ranges([cursor..cursor]);
8928 });
8929 editor.nav_history = nav_history;
8930
8931 anyhow::Ok(())
8932 })??;
8933
8934 anyhow::Ok(())
8935 })
8936 .detach_and_log_err(cx);
8937 }
8938
8939 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8940 let snapshot = self.buffer.read(cx).read(cx);
8941 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8942 Some(
8943 ranges
8944 .iter()
8945 .map(move |range| {
8946 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8947 })
8948 .collect(),
8949 )
8950 }
8951
8952 fn selection_replacement_ranges(
8953 &self,
8954 range: Range<OffsetUtf16>,
8955 cx: &AppContext,
8956 ) -> Vec<Range<OffsetUtf16>> {
8957 let selections = self.selections.all::<OffsetUtf16>(cx);
8958 let newest_selection = selections
8959 .iter()
8960 .max_by_key(|selection| selection.id)
8961 .unwrap();
8962 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8963 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8964 let snapshot = self.buffer.read(cx).read(cx);
8965 selections
8966 .into_iter()
8967 .map(|mut selection| {
8968 selection.start.0 =
8969 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8970 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8971 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8972 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8973 })
8974 .collect()
8975 }
8976
8977 fn report_copilot_event(
8978 &self,
8979 suggestion_id: Option<String>,
8980 suggestion_accepted: bool,
8981 cx: &AppContext,
8982 ) {
8983 let Some(project) = &self.project else { return };
8984
8985 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8986 let file_extension = self
8987 .buffer
8988 .read(cx)
8989 .as_singleton()
8990 .and_then(|b| b.read(cx).file())
8991 .and_then(|file| Path::new(file.file_name(cx)).extension())
8992 .and_then(|e| e.to_str())
8993 .map(|a| a.to_string());
8994
8995 let telemetry = project.read(cx).client().telemetry().clone();
8996 let telemetry_settings = *TelemetrySettings::get_global(cx);
8997
8998 telemetry.report_copilot_event(
8999 telemetry_settings,
9000 suggestion_id,
9001 suggestion_accepted,
9002 file_extension,
9003 )
9004 }
9005
9006 #[cfg(any(test, feature = "test-support"))]
9007 fn report_editor_event(
9008 &self,
9009 _operation: &'static str,
9010 _file_extension: Option<String>,
9011 _cx: &AppContext,
9012 ) {
9013 }
9014
9015 #[cfg(not(any(test, feature = "test-support")))]
9016 fn report_editor_event(
9017 &self,
9018 operation: &'static str,
9019 file_extension: Option<String>,
9020 cx: &AppContext,
9021 ) {
9022 let Some(project) = &self.project else { return };
9023
9024 // If None, we are in a file without an extension
9025 let file = self
9026 .buffer
9027 .read(cx)
9028 .as_singleton()
9029 .and_then(|b| b.read(cx).file());
9030 let file_extension = file_extension.or(file
9031 .as_ref()
9032 .and_then(|file| Path::new(file.file_name(cx)).extension())
9033 .and_then(|e| e.to_str())
9034 .map(|a| a.to_string()));
9035
9036 let vim_mode = cx
9037 .global::<SettingsStore>()
9038 .raw_user_settings()
9039 .get("vim_mode")
9040 == Some(&serde_json::Value::Bool(true));
9041 let telemetry_settings = *TelemetrySettings::get_global(cx);
9042 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
9043 let copilot_enabled_for_language = self
9044 .buffer
9045 .read(cx)
9046 .settings_at(0, cx)
9047 .show_copilot_suggestions;
9048
9049 let telemetry = project.read(cx).client().telemetry().clone();
9050 telemetry.report_editor_event(
9051 telemetry_settings,
9052 file_extension,
9053 vim_mode,
9054 operation,
9055 copilot_enabled,
9056 copilot_enabled_for_language,
9057 )
9058 }
9059
9060 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9061 /// with each line being an array of {text, highlight} objects.
9062 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9063 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9064 return;
9065 };
9066
9067 #[derive(Serialize)]
9068 struct Chunk<'a> {
9069 text: String,
9070 highlight: Option<&'a str>,
9071 }
9072
9073 let snapshot = buffer.read(cx).snapshot();
9074 let range = self
9075 .selected_text_range(cx)
9076 .and_then(|selected_range| {
9077 if selected_range.is_empty() {
9078 None
9079 } else {
9080 Some(selected_range)
9081 }
9082 })
9083 .unwrap_or_else(|| 0..snapshot.len());
9084
9085 let chunks = snapshot.chunks(range, true);
9086 let mut lines = Vec::new();
9087 let mut line: VecDeque<Chunk> = VecDeque::new();
9088
9089 let Some(style) = self.style.as_ref() else {
9090 return;
9091 };
9092
9093 for chunk in chunks {
9094 let highlight = chunk
9095 .syntax_highlight_id
9096 .and_then(|id| id.name(&style.syntax));
9097 let mut chunk_lines = chunk.text.split("\n").peekable();
9098 while let Some(text) = chunk_lines.next() {
9099 let mut merged_with_last_token = false;
9100 if let Some(last_token) = line.back_mut() {
9101 if last_token.highlight == highlight {
9102 last_token.text.push_str(text);
9103 merged_with_last_token = true;
9104 }
9105 }
9106
9107 if !merged_with_last_token {
9108 line.push_back(Chunk {
9109 text: text.into(),
9110 highlight,
9111 });
9112 }
9113
9114 if chunk_lines.peek().is_some() {
9115 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9116 line.pop_front();
9117 }
9118 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9119 line.pop_back();
9120 }
9121
9122 lines.push(mem::take(&mut line));
9123 }
9124 }
9125 }
9126
9127 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9128 return;
9129 };
9130 cx.write_to_clipboard(ClipboardItem::new(lines));
9131 }
9132
9133 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9134 &self.inlay_hint_cache
9135 }
9136
9137 pub fn replay_insert_event(
9138 &mut self,
9139 text: &str,
9140 relative_utf16_range: Option<Range<isize>>,
9141 cx: &mut ViewContext<Self>,
9142 ) {
9143 if !self.input_enabled {
9144 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9145 return;
9146 }
9147 if let Some(relative_utf16_range) = relative_utf16_range {
9148 let selections = self.selections.all::<OffsetUtf16>(cx);
9149 self.change_selections(None, cx, |s| {
9150 let new_ranges = selections.into_iter().map(|range| {
9151 let start = OffsetUtf16(
9152 range
9153 .head()
9154 .0
9155 .saturating_add_signed(relative_utf16_range.start),
9156 );
9157 let end = OffsetUtf16(
9158 range
9159 .head()
9160 .0
9161 .saturating_add_signed(relative_utf16_range.end),
9162 );
9163 start..end
9164 });
9165 s.select_ranges(new_ranges);
9166 });
9167 }
9168
9169 self.handle_input(text, cx);
9170 }
9171
9172 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9173 let Some(project) = self.project.as_ref() else {
9174 return false;
9175 };
9176 let project = project.read(cx);
9177
9178 let mut supports = false;
9179 self.buffer().read(cx).for_each_buffer(|buffer| {
9180 if !supports {
9181 supports = project
9182 .language_servers_for_buffer(buffer.read(cx), cx)
9183 .any(
9184 |(_, server)| match server.capabilities().inlay_hint_provider {
9185 Some(lsp::OneOf::Left(enabled)) => enabled,
9186 Some(lsp::OneOf::Right(_)) => true,
9187 None => false,
9188 },
9189 )
9190 }
9191 });
9192 supports
9193 }
9194
9195 pub fn focus(&self, cx: &mut WindowContext) {
9196 cx.focus(&self.focus_handle)
9197 }
9198
9199 pub fn is_focused(&self, cx: &WindowContext) -> bool {
9200 self.focus_handle.is_focused(cx)
9201 }
9202
9203 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
9204 cx.emit(EditorEvent::Focused);
9205
9206 if let Some(rename) = self.pending_rename.as_ref() {
9207 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
9208 cx.focus(&rename_editor_focus_handle);
9209 } else {
9210 self.blink_manager.update(cx, BlinkManager::enable);
9211 self.buffer.update(cx, |buffer, cx| {
9212 buffer.finalize_last_transaction(cx);
9213 if self.leader_peer_id.is_none() {
9214 buffer.set_active_selections(
9215 &self.selections.disjoint_anchors(),
9216 self.selections.line_mode,
9217 self.cursor_shape,
9218 cx,
9219 );
9220 }
9221 });
9222 }
9223 }
9224
9225 fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
9226 // todo!()
9227 // let blurred_event = EditorBlurred(cx.handle());
9228 // cx.emit_global(blurred_event);
9229 self.blink_manager.update(cx, BlinkManager::disable);
9230 self.buffer
9231 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9232 self.hide_context_menu(cx);
9233 hide_hover(self, cx);
9234 cx.emit(EditorEvent::Blurred);
9235 cx.notify();
9236 }
9237}
9238
9239pub trait CollaborationHub {
9240 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9241 fn user_participant_indices<'a>(
9242 &self,
9243 cx: &'a AppContext,
9244 ) -> &'a HashMap<u64, ParticipantIndex>;
9245}
9246
9247impl CollaborationHub for Model<Project> {
9248 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9249 self.read(cx).collaborators()
9250 }
9251
9252 fn user_participant_indices<'a>(
9253 &self,
9254 cx: &'a AppContext,
9255 ) -> &'a HashMap<u64, ParticipantIndex> {
9256 self.read(cx).user_store().read(cx).participant_indices()
9257 }
9258}
9259
9260fn inlay_hint_settings(
9261 location: Anchor,
9262 snapshot: &MultiBufferSnapshot,
9263 cx: &mut ViewContext<'_, Editor>,
9264) -> InlayHintSettings {
9265 let file = snapshot.file_at(location);
9266 let language = snapshot.language_at(location);
9267 let settings = all_language_settings(file, cx);
9268 settings
9269 .language(language.map(|l| l.name()).as_deref())
9270 .inlay_hints
9271}
9272
9273fn consume_contiguous_rows(
9274 contiguous_row_selections: &mut Vec<Selection<Point>>,
9275 selection: &Selection<Point>,
9276 display_map: &DisplaySnapshot,
9277 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9278) -> (u32, u32) {
9279 contiguous_row_selections.push(selection.clone());
9280 let start_row = selection.start.row;
9281 let mut end_row = ending_row(selection, display_map);
9282
9283 while let Some(next_selection) = selections.peek() {
9284 if next_selection.start.row <= end_row {
9285 end_row = ending_row(next_selection, display_map);
9286 contiguous_row_selections.push(selections.next().unwrap().clone());
9287 } else {
9288 break;
9289 }
9290 }
9291 (start_row, end_row)
9292}
9293
9294fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9295 if next_selection.end.column > 0 || next_selection.is_empty() {
9296 display_map.next_line_boundary(next_selection.end).0.row + 1
9297 } else {
9298 next_selection.end.row
9299 }
9300}
9301
9302impl EditorSnapshot {
9303 pub fn remote_selections_in_range<'a>(
9304 &'a self,
9305 range: &'a Range<Anchor>,
9306 collaboration_hub: &dyn CollaborationHub,
9307 cx: &'a AppContext,
9308 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9309 let participant_indices = collaboration_hub.user_participant_indices(cx);
9310 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9311 let collaborators_by_replica_id = collaborators_by_peer_id
9312 .iter()
9313 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9314 .collect::<HashMap<_, _>>();
9315 self.buffer_snapshot
9316 .remote_selections_in_range(range)
9317 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9318 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9319 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9320 Some(RemoteSelection {
9321 replica_id,
9322 selection,
9323 cursor_shape,
9324 line_mode,
9325 participant_index,
9326 peer_id: collaborator.peer_id,
9327 })
9328 })
9329 }
9330
9331 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9332 self.display_snapshot.buffer_snapshot.language_at(position)
9333 }
9334
9335 pub fn is_focused(&self) -> bool {
9336 self.is_focused
9337 }
9338
9339 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9340 self.placeholder_text.as_ref()
9341 }
9342
9343 pub fn scroll_position(&self) -> gpui::Point<f32> {
9344 self.scroll_anchor.scroll_position(&self.display_snapshot)
9345 }
9346}
9347
9348impl Deref for EditorSnapshot {
9349 type Target = DisplaySnapshot;
9350
9351 fn deref(&self) -> &Self::Target {
9352 &self.display_snapshot
9353 }
9354}
9355
9356#[derive(Clone, Debug, PartialEq, Eq)]
9357pub enum EditorEvent {
9358 InputIgnored {
9359 text: Arc<str>,
9360 },
9361 InputHandled {
9362 utf16_range_to_replace: Option<Range<isize>>,
9363 text: Arc<str>,
9364 },
9365 ExcerptsAdded {
9366 buffer: Model<Buffer>,
9367 predecessor: ExcerptId,
9368 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9369 },
9370 ExcerptsRemoved {
9371 ids: Vec<ExcerptId>,
9372 },
9373 BufferEdited,
9374 Edited,
9375 Reparsed,
9376 Focused,
9377 Blurred,
9378 DirtyChanged,
9379 Saved,
9380 TitleChanged,
9381 DiffBaseChanged,
9382 SelectionsChanged {
9383 local: bool,
9384 },
9385 ScrollPositionChanged {
9386 local: bool,
9387 autoscroll: bool,
9388 },
9389 Closed,
9390}
9391
9392pub struct EditorFocused(pub View<Editor>);
9393pub struct EditorBlurred(pub View<Editor>);
9394pub struct EditorReleased(pub WeakView<Editor>);
9395
9396// impl Entity for Editor {
9397// type Event = Event;
9398
9399// fn release(&mut self, cx: &mut AppContext) {
9400// cx.emit_global(EditorReleased(self.handle.clone()));
9401// }
9402// }
9403//
9404impl EventEmitter<EditorEvent> for Editor {}
9405
9406impl FocusableView for Editor {
9407 fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
9408 self.focus_handle.clone()
9409 }
9410}
9411
9412impl Render for Editor {
9413 type Element = EditorElement;
9414
9415 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
9416 let settings = ThemeSettings::get_global(cx);
9417 let text_style = match self.mode {
9418 EditorMode::SingleLine => TextStyle {
9419 color: cx.theme().colors().text,
9420 font_family: settings.ui_font.family.clone(),
9421 font_features: settings.ui_font.features,
9422 font_size: rems(0.875).into(),
9423 font_weight: FontWeight::NORMAL,
9424 font_style: FontStyle::Normal,
9425 line_height: relative(1.).into(),
9426 background_color: None,
9427 underline: None,
9428 white_space: WhiteSpace::Normal,
9429 },
9430
9431 EditorMode::AutoHeight { max_lines } => todo!(),
9432
9433 EditorMode::Full => TextStyle {
9434 color: cx.theme().colors().text,
9435 font_family: settings.buffer_font.family.clone(),
9436 font_features: settings.buffer_font.features,
9437 font_size: settings.buffer_font_size.into(),
9438 font_weight: FontWeight::NORMAL,
9439 font_style: FontStyle::Normal,
9440 line_height: relative(settings.buffer_line_height.value()),
9441 background_color: None,
9442 underline: None,
9443 white_space: WhiteSpace::Normal,
9444 },
9445 };
9446
9447 let background = match self.mode {
9448 EditorMode::SingleLine => cx.theme().system().transparent,
9449 EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
9450 EditorMode::Full => cx.theme().colors().editor_background,
9451 };
9452
9453 EditorElement::new(
9454 cx.view(),
9455 EditorStyle {
9456 background,
9457 local_player: cx.theme().players().local(),
9458 text: text_style,
9459 scrollbar_width: px(12.),
9460 syntax: cx.theme().syntax().clone(),
9461 diagnostic_style: cx.theme().diagnostic_style(),
9462 },
9463 )
9464 }
9465}
9466
9467// impl View for Editor {
9468// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9469// let style = self.style(cx);
9470// let font_changed = self.display_map.update(cx, |map, cx| {
9471// map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9472// map.set_font_with_size(style.text.font_id, style.text.font_size, cx)
9473// });
9474
9475// if font_changed {
9476// cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9477// hide_hover(editor, cx);
9478// hide_link_definition(editor, cx);
9479// });
9480// }
9481
9482// Stack::new()
9483// .with_child(EditorElement::new(style.clone()))
9484// .with_child(ChildView::new(&self.mouse_context_menu, cx))
9485// .into_any()
9486// }
9487
9488// fn ui_name() -> &'static str {
9489// "Editor"
9490// }
9491
9492// fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
9493// if cx.is_self_focused() {
9494// let focused_event = EditorFocused(cx.handle());
9495// cx.emit(Event::Focused);
9496// cx.emit_global(focused_event);
9497// }
9498// if let Some(rename) = self.pending_rename.as_ref() {
9499// cx.focus(&rename.editor);
9500// } else if cx.is_self_focused() || !focused.is::<Editor>() {
9501// if !self.focused {
9502// self.blink_manager.update(cx, BlinkManager::enable);
9503// }
9504// self.focused = true;
9505// self.buffer.update(cx, |buffer, cx| {
9506// buffer.finalize_last_transaction(cx);
9507// if self.leader_peer_id.is_none() {
9508// buffer.set_active_selections(
9509// &self.selections.disjoint_anchors(),
9510// self.selections.line_mode,
9511// self.cursor_shape,
9512// cx,
9513// );
9514// }
9515// });
9516// }
9517// }
9518
9519// fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
9520// let blurred_event = EditorBlurred(cx.handle());
9521// cx.emit_global(blurred_event);
9522// self.focused = false;
9523// self.blink_manager.update(cx, BlinkManager::disable);
9524// self.buffer
9525// .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9526// self.hide_context_menu(cx);
9527// hide_hover(self, cx);
9528// cx.emit(Event::Blurred);
9529// cx.notify();
9530// }
9531
9532// fn modifiers_changed(
9533// &mut self,
9534// event: &gpui::platform::ModifiersChangedEvent,
9535// cx: &mut ViewContext<Self>,
9536// ) -> bool {
9537// let pending_selection = self.has_pending_selection();
9538
9539// if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9540// if event.cmd && !pending_selection {
9541// let point = point.clone();
9542// let snapshot = self.snapshot(cx);
9543// let kind = point.definition_kind(event.shift);
9544
9545// show_link_definition(kind, self, point, snapshot, cx);
9546// return false;
9547// }
9548// }
9549
9550// {
9551// if self.link_go_to_definition_state.symbol_range.is_some()
9552// || !self.link_go_to_definition_state.definitions.is_empty()
9553// {
9554// self.link_go_to_definition_state.symbol_range.take();
9555// self.link_go_to_definition_state.definitions.clear();
9556// cx.notify();
9557// }
9558
9559// self.link_go_to_definition_state.task = None;
9560
9561// self.clear_highlights::<LinkGoToDefinitionState>(cx);
9562// }
9563
9564// false
9565// }
9566
9567impl InputHandler for Editor {
9568 fn text_for_range(
9569 &mut self,
9570 range_utf16: Range<usize>,
9571 cx: &mut ViewContext<Self>,
9572 ) -> Option<String> {
9573 Some(
9574 self.buffer
9575 .read(cx)
9576 .read(cx)
9577 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9578 .collect(),
9579 )
9580 }
9581
9582 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9583 // Prevent the IME menu from appearing when holding down an alphabetic key
9584 // while input is disabled.
9585 if !self.input_enabled {
9586 return None;
9587 }
9588
9589 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9590 Some(range.start.0..range.end.0)
9591 }
9592
9593 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9594 let snapshot = self.buffer.read(cx).read(cx);
9595 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9596 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9597 }
9598
9599 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9600 self.clear_highlights::<InputComposition>(cx);
9601 self.ime_transaction.take();
9602 }
9603
9604 fn replace_text_in_range(
9605 &mut self,
9606 range_utf16: Option<Range<usize>>,
9607 text: &str,
9608 cx: &mut ViewContext<Self>,
9609 ) {
9610 if !self.input_enabled {
9611 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9612 return;
9613 }
9614
9615 self.transact(cx, |this, cx| {
9616 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9617 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9618 Some(this.selection_replacement_ranges(range_utf16, cx))
9619 } else {
9620 this.marked_text_ranges(cx)
9621 };
9622
9623 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9624 let newest_selection_id = this.selections.newest_anchor().id;
9625 this.selections
9626 .all::<OffsetUtf16>(cx)
9627 .iter()
9628 .zip(ranges_to_replace.iter())
9629 .find_map(|(selection, range)| {
9630 if selection.id == newest_selection_id {
9631 Some(
9632 (range.start.0 as isize - selection.head().0 as isize)
9633 ..(range.end.0 as isize - selection.head().0 as isize),
9634 )
9635 } else {
9636 None
9637 }
9638 })
9639 });
9640
9641 cx.emit(EditorEvent::InputHandled {
9642 utf16_range_to_replace: range_to_replace,
9643 text: text.into(),
9644 });
9645
9646 if let Some(new_selected_ranges) = new_selected_ranges {
9647 this.change_selections(None, cx, |selections| {
9648 selections.select_ranges(new_selected_ranges)
9649 });
9650 }
9651
9652 this.handle_input(text, cx);
9653 });
9654
9655 if let Some(transaction) = self.ime_transaction {
9656 self.buffer.update(cx, |buffer, cx| {
9657 buffer.group_until_transaction(transaction, cx);
9658 });
9659 }
9660
9661 self.unmark_text(cx);
9662 }
9663
9664 fn replace_and_mark_text_in_range(
9665 &mut self,
9666 range_utf16: Option<Range<usize>>,
9667 text: &str,
9668 new_selected_range_utf16: Option<Range<usize>>,
9669 cx: &mut ViewContext<Self>,
9670 ) {
9671 if !self.input_enabled {
9672 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9673 return;
9674 }
9675
9676 let transaction = self.transact(cx, |this, cx| {
9677 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9678 let snapshot = this.buffer.read(cx).read(cx);
9679 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9680 for marked_range in &mut marked_ranges {
9681 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9682 marked_range.start.0 += relative_range_utf16.start;
9683 marked_range.start =
9684 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9685 marked_range.end =
9686 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9687 }
9688 }
9689 Some(marked_ranges)
9690 } else if let Some(range_utf16) = range_utf16 {
9691 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9692 Some(this.selection_replacement_ranges(range_utf16, cx))
9693 } else {
9694 None
9695 };
9696
9697 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9698 let newest_selection_id = this.selections.newest_anchor().id;
9699 this.selections
9700 .all::<OffsetUtf16>(cx)
9701 .iter()
9702 .zip(ranges_to_replace.iter())
9703 .find_map(|(selection, range)| {
9704 if selection.id == newest_selection_id {
9705 Some(
9706 (range.start.0 as isize - selection.head().0 as isize)
9707 ..(range.end.0 as isize - selection.head().0 as isize),
9708 )
9709 } else {
9710 None
9711 }
9712 })
9713 });
9714
9715 cx.emit(EditorEvent::InputHandled {
9716 utf16_range_to_replace: range_to_replace,
9717 text: text.into(),
9718 });
9719
9720 if let Some(ranges) = ranges_to_replace {
9721 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9722 }
9723
9724 let marked_ranges = {
9725 let snapshot = this.buffer.read(cx).read(cx);
9726 this.selections
9727 .disjoint_anchors()
9728 .iter()
9729 .map(|selection| {
9730 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9731 })
9732 .collect::<Vec<_>>()
9733 };
9734
9735 if text.is_empty() {
9736 this.unmark_text(cx);
9737 } else {
9738 this.highlight_text::<InputComposition>(
9739 marked_ranges.clone(),
9740 HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
9741 cx,
9742 );
9743 }
9744
9745 this.handle_input(text, cx);
9746
9747 if let Some(new_selected_range) = new_selected_range_utf16 {
9748 let snapshot = this.buffer.read(cx).read(cx);
9749 let new_selected_ranges = marked_ranges
9750 .into_iter()
9751 .map(|marked_range| {
9752 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9753 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9754 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9755 snapshot.clip_offset_utf16(new_start, Bias::Left)
9756 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9757 })
9758 .collect::<Vec<_>>();
9759
9760 drop(snapshot);
9761 this.change_selections(None, cx, |selections| {
9762 selections.select_ranges(new_selected_ranges)
9763 });
9764 }
9765 });
9766
9767 self.ime_transaction = self.ime_transaction.or(transaction);
9768 if let Some(transaction) = self.ime_transaction {
9769 self.buffer.update(cx, |buffer, cx| {
9770 buffer.group_until_transaction(transaction, cx);
9771 });
9772 }
9773
9774 if self.text_highlights::<InputComposition>(cx).is_none() {
9775 self.ime_transaction.take();
9776 }
9777 }
9778
9779 fn bounds_for_range(
9780 &mut self,
9781 range_utf16: Range<usize>,
9782 element_bounds: gpui::Bounds<Pixels>,
9783 cx: &mut ViewContext<Self>,
9784 ) -> Option<gpui::Bounds<Pixels>> {
9785 let text_layout_details = self.text_layout_details(cx);
9786 let style = &text_layout_details.editor_style;
9787 let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
9788 let font_size = style.text.font_size.to_pixels(cx.rem_size());
9789 let line_height = style.text.line_height_in_pixels(cx.rem_size());
9790 let em_width = cx
9791 .text_system()
9792 .typographic_bounds(font_id, font_size, 'm')
9793 .unwrap()
9794 .size
9795 .width;
9796
9797 let snapshot = self.snapshot(cx);
9798 let scroll_position = snapshot.scroll_position();
9799 let scroll_left = scroll_position.x * em_width;
9800
9801 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
9802 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
9803 + self.gutter_width;
9804 let y = line_height * (start.row() as f32 - scroll_position.y);
9805
9806 Some(Bounds {
9807 origin: element_bounds.origin + point(x, y),
9808 size: size(em_width, line_height),
9809 })
9810 }
9811}
9812
9813// fn build_style(
9814// settings: &ThemeSettings,
9815// get_field_editor_theme: Option<&GetFieldEditorTheme>,
9816// override_text_style: Option<&OverrideTextStyle>,
9817// cx: &mut AppContext,
9818// ) -> EditorStyle {
9819// let font_cache = cx.font_cache();
9820// let line_height_scalar = settings.line_height();
9821// let theme_id = settings.theme.meta.id;
9822// let mut theme = settings.theme.editor.clone();
9823// let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9824// let field_editor_theme = get_field_editor_theme(&settings.theme);
9825// theme.text_color = field_editor_theme.text.color;
9826// theme.selection = field_editor_theme.selection;
9827// theme.background = field_editor_theme
9828// .container
9829// .background_color
9830// .unwrap_or_default();
9831// EditorStyle {
9832// text: field_editor_theme.text,
9833// placeholder_text: field_editor_theme.placeholder_text,
9834// line_height_scalar,
9835// theme,
9836// theme_id,
9837// }
9838// } else {
9839// todo!();
9840// // let font_family_id = settings.buffer_font_family;
9841// // let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9842// // let font_properties = Default::default();
9843// // let font_id = font_cache
9844// // .select_font(font_family_id, &font_properties)
9845// // .unwrap();
9846// // let font_size = settings.buffer_font_size(cx);
9847// // EditorStyle {
9848// // text: TextStyle {
9849// // color: settings.theme.editor.text_color,
9850// // font_family_name,
9851// // font_family_id,
9852// // font_id,
9853// // font_size,
9854// // font_properties,
9855// // underline: Default::default(),
9856// // soft_wrap: false,
9857// // },
9858// // placeholder_text: None,
9859// // line_height_scalar,
9860// // theme,
9861// // theme_id,
9862// // }
9863// };
9864
9865// if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9866// if let Some(highlighted) = style
9867// .text
9868// .clone()
9869// .highlight(highlight_style, font_cache)
9870// .log_err()
9871// {
9872// style.text = highlighted;
9873// }
9874// }
9875
9876// style
9877// }
9878
9879trait SelectionExt {
9880 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9881 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9882 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9883 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9884 -> Range<u32>;
9885}
9886
9887impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9888 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9889 let start = self.start.to_point(buffer);
9890 let end = self.end.to_point(buffer);
9891 if self.reversed {
9892 end..start
9893 } else {
9894 start..end
9895 }
9896 }
9897
9898 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9899 let start = self.start.to_offset(buffer);
9900 let end = self.end.to_offset(buffer);
9901 if self.reversed {
9902 end..start
9903 } else {
9904 start..end
9905 }
9906 }
9907
9908 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9909 let start = self
9910 .start
9911 .to_point(&map.buffer_snapshot)
9912 .to_display_point(map);
9913 let end = self
9914 .end
9915 .to_point(&map.buffer_snapshot)
9916 .to_display_point(map);
9917 if self.reversed {
9918 end..start
9919 } else {
9920 start..end
9921 }
9922 }
9923
9924 fn spanned_rows(
9925 &self,
9926 include_end_if_at_line_start: bool,
9927 map: &DisplaySnapshot,
9928 ) -> Range<u32> {
9929 let start = self.start.to_point(&map.buffer_snapshot);
9930 let mut end = self.end.to_point(&map.buffer_snapshot);
9931 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9932 end.row -= 1;
9933 }
9934
9935 let buffer_start = map.prev_line_boundary(start).0;
9936 let buffer_end = map.next_line_boundary(end).0;
9937 buffer_start.row..buffer_end.row + 1
9938 }
9939}
9940
9941impl<T: InvalidationRegion> InvalidationStack<T> {
9942 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9943 where
9944 S: Clone + ToOffset,
9945 {
9946 while let Some(region) = self.last() {
9947 let all_selections_inside_invalidation_ranges =
9948 if selections.len() == region.ranges().len() {
9949 selections
9950 .iter()
9951 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9952 .all(|(selection, invalidation_range)| {
9953 let head = selection.head().to_offset(buffer);
9954 invalidation_range.start <= head && invalidation_range.end >= head
9955 })
9956 } else {
9957 false
9958 };
9959
9960 if all_selections_inside_invalidation_ranges {
9961 break;
9962 } else {
9963 self.pop();
9964 }
9965 }
9966 }
9967}
9968
9969impl<T> Default for InvalidationStack<T> {
9970 fn default() -> Self {
9971 Self(Default::default())
9972 }
9973}
9974
9975impl<T> Deref for InvalidationStack<T> {
9976 type Target = Vec<T>;
9977
9978 fn deref(&self) -> &Self::Target {
9979 &self.0
9980 }
9981}
9982
9983impl<T> DerefMut for InvalidationStack<T> {
9984 fn deref_mut(&mut self) -> &mut Self::Target {
9985 &mut self.0
9986 }
9987}
9988
9989impl InvalidationRegion for SnippetState {
9990 fn ranges(&self) -> &[Range<Anchor>] {
9991 &self.ranges[self.active_index]
9992 }
9993}
9994
9995// impl Deref for EditorStyle {
9996// type Target = theme::Editor;
9997
9998// fn deref(&self) -> &Self::Target {
9999// &self.theme
10000// }
10001// }
10002
10003pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
10004 let mut highlighted_lines = Vec::new();
10005
10006 for (index, line) in diagnostic.message.lines().enumerate() {
10007 let line = match &diagnostic.source {
10008 Some(source) if index == 0 => {
10009 let source_highlight = Vec::from_iter(0..source.len());
10010 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
10011 }
10012
10013 _ => highlight_diagnostic_message(Vec::new(), line),
10014 };
10015 highlighted_lines.push(line);
10016 }
10017 let message = diagnostic.message;
10018 Arc::new(move |cx: &mut BlockContext| {
10019 let message = message.clone();
10020 v_stack()
10021 .id(cx.block_id)
10022 .size_full()
10023 .bg(gpui::red())
10024 .children(highlighted_lines.iter().map(|(line, highlights)| {
10025 div()
10026 .child(HighlightedLabel::new(line.clone(), highlights.clone()))
10027 .ml(cx.anchor_x)
10028 }))
10029 .cursor_pointer()
10030 .on_click(cx.listener(move |_, _, cx| {
10031 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
10032 }))
10033 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx))
10034 .into_any_element()
10035 })
10036}
10037
10038pub fn highlight_diagnostic_message(
10039 initial_highlights: Vec<usize>,
10040 message: &str,
10041) -> (String, Vec<usize>) {
10042 let mut message_without_backticks = String::new();
10043 let mut prev_offset = 0;
10044 let mut inside_block = false;
10045 let mut highlights = initial_highlights;
10046 for (match_ix, (offset, _)) in message
10047 .match_indices('`')
10048 .chain([(message.len(), "")])
10049 .enumerate()
10050 {
10051 message_without_backticks.push_str(&message[prev_offset..offset]);
10052 if inside_block {
10053 highlights.extend(prev_offset - match_ix..offset - match_ix);
10054 }
10055
10056 inside_block = !inside_block;
10057 prev_offset = offset + 1;
10058 }
10059
10060 (message_without_backticks, highlights)
10061}
10062
10063pub fn diagnostic_style(
10064 severity: DiagnosticSeverity,
10065 valid: bool,
10066 style: &DiagnosticStyle,
10067) -> Hsla {
10068 match (severity, valid) {
10069 (DiagnosticSeverity::ERROR, true) => style.error,
10070 (DiagnosticSeverity::ERROR, false) => style.error,
10071 (DiagnosticSeverity::WARNING, true) => style.warning,
10072 (DiagnosticSeverity::WARNING, false) => style.warning,
10073 (DiagnosticSeverity::INFORMATION, true) => style.info,
10074 (DiagnosticSeverity::INFORMATION, false) => style.info,
10075 (DiagnosticSeverity::HINT, true) => style.info,
10076 (DiagnosticSeverity::HINT, false) => style.info,
10077 _ => style.ignored,
10078 }
10079}
10080
10081pub fn combine_syntax_and_fuzzy_match_highlights(
10082 text: &str,
10083 default_style: HighlightStyle,
10084 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
10085 match_indices: &[usize],
10086) -> Vec<(Range<usize>, HighlightStyle)> {
10087 let mut result = Vec::new();
10088 let mut match_indices = match_indices.iter().copied().peekable();
10089
10090 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
10091 {
10092 syntax_highlight.font_weight = None;
10093
10094 // Add highlights for any fuzzy match characters before the next
10095 // syntax highlight range.
10096 while let Some(&match_index) = match_indices.peek() {
10097 if match_index >= range.start {
10098 break;
10099 }
10100 match_indices.next();
10101 let end_index = char_ix_after(match_index, text);
10102 let mut match_style = default_style;
10103 match_style.font_weight = Some(FontWeight::BOLD);
10104 result.push((match_index..end_index, match_style));
10105 }
10106
10107 if range.start == usize::MAX {
10108 break;
10109 }
10110
10111 // Add highlights for any fuzzy match characters within the
10112 // syntax highlight range.
10113 let mut offset = range.start;
10114 while let Some(&match_index) = match_indices.peek() {
10115 if match_index >= range.end {
10116 break;
10117 }
10118
10119 match_indices.next();
10120 if match_index > offset {
10121 result.push((offset..match_index, syntax_highlight));
10122 }
10123
10124 let mut end_index = char_ix_after(match_index, text);
10125 while let Some(&next_match_index) = match_indices.peek() {
10126 if next_match_index == end_index && next_match_index < range.end {
10127 end_index = char_ix_after(next_match_index, text);
10128 match_indices.next();
10129 } else {
10130 break;
10131 }
10132 }
10133
10134 let mut match_style = syntax_highlight;
10135 match_style.font_weight = Some(FontWeight::BOLD);
10136 result.push((match_index..end_index, match_style));
10137 offset = end_index;
10138 }
10139
10140 if offset < range.end {
10141 result.push((offset..range.end, syntax_highlight));
10142 }
10143 }
10144
10145 fn char_ix_after(ix: usize, text: &str) -> usize {
10146 ix + text[ix..].chars().next().unwrap().len_utf8()
10147 }
10148
10149 result
10150}
10151
10152pub fn styled_runs_for_code_label<'a>(
10153 label: &'a CodeLabel,
10154 syntax_theme: &'a theme::SyntaxTheme,
10155) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10156 let fade_out = HighlightStyle {
10157 fade_out: Some(0.35),
10158 ..Default::default()
10159 };
10160
10161 let mut prev_end = label.filter_range.end;
10162 label
10163 .runs
10164 .iter()
10165 .enumerate()
10166 .flat_map(move |(ix, (range, highlight_id))| {
10167 let style = if let Some(style) = highlight_id.style(syntax_theme) {
10168 style
10169 } else {
10170 return Default::default();
10171 };
10172 let mut muted_style = style;
10173 muted_style.highlight(fade_out);
10174
10175 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10176 if range.start >= label.filter_range.end {
10177 if range.start > prev_end {
10178 runs.push((prev_end..range.start, fade_out));
10179 }
10180 runs.push((range.clone(), muted_style));
10181 } else if range.end <= label.filter_range.end {
10182 runs.push((range.clone(), style));
10183 } else {
10184 runs.push((range.start..label.filter_range.end, style));
10185 runs.push((label.filter_range.end..range.end, muted_style));
10186 }
10187 prev_end = cmp::max(prev_end, range.end);
10188
10189 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10190 runs.push((prev_end..label.text.len(), fade_out));
10191 }
10192
10193 runs
10194 })
10195}
10196
10197pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10198 let mut index = 0;
10199 let mut codepoints = text.char_indices().peekable();
10200
10201 std::iter::from_fn(move || {
10202 let start_index = index;
10203 while let Some((new_index, codepoint)) = codepoints.next() {
10204 index = new_index + codepoint.len_utf8();
10205 let current_upper = codepoint.is_uppercase();
10206 let next_upper = codepoints
10207 .peek()
10208 .map(|(_, c)| c.is_uppercase())
10209 .unwrap_or(false);
10210
10211 if !current_upper && next_upper {
10212 return Some(&text[start_index..index]);
10213 }
10214 }
10215
10216 index = text.len();
10217 if start_index < text.len() {
10218 return Some(&text[start_index..]);
10219 }
10220 None
10221 })
10222 .flat_map(|word| word.split_inclusive('_'))
10223 .flat_map(|word| word.split_inclusive('-'))
10224}
10225
10226trait RangeToAnchorExt {
10227 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10228}
10229
10230impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10231 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10232 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10233 }
10234}