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