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