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