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