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