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