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