This commit is contained in:
Nathan Sobo 2023-11-02 16:47:14 -06:00
parent 583c36e24b
commit 7b712ac68f
25 changed files with 13799 additions and 13530 deletions

1
Cargo.lock generated
View file

@ -4408,6 +4408,7 @@ dependencies = [
"lsp2", "lsp2",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"postage", "postage",
"pulldown-cmark",
"rand 0.8.5", "rand 0.8.5",
"regex", "regex",
"rpc2", "rpc2",

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@ use super::{
Highlights, Highlights,
}; };
use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset}; use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
use gpui::{fonts::HighlightStyle, Hsla}; use gpui::{HighlightStyle, Hsla};
use language::{Chunk, Edit, Point, TextSummary}; use language::{Chunk, Edit, Point, TextSummary};
use std::{ use std::{
any::TypeId, any::TypeId,
@ -221,7 +221,7 @@ impl FoldMap {
(FoldMapWriter(self), snapshot, edits) (FoldMapWriter(self), snapshot, edits)
} }
pub fn set_ellipses_color(&mut self, color: Color) -> bool { pub fn set_ellipses_color(&mut self, color: Hsla) -> bool {
if self.ellipses_color != Some(color) { if self.ellipses_color != Some(color) {
self.ellipses_color = Some(color); self.ellipses_color = Some(color);
true true
@ -469,7 +469,7 @@ pub struct FoldSnapshot {
folds: SumTree<Fold>, folds: SumTree<Fold>,
pub inlay_snapshot: InlaySnapshot, pub inlay_snapshot: InlaySnapshot,
pub version: usize, pub version: usize,
pub ellipses_color: Option<Color>, pub ellipses_color: Option<Hsla>,
} }
impl FoldSnapshot { impl FoldSnapshot {
@ -959,7 +959,7 @@ pub struct FoldChunks<'a> {
inlay_offset: InlayOffset, inlay_offset: InlayOffset,
output_offset: usize, output_offset: usize,
max_output_offset: usize, max_output_offset: usize,
ellipses_color: Option<Color>, ellipses_color: Option<Hsla>,
} }
impl<'a> Iterator for FoldChunks<'a> { impl<'a> Iterator for FoldChunks<'a> {

View file

@ -1,6 +1,6 @@
use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset}; use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset};
use collections::{BTreeMap, BTreeSet}; use collections::{BTreeMap, BTreeSet};
use gpui::fonts::HighlightStyle; use gpui::HighlightStyle;
use language::{Chunk, Edit, Point, TextSummary}; use language::{Chunk, Edit, Point, TextSummary};
use multi_buffer::{MultiBufferChunks, MultiBufferRows}; use multi_buffer::{MultiBufferChunks, MultiBufferRows};
use std::{ use std::{

View file

@ -4,7 +4,7 @@ use super::{
Highlights, Highlights,
}; };
use crate::MultiBufferSnapshot; use crate::MultiBufferSnapshot;
use gpui::{AppContext, Entity, Model, ModelContext, Task}; use gpui::{AppContext, FontId, Model, ModelContext, Pixels, Task};
use language::{Chunk, Point}; use language::{Chunk, Point};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use smol::future::yield_now; use smol::future::yield_now;
@ -22,7 +22,7 @@ pub struct WrapMap {
edits_since_sync: Patch<u32>, edits_since_sync: Patch<u32>,
wrap_width: Option<f32>, wrap_width: Option<f32>,
background_task: Option<Task<()>>, background_task: Option<Task<()>>,
font: (FontId, f32), font: (FontId, Pixels),
} }
#[derive(Clone)] #[derive(Clone)]

View file

@ -38,9 +38,8 @@ pub use element::{
use futures::FutureExt; use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem, serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, Hsla,
Element, Entity, Hsla, Model, Subscription, Task, View, ViewContext, Model, Quad, Subscription, Task, Text, View, ViewContext, WeakView, WindowContext,
WindowContext,
}; };
use highlight_matching_bracket::refresh_matching_bracket_highlights; use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState}; use hover_popover::{hide_hover, HoverState};
@ -50,10 +49,10 @@ use itertools::Itertools;
pub use language::{char_kind, CharKind}; pub use language::{char_kind, CharKind};
use language::{ use language::{
language_settings::{self, all_language_settings, InlayHintSettings}, language_settings::{self, all_language_settings, InlayHintSettings},
point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
Completion, CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language,
IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection,
Selection, SelectionGoal, TransactionId, SelectionGoal, TransactionId,
}; };
use link_go_to_definition::{ use link_go_to_definition::{
hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight, hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
@ -113,7 +112,7 @@ pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
pub fn render_parsed_markdown<Tag: 'static>( pub fn render_parsed_markdown<Tag: 'static>(
parsed: &language::ParsedMarkdown, parsed: &language::ParsedMarkdown,
editor_style: &EditorStyle, editor_style: &EditorStyle,
workspace: Option<WeakViewHandle<Workspace>>, workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Text { ) -> Text {
enum RenderedMarkdown {} enum RenderedMarkdown {}
@ -124,51 +123,55 @@ pub fn render_parsed_markdown<Tag: 'static>(
let mut region_id = 0; let mut region_id = 0;
Text::new(parsed.text, editor_style.text.clone()) todo!()
.with_highlights( // Text::new(parsed.text, editor_style.text.clone())
parsed // .with_highlights(
.highlights // parsed
.iter() // .highlights
.filter_map(|(range, highlight)| { // .iter()
let highlight = highlight.to_highlight_style(&editor_style.syntax)?; // .filter_map(|(range, highlight)| {
Some((range.clone(), highlight)) // let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
}) // Some((range.clone(), highlight))
.collect::<Vec<_>>(), // })
) // .collect::<Vec<_>>(),
.with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| { // )
region_id += 1; // .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| {
let region = parsed.regions[ix].clone(); // region_id += 1;
// let region = parsed.regions[ix].clone();
if let Some(link) = region.link { // if let Some(link) = region.link {
cx.scene().push_cursor_region(CursorRegion { // cx.scene().push_cursor_region(CursorRegion {
bounds, // bounds,
style: CursorStyle::PointingHand, // style: CursorStyle::PointingHand,
}); // });
cx.scene().push_mouse_region( // cx.scene().push_mouse_region(
MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds) // MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
.on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link { // .on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
markdown::Link::Web { url } => cx.platform().open_url(url), // markdown::Link::Web { url } => cx.platform().open_url(url),
markdown::Link::Path { path } => { // markdown::Link::Path { path } => {
if let Some(workspace) = &workspace { // if let Some(workspace) = &workspace {
_ = workspace.update(cx, |workspace, cx| { // _ = workspace.update(cx, |workspace, cx| {
workspace.open_abs_path(path.clone(), false, cx).detach(); // workspace.open_abs_path(path.clone(), false, cx).detach();
}); // });
} // }
} // }
}), // }),
); // );
} // }
if region.code { // if region.code {
cx.scene().push_quad(gpui::Quad { // cx.draw_quad(Quad {
bounds, // bounds,
background: Some(code_span_background_color), // background: Some(code_span_background_color),
border: Default::default(), // corner_radii: (2.0).into(),
corner_radii: (2.0).into(), // order: todo!(),
}); // content_mask: todo!(),
} // border_color: todo!(),
}) // border_widths: todo!(),
.with_soft_wrap(true) // });
// }
// })
// .with_soft_wrap(true)
} }
#[derive(Clone, Deserialize, PartialEq, Default)] #[derive(Clone, Deserialize, PartialEq, Default)]
@ -416,133 +419,133 @@ pub fn init_settings(cx: &mut AppContext) {
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
init_settings(cx); init_settings(cx);
cx.add_action(Editor::new_file); // cx.add_action(Editor::new_file);
cx.add_action(Editor::new_file_in_direction); // cx.add_action(Editor::new_file_in_direction);
cx.add_action(Editor::cancel); // cx.add_action(Editor::cancel);
cx.add_action(Editor::newline); // cx.add_action(Editor::newline);
cx.add_action(Editor::newline_above); // cx.add_action(Editor::newline_above);
cx.add_action(Editor::newline_below); // cx.add_action(Editor::newline_below);
cx.add_action(Editor::backspace); // cx.add_action(Editor::backspace);
cx.add_action(Editor::delete); // cx.add_action(Editor::delete);
cx.add_action(Editor::tab); // cx.add_action(Editor::tab);
cx.add_action(Editor::tab_prev); // cx.add_action(Editor::tab_prev);
cx.add_action(Editor::indent); // cx.add_action(Editor::indent);
cx.add_action(Editor::outdent); // cx.add_action(Editor::outdent);
cx.add_action(Editor::delete_line); // cx.add_action(Editor::delete_line);
cx.add_action(Editor::join_lines); // cx.add_action(Editor::join_lines);
cx.add_action(Editor::sort_lines_case_sensitive); // cx.add_action(Editor::sort_lines_case_sensitive);
cx.add_action(Editor::sort_lines_case_insensitive); // cx.add_action(Editor::sort_lines_case_insensitive);
cx.add_action(Editor::reverse_lines); // cx.add_action(Editor::reverse_lines);
cx.add_action(Editor::shuffle_lines); // cx.add_action(Editor::shuffle_lines);
cx.add_action(Editor::convert_to_upper_case); // cx.add_action(Editor::convert_to_upper_case);
cx.add_action(Editor::convert_to_lower_case); // cx.add_action(Editor::convert_to_lower_case);
cx.add_action(Editor::convert_to_title_case); // cx.add_action(Editor::convert_to_title_case);
cx.add_action(Editor::convert_to_snake_case); // cx.add_action(Editor::convert_to_snake_case);
cx.add_action(Editor::convert_to_kebab_case); // cx.add_action(Editor::convert_to_kebab_case);
cx.add_action(Editor::convert_to_upper_camel_case); // cx.add_action(Editor::convert_to_upper_camel_case);
cx.add_action(Editor::convert_to_lower_camel_case); // cx.add_action(Editor::convert_to_lower_camel_case);
cx.add_action(Editor::delete_to_previous_word_start); // cx.add_action(Editor::delete_to_previous_word_start);
cx.add_action(Editor::delete_to_previous_subword_start); // cx.add_action(Editor::delete_to_previous_subword_start);
cx.add_action(Editor::delete_to_next_word_end); // cx.add_action(Editor::delete_to_next_word_end);
cx.add_action(Editor::delete_to_next_subword_end); // cx.add_action(Editor::delete_to_next_subword_end);
cx.add_action(Editor::delete_to_beginning_of_line); // cx.add_action(Editor::delete_to_beginning_of_line);
cx.add_action(Editor::delete_to_end_of_line); // cx.add_action(Editor::delete_to_end_of_line);
cx.add_action(Editor::cut_to_end_of_line); // cx.add_action(Editor::cut_to_end_of_line);
cx.add_action(Editor::duplicate_line); // cx.add_action(Editor::duplicate_line);
cx.add_action(Editor::move_line_up); // cx.add_action(Editor::move_line_up);
cx.add_action(Editor::move_line_down); // cx.add_action(Editor::move_line_down);
cx.add_action(Editor::transpose); // cx.add_action(Editor::transpose);
cx.add_action(Editor::cut); // cx.add_action(Editor::cut);
cx.add_action(Editor::copy); // cx.add_action(Editor::copy);
cx.add_action(Editor::paste); // cx.add_action(Editor::paste);
cx.add_action(Editor::undo); // cx.add_action(Editor::undo);
cx.add_action(Editor::redo); // cx.add_action(Editor::redo);
cx.add_action(Editor::move_up); // cx.add_action(Editor::move_up);
cx.add_action(Editor::move_page_up); // cx.add_action(Editor::move_page_up);
cx.add_action(Editor::move_down); // cx.add_action(Editor::move_down);
cx.add_action(Editor::move_page_down); // cx.add_action(Editor::move_page_down);
cx.add_action(Editor::next_screen); // cx.add_action(Editor::next_screen);
cx.add_action(Editor::move_left); // cx.add_action(Editor::move_left);
cx.add_action(Editor::move_right); // cx.add_action(Editor::move_right);
cx.add_action(Editor::move_to_previous_word_start); // cx.add_action(Editor::move_to_previous_word_start);
cx.add_action(Editor::move_to_previous_subword_start); // cx.add_action(Editor::move_to_previous_subword_start);
cx.add_action(Editor::move_to_next_word_end); // cx.add_action(Editor::move_to_next_word_end);
cx.add_action(Editor::move_to_next_subword_end); // cx.add_action(Editor::move_to_next_subword_end);
cx.add_action(Editor::move_to_beginning_of_line); // cx.add_action(Editor::move_to_beginning_of_line);
cx.add_action(Editor::move_to_end_of_line); // cx.add_action(Editor::move_to_end_of_line);
cx.add_action(Editor::move_to_start_of_paragraph); // cx.add_action(Editor::move_to_start_of_paragraph);
cx.add_action(Editor::move_to_end_of_paragraph); // cx.add_action(Editor::move_to_end_of_paragraph);
cx.add_action(Editor::move_to_beginning); // cx.add_action(Editor::move_to_beginning);
cx.add_action(Editor::move_to_end); // cx.add_action(Editor::move_to_end);
cx.add_action(Editor::select_up); // cx.add_action(Editor::select_up);
cx.add_action(Editor::select_down); // cx.add_action(Editor::select_down);
cx.add_action(Editor::select_left); // cx.add_action(Editor::select_left);
cx.add_action(Editor::select_right); // cx.add_action(Editor::select_right);
cx.add_action(Editor::select_to_previous_word_start); // cx.add_action(Editor::select_to_previous_word_start);
cx.add_action(Editor::select_to_previous_subword_start); // cx.add_action(Editor::select_to_previous_subword_start);
cx.add_action(Editor::select_to_next_word_end); // cx.add_action(Editor::select_to_next_word_end);
cx.add_action(Editor::select_to_next_subword_end); // cx.add_action(Editor::select_to_next_subword_end);
cx.add_action(Editor::select_to_beginning_of_line); // cx.add_action(Editor::select_to_beginning_of_line);
cx.add_action(Editor::select_to_end_of_line); // cx.add_action(Editor::select_to_end_of_line);
cx.add_action(Editor::select_to_start_of_paragraph); // cx.add_action(Editor::select_to_start_of_paragraph);
cx.add_action(Editor::select_to_end_of_paragraph); // cx.add_action(Editor::select_to_end_of_paragraph);
cx.add_action(Editor::select_to_beginning); // cx.add_action(Editor::select_to_beginning);
cx.add_action(Editor::select_to_end); // cx.add_action(Editor::select_to_end);
cx.add_action(Editor::select_all); // cx.add_action(Editor::select_all);
cx.add_action(Editor::select_all_matches); // cx.add_action(Editor::select_all_matches);
cx.add_action(Editor::select_line); // cx.add_action(Editor::select_line);
cx.add_action(Editor::split_selection_into_lines); // cx.add_action(Editor::split_selection_into_lines);
cx.add_action(Editor::add_selection_above); // cx.add_action(Editor::add_selection_above);
cx.add_action(Editor::add_selection_below); // cx.add_action(Editor::add_selection_below);
cx.add_action(Editor::select_next); // cx.add_action(Editor::select_next);
cx.add_action(Editor::select_previous); // cx.add_action(Editor::select_previous);
cx.add_action(Editor::toggle_comments); // cx.add_action(Editor::toggle_comments);
cx.add_action(Editor::select_larger_syntax_node); // cx.add_action(Editor::select_larger_syntax_node);
cx.add_action(Editor::select_smaller_syntax_node); // cx.add_action(Editor::select_smaller_syntax_node);
cx.add_action(Editor::move_to_enclosing_bracket); // cx.add_action(Editor::move_to_enclosing_bracket);
cx.add_action(Editor::undo_selection); // cx.add_action(Editor::undo_selection);
cx.add_action(Editor::redo_selection); // cx.add_action(Editor::redo_selection);
cx.add_action(Editor::go_to_diagnostic); // cx.add_action(Editor::go_to_diagnostic);
cx.add_action(Editor::go_to_prev_diagnostic); // cx.add_action(Editor::go_to_prev_diagnostic);
cx.add_action(Editor::go_to_hunk); // cx.add_action(Editor::go_to_hunk);
cx.add_action(Editor::go_to_prev_hunk); // cx.add_action(Editor::go_to_prev_hunk);
cx.add_action(Editor::go_to_definition); // cx.add_action(Editor::go_to_definition);
cx.add_action(Editor::go_to_definition_split); // cx.add_action(Editor::go_to_definition_split);
cx.add_action(Editor::go_to_type_definition); // cx.add_action(Editor::go_to_type_definition);
cx.add_action(Editor::go_to_type_definition_split); // cx.add_action(Editor::go_to_type_definition_split);
cx.add_action(Editor::fold); // cx.add_action(Editor::fold);
cx.add_action(Editor::fold_at); // cx.add_action(Editor::fold_at);
cx.add_action(Editor::unfold_lines); // cx.add_action(Editor::unfold_lines);
cx.add_action(Editor::unfold_at); // cx.add_action(Editor::unfold_at);
cx.add_action(Editor::gutter_hover); // cx.add_action(Editor::gutter_hover);
cx.add_action(Editor::fold_selected_ranges); // cx.add_action(Editor::fold_selected_ranges);
cx.add_action(Editor::show_completions); // cx.add_action(Editor::show_completions);
cx.add_action(Editor::toggle_code_actions); // cx.add_action(Editor::toggle_code_actions);
cx.add_action(Editor::open_excerpts); // cx.add_action(Editor::open_excerpts);
cx.add_action(Editor::toggle_soft_wrap); // cx.add_action(Editor::toggle_soft_wrap);
cx.add_action(Editor::toggle_inlay_hints); // cx.add_action(Editor::toggle_inlay_hints);
cx.add_action(Editor::reveal_in_finder); // cx.add_action(Editor::reveal_in_finder);
cx.add_action(Editor::copy_path); // cx.add_action(Editor::copy_path);
cx.add_action(Editor::copy_relative_path); // cx.add_action(Editor::copy_relative_path);
cx.add_action(Editor::copy_highlight_json); // cx.add_action(Editor::copy_highlight_json);
cx.add_async_action(Editor::format); // cx.add_async_action(Editor::format);
cx.add_action(Editor::restart_language_server); // cx.add_action(Editor::restart_language_server);
cx.add_action(Editor::show_character_palette); // cx.add_action(Editor::show_character_palette);
cx.add_async_action(Editor::confirm_completion); // cx.add_async_action(Editor::confirm_completion);
cx.add_async_action(Editor::confirm_code_action); // cx.add_async_action(Editor::confirm_code_action);
cx.add_async_action(Editor::rename); // cx.add_async_action(Editor::rename);
cx.add_async_action(Editor::confirm_rename); // cx.add_async_action(Editor::confirm_rename);
cx.add_async_action(Editor::find_all_references); // cx.add_async_action(Editor::find_all_references);
cx.add_action(Editor::next_copilot_suggestion); // cx.add_action(Editor::next_copilot_suggestion);
cx.add_action(Editor::previous_copilot_suggestion); // cx.add_action(Editor::previous_copilot_suggestion);
cx.add_action(Editor::copilot_suggest); // cx.add_action(Editor::copilot_suggest);
cx.add_action(Editor::context_menu_first); // cx.add_action(Editor::context_menu_first);
cx.add_action(Editor::context_menu_prev); // cx.add_action(Editor::context_menu_prev);
cx.add_action(Editor::context_menu_next); // cx.add_action(Editor::context_menu_next);
cx.add_action(Editor::context_menu_last); // cx.add_action(Editor::context_menu_last);
hover_popover::init(cx); hover_popover::init(cx);
/scroll::actions::init(cx); scroll::actions::init(cx);
workspace::register_project_item::<Editor>(cx); workspace::register_project_item::<Editor>(cx);
workspace::register_followable_item::<Editor>(cx); workspace::register_followable_item::<Editor>(cx);
@ -571,7 +574,7 @@ pub enum SelectPhase {
Update { Update {
position: DisplayPoint, position: DisplayPoint,
goal_column: u32, goal_column: u32,
scroll_position: Vector2F, scroll_position: Point<Pixels>,
}, },
End, End,
} }
@ -612,11 +615,11 @@ type CompletionId = usize;
type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>; type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
type BackgroundHighlight = (fn(&Theme) -> Color, Vec<Range<Anchor>>); type BackgroundHighlight = (fn(&Theme) -> Hsla, Vec<Range<Anchor>>);
type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec<InlayHighlight>); type InlayBackgroundHighlight = (fn(&Theme) -> Hsla, Vec<InlayHighlight>);
pub struct Editor { pub struct Editor {
handle: WeakViewHandle<Self>, handle: WeakView<Self>,
buffer: Model<MultiBuffer>, buffer: Model<MultiBuffer>,
display_map: Model<DisplayMap>, display_map: Model<DisplayMap>,
pub selections: SelectionsCollection, pub selections: SelectionsCollection,
@ -648,7 +651,7 @@ pub struct Editor {
inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>, inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
nav_history: Option<ItemNavHistory>, nav_history: Option<ItemNavHistory>,
context_menu: RwLock<Option<ContextMenu>>, context_menu: RwLock<Option<ContextMenu>>,
mouse_context_menu: ViewHandle<context_menu::ContextMenu>, mouse_context_menu: View<context_menu::ContextMenu>,
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>, completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
next_completion_id: CompletionId, next_completion_id: CompletionId,
available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>, available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
@ -659,7 +662,7 @@ pub struct Editor {
cursor_shape: CursorShape, cursor_shape: CursorShape,
collapse_matches: bool, collapse_matches: bool,
autoindent_mode: Option<AutoindentMode>, autoindent_mode: Option<AutoindentMode>,
workspace: Option<(WeakViewHandle<Workspace>, i64)>, workspace: Option<(WeakView<Workspace>, i64)>,
keymap_context_layers: BTreeMap<TypeId, KeymapContext>, keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
input_enabled: bool, input_enabled: bool,
read_only: bool, read_only: bool,
@ -672,7 +675,7 @@ pub struct Editor {
// inlay_hint_cache: InlayHintCache, // inlay_hint_cache: InlayHintCache,
next_inlay_id: usize, next_inlay_id: usize,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
pixel_position_of_newest_cursor: Option<Vector2F>, pixel_position_of_newest_cursor: Option<Point<Pixels>>,
} }
pub struct EditorSnapshot { pub struct EditorSnapshot {
@ -828,7 +831,7 @@ struct SnippetState {
pub struct RenameState { pub struct RenameState {
pub range: Range<Anchor>, pub range: Range<Anchor>,
pub old_name: Arc<str>, pub old_name: Arc<str>,
pub editor: ViewHandle<Editor>, pub editor: View<Editor>,
block_id: BlockId, block_id: BlockId,
} }
@ -915,7 +918,7 @@ impl ContextMenu {
&self, &self,
cursor_position: DisplayPoint, cursor_position: DisplayPoint,
style: EditorStyle, style: EditorStyle,
workspace: Option<WeakViewHandle<Workspace>>, workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> (DisplayPoint, AnyElement<Editor>) { ) -> (DisplayPoint, AnyElement<Editor>) {
match self { match self {
@ -938,22 +941,14 @@ struct CompletionsMenu {
} }
impl CompletionsMenu { impl CompletionsMenu {
fn select_first( fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
&mut self,
project: Option<&Model<Project>>,
cx: &mut ViewContext<Editor>,
) {
self.selected_item = 0; self.selected_item = 0;
self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.list.scroll_to(ScrollTarget::Show(self.selected_item));
self.attempt_resolve_selected_completion_documentation(project, cx); self.attempt_resolve_selected_completion_documentation(project, cx);
cx.notify(); cx.notify();
} }
fn select_prev( fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
&mut self,
project: Option<&Model<Project>>,
cx: &mut ViewContext<Editor>,
) {
if self.selected_item > 0 { if self.selected_item > 0 {
self.selected_item -= 1; self.selected_item -= 1;
} else { } else {
@ -964,11 +959,7 @@ impl CompletionsMenu {
cx.notify(); cx.notify();
} }
fn select_next( fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
&mut self,
project: Option<&Model<Project>>,
cx: &mut ViewContext<Editor>,
) {
if self.selected_item + 1 < self.matches.len() { if self.selected_item + 1 < self.matches.len() {
self.selected_item += 1; self.selected_item += 1;
} else { } else {
@ -979,11 +970,7 @@ impl CompletionsMenu {
cx.notify(); cx.notify();
} }
fn select_last( fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
&mut self,
project: Option<&Model<Project>>,
cx: &mut ViewContext<Editor>,
) {
self.selected_item = self.matches.len() - 1; self.selected_item = self.matches.len() - 1;
self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.list.scroll_to(ScrollTarget::Show(self.selected_item));
self.attempt_resolve_selected_completion_documentation(project, cx); self.attempt_resolve_selected_completion_documentation(project, cx);
@ -1241,7 +1228,7 @@ impl CompletionsMenu {
fn render( fn render(
&self, &self,
style: EditorStyle, style: EditorStyle,
workspace: Option<WeakViewHandle<Workspace>>, workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> AnyElement<Editor> { ) -> AnyElement<Editor> {
enum CompletionTag {} enum CompletionTag {}
@ -1760,7 +1747,7 @@ pub struct NavigationData {
scroll_top_row: u32, scroll_top_row: u32,
} }
pub struct EditorCreated(pub ViewHandle<Editor>); pub struct EditorCreated(pub View<Editor>);
enum GotoDefinitionKind { enum GotoDefinitionKind {
Symbol, Symbol,
@ -3845,8 +3832,8 @@ impl InlayHintRefreshReason {
// } // }
// async fn open_project_transaction( // async fn open_project_transaction(
// this: &WeakViewHandle<Editor>, // this: &WeakViewHandle<Editor
// workspace: WeakViewHandle<Workspace>, // workspace: WeakViewHandle<Workspace
// transaction: ProjectTransaction, // transaction: ProjectTransaction,
// title: String, // title: String,
// mut cx: AsyncAppContext, // mut cx: AsyncAppContext,
@ -9237,7 +9224,7 @@ impl EditorSnapshot {
self.placeholder_text.as_ref() self.placeholder_text.as_ref()
} }
pub fn scroll_position(&self) -> Vector2F { pub fn scroll_position(&self) -> Point<Pixels> {
self.scroll_anchor.scroll_position(&self.display_snapshot) self.scroll_anchor.scroll_position(&self.display_snapshot)
} }
} }
@ -9286,9 +9273,9 @@ pub enum Event {
Closed, Closed,
} }
pub struct EditorFocused(pub ViewHandle<Editor>); pub struct EditorFocused(pub View<Editor>);
pub struct EditorBlurred(pub ViewHandle<Editor>); pub struct EditorBlurred(pub View<Editor>);
pub struct EditorReleased(pub WeakViewHandle<Editor>); pub struct EditorReleased(pub WeakView<Editor>);
impl Entity for Editor { impl Entity for Editor {
type Event = Event; type Event = Event;
@ -9323,7 +9310,7 @@ impl View for Editor {
"Editor" "Editor"
} }
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) { fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
if cx.is_self_focused() { if cx.is_self_focused() {
let focused_event = EditorFocused(cx.handle()); let focused_event = EditorFocused(cx.handle());
cx.emit(Event::Focused); cx.emit(Event::Focused);
@ -9350,7 +9337,7 @@ impl View for Editor {
} }
} }
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) { fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
let blurred_event = EditorBlurred(cx.handle()); let blurred_event = EditorBlurred(cx.handle());
cx.emit_global(blurred_event); cx.emit_global(blurred_event);
self.focused = false; self.focused = false;
@ -9649,7 +9636,7 @@ fn build_style(
settings: &ThemeSettings, settings: &ThemeSettings,
get_field_editor_theme: Option<&GetFieldEditorTheme>, get_field_editor_theme: Option<&GetFieldEditorTheme>,
override_text_style: Option<&OverrideTextStyle>, override_text_style: Option<&OverrideTextStyle>,
cx: &AppContext, cx: &mut AppContext,
) -> EditorStyle { ) -> EditorStyle {
let font_cache = cx.font_cache(); let font_cache = cx.font_cache();
let line_height_scalar = settings.line_height(); let line_height_scalar = settings.line_height();

File diff suppressed because it is too large Load diff

View file

@ -88,195 +88,195 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
} }
} }
#[cfg(any(test, feature = "test_support"))] // #[cfg(any(test, feature = "test_support"))]
mod tests { // mod tests {
use crate::editor_tests::init_test; // // use crate::editor_tests::init_test;
use crate::Point; // use crate::Point;
use gpui::TestAppContext; // use gpui::TestAppContext;
use multi_buffer::{ExcerptRange, MultiBuffer}; // use multi_buffer::{ExcerptRange, MultiBuffer};
use project::{FakeFs, Project}; // use project::{FakeFs, Project};
use unindent::Unindent; // use unindent::Unindent;
#[gpui::test] // #[gpui::test]
async fn test_diff_hunks_in_range(cx: &mut TestAppContext) { // async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
use git::diff::DiffHunkStatus; // use git::diff::DiffHunkStatus;
init_test(cx, |_| {}); // init_test(cx, |_| {});
let fs = FakeFs::new(cx.background()); // let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; // let project = Project::test(fs, [], cx).await;
// buffer has two modified hunks with two rows each // // buffer has two modified hunks with two rows each
let buffer_1 = project // let buffer_1 = project
.update(cx, |project, cx| { // .update(cx, |project, cx| {
project.create_buffer( // project.create_buffer(
" // "
1.zero // 1.zero
1.ONE // 1.ONE
1.TWO // 1.TWO
1.three // 1.three
1.FOUR // 1.FOUR
1.FIVE // 1.FIVE
1.six // 1.six
" // "
.unindent() // .unindent()
.as_str(), // .as_str(),
None, // None,
cx, // cx,
) // )
}) // })
.unwrap(); // .unwrap();
buffer_1.update(cx, |buffer, cx| { // buffer_1.update(cx, |buffer, cx| {
buffer.set_diff_base( // buffer.set_diff_base(
Some( // Some(
" // "
1.zero // 1.zero
1.one // 1.one
1.two // 1.two
1.three // 1.three
1.four // 1.four
1.five // 1.five
1.six // 1.six
" // "
.unindent(), // .unindent(),
), // ),
cx, // cx,
); // );
}); // });
// buffer has a deletion hunk and an insertion hunk // // buffer has a deletion hunk and an insertion hunk
let buffer_2 = project // let buffer_2 = project
.update(cx, |project, cx| { // .update(cx, |project, cx| {
project.create_buffer( // project.create_buffer(
" // "
2.zero // 2.zero
2.one // 2.one
2.two // 2.two
2.three // 2.three
2.four // 2.four
2.five // 2.five
2.six // 2.six
" // "
.unindent() // .unindent()
.as_str(), // .as_str(),
None, // None,
cx, // cx,
) // )
}) // })
.unwrap(); // .unwrap();
buffer_2.update(cx, |buffer, cx| { // buffer_2.update(cx, |buffer, cx| {
buffer.set_diff_base( // buffer.set_diff_base(
Some( // Some(
" // "
2.zero // 2.zero
2.one // 2.one
2.one-and-a-half // 2.one-and-a-half
2.two // 2.two
2.three // 2.three
2.four // 2.four
2.six // 2.six
" // "
.unindent(), // .unindent(),
), // ),
cx, // cx,
); // );
}); // });
cx.foreground().run_until_parked(); // cx.foreground().run_until_parked();
let multibuffer = cx.add_model(|cx| { // let multibuffer = cx.add_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); // let mut multibuffer = MultiBuffer::new(0);
multibuffer.push_excerpts( // multibuffer.push_excerpts(
buffer_1.clone(), // buffer_1.clone(),
[ // [
// excerpt ends in the middle of a modified hunk // // excerpt ends in the middle of a modified hunk
ExcerptRange { // ExcerptRange {
context: Point::new(0, 0)..Point::new(1, 5), // context: Point::new(0, 0)..Point::new(1, 5),
primary: Default::default(), // primary: Default::default(),
}, // },
// excerpt begins in the middle of a modified hunk // // excerpt begins in the middle of a modified hunk
ExcerptRange { // ExcerptRange {
context: Point::new(5, 0)..Point::new(6, 5), // context: Point::new(5, 0)..Point::new(6, 5),
primary: Default::default(), // primary: Default::default(),
}, // },
], // ],
cx, // cx,
); // );
multibuffer.push_excerpts( // multibuffer.push_excerpts(
buffer_2.clone(), // buffer_2.clone(),
[ // [
// excerpt ends at a deletion // // excerpt ends at a deletion
ExcerptRange { // ExcerptRange {
context: Point::new(0, 0)..Point::new(1, 5), // context: Point::new(0, 0)..Point::new(1, 5),
primary: Default::default(), // primary: Default::default(),
}, // },
// excerpt starts at a deletion // // excerpt starts at a deletion
ExcerptRange { // ExcerptRange {
context: Point::new(2, 0)..Point::new(2, 5), // context: Point::new(2, 0)..Point::new(2, 5),
primary: Default::default(), // primary: Default::default(),
}, // },
// excerpt fully contains a deletion hunk // // excerpt fully contains a deletion hunk
ExcerptRange { // ExcerptRange {
context: Point::new(1, 0)..Point::new(2, 5), // context: Point::new(1, 0)..Point::new(2, 5),
primary: Default::default(), // primary: Default::default(),
}, // },
// excerpt fully contains an insertion hunk // // excerpt fully contains an insertion hunk
ExcerptRange { // ExcerptRange {
context: Point::new(4, 0)..Point::new(6, 5), // context: Point::new(4, 0)..Point::new(6, 5),
primary: Default::default(), // primary: Default::default(),
}, // },
], // ],
cx, // cx,
); // );
multibuffer // multibuffer
}); // });
let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx)); // let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
assert_eq!( // assert_eq!(
snapshot.text(), // snapshot.text(),
" // "
1.zero // 1.zero
1.ONE // 1.ONE
1.FIVE // 1.FIVE
1.six // 1.six
2.zero // 2.zero
2.one // 2.one
2.two // 2.two
2.one // 2.one
2.two // 2.two
2.four // 2.four
2.five // 2.five
2.six" // 2.six"
.unindent() // .unindent()
); // );
let expected = [ // let expected = [
(DiffHunkStatus::Modified, 1..2), // (DiffHunkStatus::Modified, 1..2),
(DiffHunkStatus::Modified, 2..3), // (DiffHunkStatus::Modified, 2..3),
//TODO: Define better when and where removed hunks show up at range extremities // //TODO: Define better when and where removed hunks show up at range extremities
(DiffHunkStatus::Removed, 6..6), // (DiffHunkStatus::Removed, 6..6),
(DiffHunkStatus::Removed, 8..8), // (DiffHunkStatus::Removed, 8..8),
(DiffHunkStatus::Added, 10..11), // (DiffHunkStatus::Added, 10..11),
]; // ];
assert_eq!( // assert_eq!(
snapshot // snapshot
.git_diff_hunks_in_range(0..12) // .git_diff_hunks_in_range(0..12)
.map(|hunk| (hunk.status(), hunk.buffer_range)) // .map(|hunk| (hunk.status(), hunk.buffer_range))
.collect::<Vec<_>>(), // .collect::<Vec<_>>(),
&expected, // &expected,
); // );
assert_eq!( // assert_eq!(
snapshot // snapshot
.git_diff_hunks_in_range_rev(0..12) // .git_diff_hunks_in_range_rev(0..12)
.map(|hunk| (hunk.status(), hunk.buffer_range)) // .map(|hunk| (hunk.status(), hunk.buffer_range))
.collect::<Vec<_>>(), // .collect::<Vec<_>>(),
expected // expected
.iter() // .iter()
.rev() // .rev()
.cloned() // .cloned()
.collect::<Vec<_>>() // .collect::<Vec<_>>()
.as_slice(), // .as_slice(),
); // );
} // }
} // }

View file

@ -30,109 +30,109 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
} }
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext}; // use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
use indoc::indoc; // use indoc::indoc;
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig}; // use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
#[gpui::test] // #[gpui::test]
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) { // async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {}); // init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new( // let mut cx = EditorLspTestContext::new(
Language::new( // Language::new(
LanguageConfig { // LanguageConfig {
name: "Rust".into(), // name: "Rust".into(),
path_suffixes: vec!["rs".to_string()], // path_suffixes: vec!["rs".to_string()],
brackets: BracketPairConfig { // brackets: BracketPairConfig {
pairs: vec![ // pairs: vec![
BracketPair { // BracketPair {
start: "{".to_string(), // start: "{".to_string(),
end: "}".to_string(), // end: "}".to_string(),
close: false, // close: false,
newline: true, // newline: true,
}, // },
BracketPair { // BracketPair {
start: "(".to_string(), // start: "(".to_string(),
end: ")".to_string(), // end: ")".to_string(),
close: false, // close: false,
newline: true, // newline: true,
}, // },
], // ],
..Default::default() // ..Default::default()
}, // },
..Default::default() // ..Default::default()
}, // },
Some(tree_sitter_rust::language()), // Some(tree_sitter_rust::language()),
) // )
.with_brackets_query(indoc! {r#" // .with_brackets_query(indoc! {r#"
("{" @open "}" @close) // ("{" @open "}" @close)
("(" @open ")" @close) // ("(" @open ")" @close)
"#}) // "#})
.unwrap(), // .unwrap(),
Default::default(), // Default::default(),
cx, // cx,
) // )
.await; // .await;
// positioning cursor inside bracket highlights both // // positioning cursor inside bracket highlights both
cx.set_state(indoc! {r#" // cx.set_state(indoc! {r#"
pub fn test("Test ˇargument") { // pub fn test("Test ˇargument") {
another_test(1, 2, 3); // another_test(1, 2, 3);
} // }
"#}); // "#});
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" // cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test«(»"Test argument"«)» { // pub fn test«(»"Test argument"«)» {
another_test(1, 2, 3); // another_test(1, 2, 3);
} // }
"#}); // "#});
cx.set_state(indoc! {r#" // cx.set_state(indoc! {r#"
pub fn test("Test argument") { // pub fn test("Test argument") {
another_test(1, ˇ2, 3); // another_test(1, ˇ2, 3);
} // }
"#}); // "#});
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" // cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") { // pub fn test("Test argument") {
another_test«(»1, 2, 3«)»; // another_test«(»1, 2, 3«)»;
} // }
"#}); // "#});
cx.set_state(indoc! {r#" // cx.set_state(indoc! {r#"
pub fn test("Test argument") { // pub fn test("Test argument") {
anotherˇ_test(1, 2, 3); // anotherˇ_test(1, 2, 3);
} // }
"#}); // "#});
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" // cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") «{» // pub fn test("Test argument") «{»
another_test(1, 2, 3); // another_test(1, 2, 3);
«}» // «}»
"#}); // "#});
// positioning outside of brackets removes highlight // // positioning outside of brackets removes highlight
cx.set_state(indoc! {r#" // cx.set_state(indoc! {r#"
pub fˇn test("Test argument") { // pub fˇn test("Test argument") {
another_test(1, 2, 3); // another_test(1, 2, 3);
} // }
"#}); // "#});
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" // cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") { // pub fn test("Test argument") {
another_test(1, 2, 3); // another_test(1, 2, 3);
} // }
"#}); // "#});
// non empty selection dismisses highlight // // non empty selection dismisses highlight
cx.set_state(indoc! {r#" // cx.set_state(indoc! {r#"
pub fn test("Te«st argˇ»ument") { // pub fn test("Te«st argˇ»ument") {
another_test(1, 2, 3); // another_test(1, 2, 3);
} // }
"#}); // "#});
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" // cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") { // pub fn test("Test argument") {
another_test(1, 2, 3); // another_test(1, 2, 3);
} // }
"#}); // "#});
} // }
} // }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -20,23 +20,18 @@ use smallvec::SmallVec;
use std::{ use std::{
borrow::Cow, borrow::Cow,
cmp::{self, Ordering}, cmp::{self, Ordering},
fmt::Write,
iter, iter,
ops::Range, ops::Range,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
}; };
use text::Selection; use text::Selection;
use util::{ use util::{paths::PathExt, ResultExt, TryFutureExt};
paths::{PathExt, FILE_ROW_COLUMN_DELIMITER},
ResultExt, TryFutureExt,
};
use workspace::item::{BreadcrumbText, FollowableItemHandle}; use workspace::item::{BreadcrumbText, FollowableItemHandle};
use workspace::{ use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem}, item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle}, searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, ViewId, Workspace, ItemId, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
WorkspaceId,
}; };
pub const MAX_TAB_TITLE_LEN: usize = 24; pub const MAX_TAB_TITLE_LEN: usize = 24;
@ -607,7 +602,7 @@ impl Item for Editor {
where where
Self: Sized, Self: Sized,
{ {
Some(self.clone(cx)) Some(self.clone())
} }
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) { fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,10 @@
use crate::{ use crate::{DisplayPoint, Editor, EditorMode, SelectMode};
DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
Rename, RevealInFinder, SelectMode, ToggleCodeActions,
};
use context_menu::ContextMenuItem; use context_menu::ContextMenuItem;
use gpui::{elements::AnchorCorner, geometry::vector::Vector2F, ViewContext}; use gpui::{Pixels, Point, ViewContext};
pub fn deploy_context_menu( pub fn deploy_context_menu(
editor: &mut Editor, editor: &mut Editor,
position: Vector2F, position: Point<Pixels>,
point: DisplayPoint, point: DisplayPoint,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
@ -31,66 +28,67 @@ pub fn deploy_context_menu(
s.set_pending_display_range(point..point, SelectMode::Character); s.set_pending_display_range(point..point, SelectMode::Character);
}); });
editor.mouse_context_menu.update(cx, |menu, cx| { // todo!()
menu.show( // editor.mouse_context_menu.update(cx, |menu, cx| {
position, // menu.show(
AnchorCorner::TopLeft, // position,
vec![ // AnchorCorner::TopLeft,
ContextMenuItem::action("Rename Symbol", Rename), // vec![
ContextMenuItem::action("Go to Definition", GoToDefinition), // ContextMenuItem::action("Rename Symbol", Rename),
ContextMenuItem::action("Go to Type Definition", GoToTypeDefinition), // ContextMenuItem::action("Go to Definition", GoToDefinition),
ContextMenuItem::action("Find All References", FindAllReferences), // ContextMenuItem::action("Go to Type Definition", GoToTypeDefinition),
ContextMenuItem::action( // ContextMenuItem::action("Find All References", FindAllReferences),
"Code Actions", // ContextMenuItem::action(
ToggleCodeActions { // "Code Actions",
deployed_from_indicator: false, // ToggleCodeActions {
}, // deployed_from_indicator: false,
), // },
ContextMenuItem::Separator, // ),
ContextMenuItem::action("Reveal in Finder", RevealInFinder), // ContextMenuItem::Separator,
], // ContextMenuItem::action("Reveal in Finder", RevealInFinder),
cx, // ],
); // cx,
}); // );
// });
cx.notify(); cx.notify();
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext}; // use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
use indoc::indoc; // use indoc::indoc;
#[gpui::test] // #[gpui::test]
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) { // async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {}); // init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust( // let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities { // lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), // hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
..Default::default() // ..Default::default()
}, // },
cx, // cx,
) // )
.await; // .await;
cx.set_state(indoc! {" // cx.set_state(indoc! {"
fn teˇst() { // fn teˇst() {
do_work(); // do_work();
} // }
"}); // "});
let point = cx.display_point(indoc! {" // let point = cx.display_point(indoc! {"
fn test() { // fn test() {
do_wˇork(); // do_wˇork();
} // }
"}); // "});
cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx)); // cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
cx.assert_editor_state(indoc! {" // cx.assert_editor_state(indoc! {"
fn test() { // fn test() {
do_wˇork(); // do_wˇork();
} // }
"}); // "});
cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible())); // cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
} // }
} // }

View file

@ -1,6 +1,6 @@
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint}; use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{FontCache, TextLayoutCache}; use gpui::TextSystem;
use language::Point; use language::Point;
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
@ -13,8 +13,7 @@ pub enum FindRange {
/// TextLayoutDetails encompasses everything we need to move vertically /// TextLayoutDetails encompasses everything we need to move vertically
/// taking into account variable width characters. /// taking into account variable width characters.
pub struct TextLayoutDetails { pub struct TextLayoutDetails {
pub font_cache: Arc<FontCache>, pub text_system: TextSystem,
pub text_layout_cache: Arc<TextLayoutCache>,
pub editor_style: EditorStyle, pub editor_style: EditorStyle,
} }

View file

@ -2,19 +2,6 @@ pub mod actions;
pub mod autoscroll; pub mod autoscroll;
pub mod scroll_amount; pub mod scroll_amount;
use std::{
cmp::Ordering,
time::{Duration, Instant},
};
use gpui::{
geometry::vector::{vec2f, Vector2F},
AppContext, Axis, Task, ViewContext,
};
use language::{Bias, Point};
use util::ResultExt;
use workspace::WorkspaceId;
use crate::{ use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint}, display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover, hover_popover::hide_hover,
@ -22,6 +9,14 @@ use crate::{
Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot, Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
ToPoint, ToPoint,
}; };
use gpui::{point, AppContext, Pixels, Task, ViewContext};
use language::{Bias, Point};
use std::{
cmp::Ordering,
time::{Duration, Instant},
};
use util::ResultExt;
use workspace::WorkspaceId;
use self::{ use self::{
autoscroll::{Autoscroll, AutoscrollStrategy}, autoscroll::{Autoscroll, AutoscrollStrategy},
@ -37,19 +32,19 @@ pub struct ScrollbarAutoHide(pub bool);
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct ScrollAnchor { pub struct ScrollAnchor {
pub offset: Vector2F, pub offset: gpui::Point<f32>,
pub anchor: Anchor, pub anchor: Anchor,
} }
impl ScrollAnchor { impl ScrollAnchor {
fn new() -> Self { fn new() -> Self {
Self { Self {
offset: Vector2F::zero(), offset: Point::zero(),
anchor: Anchor::min(), anchor: Anchor::min(),
} }
} }
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F { pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Point<Pixels> {
let mut scroll_position = self.offset; let mut scroll_position = self.offset;
if self.anchor != Anchor::min() { if self.anchor != Anchor::min() {
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32; let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
@ -65,6 +60,12 @@ impl ScrollAnchor {
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Axis {
Vertical,
Horizontal,
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct OngoingScroll { pub struct OngoingScroll {
last_event: Instant, last_event: Instant,
@ -79,7 +80,7 @@ impl OngoingScroll {
} }
} }
pub fn filter(&self, delta: &mut Vector2F) -> Option<Axis> { pub fn filter(&self, delta: &mut Point<Pixels>) -> Option<Axis> {
const UNLOCK_PERCENT: f32 = 1.9; const UNLOCK_PERCENT: f32 = 1.9;
const UNLOCK_LOWER_BOUND: f32 = 6.; const UNLOCK_LOWER_BOUND: f32 = 6.;
let mut axis = self.axis; let mut axis = self.axis;
@ -114,8 +115,8 @@ impl OngoingScroll {
} }
match axis { match axis {
Some(Axis::Vertical) => *delta = vec2f(0., delta.y()), Some(Axis::Vertical) => *delta = point(0., delta.y()),
Some(Axis::Horizontal) => *delta = vec2f(delta.x(), 0.), Some(Axis::Horizontal) => *delta = point(delta.x(), 0.),
None => {} None => {}
} }
@ -128,7 +129,7 @@ pub struct ScrollManager {
anchor: ScrollAnchor, anchor: ScrollAnchor,
ongoing: OngoingScroll, ongoing: OngoingScroll,
autoscroll_request: Option<(Autoscroll, bool)>, autoscroll_request: Option<(Autoscroll, bool)>,
last_autoscroll: Option<(Vector2F, f32, f32, AutoscrollStrategy)>, last_autoscroll: Option<(gpui::Point<Pixels>, f32, f32, AutoscrollStrategy)>,
show_scrollbars: bool, show_scrollbars: bool,
hide_scrollbar_task: Option<Task<()>>, hide_scrollbar_task: Option<Task<()>>,
visible_line_count: Option<f32>, visible_line_count: Option<f32>,
@ -166,13 +167,13 @@ impl ScrollManager {
self.ongoing.axis = axis; self.ongoing.axis = axis;
} }
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F { pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Point<Pixels> {
self.anchor.scroll_position(snapshot) self.anchor.scroll_position(snapshot)
} }
fn set_scroll_position( fn set_scroll_position(
&mut self, &mut self,
scroll_position: Vector2F, scroll_position: Point<Pixels>,
map: &DisplaySnapshot, map: &DisplaySnapshot,
local: bool, local: bool,
autoscroll: bool, autoscroll: bool,
@ -183,7 +184,7 @@ impl ScrollManager {
( (
ScrollAnchor { ScrollAnchor {
anchor: Anchor::min(), anchor: Anchor::min(),
offset: scroll_position.max(vec2f(0., 0.)), offset: scroll_position.max(Point::zero()),
}, },
0, 0,
) )
@ -197,7 +198,7 @@ impl ScrollManager {
( (
ScrollAnchor { ScrollAnchor {
anchor: top_anchor, anchor: top_anchor,
offset: vec2f( offset: point(
scroll_position.x(), scroll_position.x(),
scroll_position.y() - top_anchor.to_display_point(&map).row() as f32, scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
), ),
@ -310,13 +311,17 @@ impl Editor {
} }
} }
pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) { pub fn set_scroll_position(
&mut self,
scroll_position: Point<Pixels>,
cx: &mut ViewContext<Self>,
) {
self.set_scroll_position_internal(scroll_position, true, false, cx); self.set_scroll_position_internal(scroll_position, true, false, cx);
} }
pub(crate) fn set_scroll_position_internal( pub(crate) fn set_scroll_position_internal(
&mut self, &mut self,
scroll_position: Vector2F, scroll_position: Point<Pixels>,
local: bool, local: bool,
autoscroll: bool, autoscroll: bool,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
@ -337,7 +342,7 @@ impl Editor {
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
} }
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F { pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Point<Pixels> {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.scroll_manager.anchor.scroll_position(&display_map) self.scroll_manager.anchor.scroll_position(&display_map)
} }
@ -379,7 +384,7 @@ impl Editor {
} }
let cur_position = self.scroll_position(cx); let cur_position = self.scroll_position(cx);
let new_pos = cur_position + vec2f(0., amount.lines(self)); let new_pos = cur_position + point(0., amount.lines(self));
self.set_scroll_position(new_pos, cx); self.set_scroll_position(new_pos, cx);
} }
@ -427,7 +432,7 @@ impl Editor {
.snapshot(cx) .snapshot(cx)
.anchor_at(Point::new(top_row as u32, 0), Bias::Left); .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
let scroll_anchor = ScrollAnchor { let scroll_anchor = ScrollAnchor {
offset: Vector2F::new(x, y), offset: Point::new(x, y),
anchor: top_anchor, anchor: top_anchor,
}; };
self.set_scroll_anchor(scroll_anchor, cx); self.set_scroll_anchor(scroll_anchor, cx);

View file

@ -17,7 +17,7 @@ use gpui::AppContext;
// ); // );
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
/// todo!() // todo!()
// cx.add_action(Editor::next_screen); // cx.add_action(Editor::next_screen);
// cx.add_action(Editor::scroll_cursor_top); // cx.add_action(Editor::scroll_cursor_top);
// cx.add_action(Editor::scroll_cursor_center); // cx.add_action(Editor::scroll_cursor_center);

View file

@ -25,8 +25,8 @@ pub struct PendingSelection {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SelectionsCollection { pub struct SelectionsCollection {
display_map: ModelHandle<DisplayMap>, display_map: Model<DisplayMap>,
buffer: ModelHandle<MultiBuffer>, buffer: Model<MultiBuffer>,
pub next_selection_id: usize, pub next_selection_id: usize,
pub line_mode: bool, pub line_mode: bool,
disjoint: Arc<[Selection<Anchor>]>, disjoint: Arc<[Selection<Anchor>]>,
@ -34,7 +34,7 @@ pub struct SelectionsCollection {
} }
impl SelectionsCollection { impl SelectionsCollection {
pub fn new(display_map: ModelHandle<DisplayMap>, buffer: ModelHandle<MultiBuffer>) -> Self { pub fn new(display_map: Model<DisplayMap>, buffer: Model<MultiBuffer>) -> Self {
Self { Self {
display_map, display_map,
buffer, buffer,

View file

@ -67,16 +67,13 @@ pub fn assert_text_with_selections(
// RA thinks this is dead code even though it is used in a whole lot of tests // RA thinks this is dead code even though it is used in a whole lot of tests
#[allow(dead_code)] #[allow(dead_code)]
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub(crate) fn build_editor( pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
buffer: ModelHandle<MultiBuffer>,
cx: &mut ViewContext<Editor>,
) -> Editor {
Editor::new(EditorMode::Full, buffer, None, None, cx) Editor::new(EditorMode::Full, buffer, None, None, cx)
} }
pub(crate) fn build_editor_with_project( pub(crate) fn build_editor_with_project(
project: ModelHandle<Project>, project: Model<Project>,
buffer: ModelHandle<MultiBuffer>, buffer: Model<MultiBuffer>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Editor { ) -> Editor {
Editor::new(EditorMode::Full, buffer, Some(project), None, cx) Editor::new(EditorMode::Full, buffer, Some(project), None, cx)

View file

@ -9,7 +9,7 @@ use anyhow::Result;
use crate::{Editor, ToPoint}; use crate::{Editor, ToPoint};
use collections::HashSet; use collections::HashSet;
use futures::Future; use futures::Future;
use gpui::{json, ViewContext, ViewHandle}; use gpui::{json, View, ViewContext};
use indoc::indoc; use indoc::indoc;
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries}; use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
use lsp::{notification, request}; use lsp::{notification, request};

View file

@ -3,8 +3,7 @@ use crate::{
}; };
use futures::Future; use futures::Future;
use gpui::{ use gpui::{
executor::Foreground, keymap_matcher::Keystroke, AnyWindowHandle, AppContext, ContextHandle, AnyWindowHandle, AppContext, ForegroundExecutor, Keystroke, ModelContext, View, ViewContext,
ModelContext, ViewContext, ViewHandle,
}; };
use indoc::indoc; use indoc::indoc;
use language::{Buffer, BufferSnapshot}; use language::{Buffer, BufferSnapshot};
@ -23,7 +22,7 @@ use super::build_editor_with_project;
pub struct EditorTestContext<'a> { pub struct EditorTestContext<'a> {
pub cx: &'a mut gpui::TestAppContext, pub cx: &'a mut gpui::TestAppContext,
pub window: AnyWindowHandle, pub window: AnyWindowHandle,
pub editor: ViewHandle<Editor>, pub editor: View<Editor>,
} }
impl<'a> EditorTestContext<'a> { impl<'a> EditorTestContext<'a> {
@ -119,37 +118,37 @@ impl<'a> EditorTestContext<'a> {
self.buffer(|buffer, _| buffer.snapshot()) self.buffer(|buffer, _| buffer.snapshot())
} }
pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle { // pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
let keystroke_under_test_handle = // let keystroke_under_test_handle =
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text)); // self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
let keystroke = Keystroke::parse(keystroke_text).unwrap(); // let keystroke = Keystroke::parse(keystroke_text).unwrap();
self.cx.dispatch_keystroke(self.window, keystroke, false); // self.cx.dispatch_keystroke(self.window, keystroke, false);
keystroke_under_test_handle // keystroke_under_test_handle
} // }
pub fn simulate_keystrokes<const COUNT: usize>( // pub fn simulate_keystrokes<const COUNT: usize>(
&mut self, // &mut self,
keystroke_texts: [&str; COUNT], // keystroke_texts: [&str; COUNT],
) -> ContextHandle { // ) -> ContextHandle {
let keystrokes_under_test_handle = // let keystrokes_under_test_handle =
self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts)); // self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
for keystroke_text in keystroke_texts.into_iter() { // for keystroke_text in keystroke_texts.into_iter() {
self.simulate_keystroke(keystroke_text); // self.simulate_keystroke(keystroke_text);
} // }
// it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete // // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
// before returning. // // before returning.
// NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too // // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
// quickly races with async actions. // // quickly races with async actions.
if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() { // if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
executor.run_until_parked(); // executor.run_until_parked();
} else { // } else {
unreachable!(); // unreachable!();
} // }
keystrokes_under_test_handle // keystrokes_under_test_handle
} // }
pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> { pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false); let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
@ -177,144 +176,144 @@ impl<'a> EditorTestContext<'a> {
self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx)); self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
} }
/// Change the editor's text and selections using a string containing // /// Change the editor's text and selections using a string containing
/// embedded range markers that represent the ranges and directions of // /// embedded range markers that represent the ranges and directions of
/// each selection. // /// each selection.
/// // ///
/// Returns a context handle so that assertion failures can print what // /// Returns a context handle so that assertion failures can print what
/// editor state was needed to cause the failure. // /// editor state was needed to cause the failure.
/// // ///
/// See the `util::test::marked_text_ranges` function for more information. // /// See the `util::test::marked_text_ranges` function for more information.
pub fn set_state(&mut self, marked_text: &str) -> ContextHandle { // pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
let state_context = self.add_assertion_context(format!( // let state_context = self.add_assertion_context(format!(
"Initial Editor State: \"{}\"", // "Initial Editor State: \"{}\"",
marked_text.escape_debug().to_string() // marked_text.escape_debug().to_string()
)); // ));
let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); // let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
self.editor.update(self.cx, |editor, cx| { // self.editor.update(self.cx, |editor, cx| {
editor.set_text(unmarked_text, cx); // editor.set_text(unmarked_text, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| { // editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(selection_ranges) // s.select_ranges(selection_ranges)
}) // })
}); // });
state_context // state_context
} // }
/// Only change the editor's selections // /// Only change the editor's selections
pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle { // pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
let state_context = self.add_assertion_context(format!( // let state_context = self.add_assertion_context(format!(
"Initial Editor State: \"{}\"", // "Initial Editor State: \"{}\"",
marked_text.escape_debug().to_string() // marked_text.escape_debug().to_string()
)); // ));
let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); // let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
self.editor.update(self.cx, |editor, cx| { // self.editor.update(self.cx, |editor, cx| {
assert_eq!(editor.text(cx), unmarked_text); // assert_eq!(editor.text(cx), unmarked_text);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| { // editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(selection_ranges) // s.select_ranges(selection_ranges)
}) // })
}); // });
state_context // state_context
} // }
/// Make an assertion about the editor's text and the ranges and directions // /// Make an assertion about the editor's text and the ranges and directions
/// of its selections using a string containing embedded range markers. // /// of its selections using a string containing embedded range markers.
/// // ///
/// See the `util::test::marked_text_ranges` function for more information. // /// See the `util::test::marked_text_ranges` function for more information.
#[track_caller] // #[track_caller]
pub fn assert_editor_state(&mut self, marked_text: &str) { // pub fn assert_editor_state(&mut self, marked_text: &str) {
let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true); // let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
let buffer_text = self.buffer_text(); // let buffer_text = self.buffer_text();
if buffer_text != unmarked_text { // if buffer_text != unmarked_text {
panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}"); // panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
} // }
self.assert_selections(expected_selections, marked_text.to_string()) // self.assert_selections(expected_selections, marked_text.to_string())
} // }
pub fn editor_state(&mut self) -> String { // pub fn editor_state(&mut self) -> String {
generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true) // generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
} // }
#[track_caller] // #[track_caller]
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) { // pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
let expected_ranges = self.ranges(marked_text); // let expected_ranges = self.ranges(marked_text);
let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| { // let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
let snapshot = editor.snapshot(cx); // let snapshot = editor.snapshot(cx);
editor // editor
.background_highlights // .background_highlights
.get(&TypeId::of::<Tag>()) // .get(&TypeId::of::<Tag>())
.map(|h| h.1.clone()) // .map(|h| h.1.clone())
.unwrap_or_default() // .unwrap_or_default()
.into_iter() // .into_iter()
.map(|range| range.to_offset(&snapshot.buffer_snapshot)) // .map(|range| range.to_offset(&snapshot.buffer_snapshot))
.collect() // .collect()
}); // });
assert_set_eq!(actual_ranges, expected_ranges); // assert_set_eq!(actual_ranges, expected_ranges);
} // }
#[track_caller] // #[track_caller]
pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) { // pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
let expected_ranges = self.ranges(marked_text); // let expected_ranges = self.ranges(marked_text);
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); // let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
let actual_ranges: Vec<Range<usize>> = snapshot // let actual_ranges: Vec<Range<usize>> = snapshot
.text_highlight_ranges::<Tag>() // .text_highlight_ranges::<Tag>()
.map(|ranges| ranges.as_ref().clone().1) // .map(|ranges| ranges.as_ref().clone().1)
.unwrap_or_default() // .unwrap_or_default()
.into_iter() // .into_iter()
.map(|range| range.to_offset(&snapshot.buffer_snapshot)) // .map(|range| range.to_offset(&snapshot.buffer_snapshot))
.collect(); // .collect();
assert_set_eq!(actual_ranges, expected_ranges); // assert_set_eq!(actual_ranges, expected_ranges);
} // }
#[track_caller] // #[track_caller]
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) { // pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
let expected_marked_text = // let expected_marked_text =
generate_marked_text(&self.buffer_text(), &expected_selections, true); // generate_marked_text(&self.buffer_text(), &expected_selections, true);
self.assert_selections(expected_selections, expected_marked_text) // self.assert_selections(expected_selections, expected_marked_text)
} // }
fn editor_selections(&self) -> Vec<Range<usize>> { // fn editor_selections(&self) -> Vec<Range<usize>> {
self.editor // self.editor
.read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx)) // .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
.into_iter() // .into_iter()
.map(|s| { // .map(|s| {
if s.reversed { // if s.reversed {
s.end..s.start // s.end..s.start
} else { // } else {
s.start..s.end // s.start..s.end
} // }
}) // })
.collect::<Vec<_>>() // .collect::<Vec<_>>()
} // }
#[track_caller] // #[track_caller]
fn assert_selections( // fn assert_selections(
&mut self, // &mut self,
expected_selections: Vec<Range<usize>>, // expected_selections: Vec<Range<usize>>,
expected_marked_text: String, // expected_marked_text: String,
) { // ) {
let actual_selections = self.editor_selections(); // let actual_selections = self.editor_selections();
let actual_marked_text = // let actual_marked_text =
generate_marked_text(&self.buffer_text(), &actual_selections, true); // generate_marked_text(&self.buffer_text(), &actual_selections, true);
if expected_selections != actual_selections { // if expected_selections != actual_selections {
panic!( // panic!(
indoc! {" // indoc! {"
{}Editor has unexpected selections. // {}Editor has unexpected selections.
Expected selections: // Expected selections:
{} // {}
Actual selections: // Actual selections:
{} // {}
"}, // "},
self.assertion_context(), // self.assertion_context(),
expected_marked_text, // expected_marked_text,
actual_marked_text, // actual_marked_text,
); // );
} // }
} // }
} }
impl<'a> Deref for EditorTestContext<'a> { impl<'a> Deref for EditorTestContext<'a> {

View file

@ -7,7 +7,7 @@ use anyhow::anyhow;
pub use font_features::*; pub use font_features::*;
pub use line::*; pub use line::*;
pub use line_layout::*; pub use line_layout::*;
use line_wrapper::*; pub use line_wrapper::*;
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{

View file

@ -58,6 +58,7 @@ unicase = "2.6"
rand = { workspace = true, optional = true } rand = { workspace = true, optional = true }
tree-sitter-rust = { workspace = true, optional = true } tree-sitter-rust = { workspace = true, optional = true }
tree-sitter-typescript = { workspace = true, optional = true } tree-sitter-typescript = { workspace = true, optional = true }
pulldown-cmark = { version = "0.9.2", default-features = false }
[dev-dependencies] [dev-dependencies]
client = { package = "client2", path = "../client2", features = ["test-support"] } client = { package = "client2", path = "../client2", features = ["test-support"] }

View file

@ -8,6 +8,7 @@ mod syntax_map;
#[cfg(test)] #[cfg(test)]
mod buffer_tests; mod buffer_tests;
pub mod markdown;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;

View file

@ -0,0 +1,301 @@
use std::sync::Arc;
use std::{ops::Range, path::PathBuf};
use crate::{HighlightId, Language, LanguageRegistry};
use gpui::{px, FontStyle, FontWeight, HighlightStyle, UnderlineStyle};
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
#[derive(Debug, Clone)]
pub struct ParsedMarkdown {
pub text: String,
pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
pub region_ranges: Vec<Range<usize>>,
pub regions: Vec<ParsedRegion>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MarkdownHighlight {
Style(MarkdownHighlightStyle),
Code(HighlightId),
}
impl MarkdownHighlight {
pub fn to_highlight_style(&self, theme: &theme::SyntaxTheme) -> Option<HighlightStyle> {
match self {
MarkdownHighlight::Style(style) => {
let mut highlight = HighlightStyle::default();
if style.italic {
highlight.font_style = Some(FontStyle::Italic);
}
if style.underline {
highlight.underline = Some(UnderlineStyle {
thickness: px(1.),
..Default::default()
});
}
if style.weight != FontWeight::default() {
highlight.font_weight = Some(style.weight);
}
Some(highlight)
}
MarkdownHighlight::Code(id) => id.style(theme),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MarkdownHighlightStyle {
pub italic: bool,
pub underline: bool,
pub weight: FontWeight,
}
#[derive(Debug, Clone)]
pub struct ParsedRegion {
pub code: bool,
pub link: Option<Link>,
}
#[derive(Debug, Clone)]
pub enum Link {
Web { url: String },
Path { path: PathBuf },
}
impl Link {
fn identify(text: String) -> Option<Link> {
if text.starts_with("http") {
return Some(Link::Web { url: text });
}
let path = PathBuf::from(text);
if path.is_absolute() {
return Some(Link::Path { path });
}
None
}
}
pub async fn parse_markdown(
markdown: &str,
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
) -> ParsedMarkdown {
let mut text = String::new();
let mut highlights = Vec::new();
let mut region_ranges = Vec::new();
let mut regions = Vec::new();
parse_markdown_block(
markdown,
language_registry,
language,
&mut text,
&mut highlights,
&mut region_ranges,
&mut regions,
)
.await;
ParsedMarkdown {
text,
highlights,
region_ranges,
regions,
}
}
pub async fn parse_markdown_block(
markdown: &str,
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
text: &mut String,
highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
region_ranges: &mut Vec<Range<usize>>,
regions: &mut Vec<ParsedRegion>,
) {
let mut bold_depth = 0;
let mut italic_depth = 0;
let mut link_url = None;
let mut current_language = None;
let mut list_stack = Vec::new();
for event in Parser::new_ext(&markdown, Options::all()) {
let prev_len = text.len();
match event {
Event::Text(t) => {
if let Some(language) = &current_language {
highlight_code(text, highlights, t.as_ref(), language);
} else {
text.push_str(t.as_ref());
let mut style = MarkdownHighlightStyle::default();
if bold_depth > 0 {
style.weight = FontWeight::BOLD;
}
if italic_depth > 0 {
style.italic = true;
}
if let Some(link) = link_url.clone().and_then(|u| Link::identify(u)) {
region_ranges.push(prev_len..text.len());
regions.push(ParsedRegion {
code: false,
link: Some(link),
});
style.underline = true;
}
if style != MarkdownHighlightStyle::default() {
let mut new_highlight = true;
if let Some((last_range, MarkdownHighlight::Style(last_style))) =
highlights.last_mut()
{
if last_range.end == prev_len && last_style == &style {
last_range.end = text.len();
new_highlight = false;
}
}
if new_highlight {
let range = prev_len..text.len();
highlights.push((range, MarkdownHighlight::Style(style)));
}
}
}
}
Event::Code(t) => {
text.push_str(t.as_ref());
region_ranges.push(prev_len..text.len());
let link = link_url.clone().and_then(|u| Link::identify(u));
if link.is_some() {
highlights.push((
prev_len..text.len(),
MarkdownHighlight::Style(MarkdownHighlightStyle {
underline: true,
..Default::default()
}),
));
}
regions.push(ParsedRegion { code: true, link });
}
Event::Start(tag) => match tag {
Tag::Paragraph => new_paragraph(text, &mut list_stack),
Tag::Heading(_, _, _) => {
new_paragraph(text, &mut list_stack);
bold_depth += 1;
}
Tag::CodeBlock(kind) => {
new_paragraph(text, &mut list_stack);
current_language = if let CodeBlockKind::Fenced(language) = kind {
language_registry
.language_for_name(language.as_ref())
.await
.ok()
} else {
language.clone()
}
}
Tag::Emphasis => italic_depth += 1,
Tag::Strong => bold_depth += 1,
Tag::Link(_, url, _) => link_url = Some(url.to_string()),
Tag::List(number) => {
list_stack.push((number, false));
}
Tag::Item => {
let len = list_stack.len();
if let Some((list_number, has_content)) = list_stack.last_mut() {
*has_content = false;
if !text.is_empty() && !text.ends_with('\n') {
text.push('\n');
}
for _ in 0..len - 1 {
text.push_str(" ");
}
if let Some(number) = list_number {
text.push_str(&format!("{}. ", number));
*number += 1;
*has_content = false;
} else {
text.push_str("- ");
}
}
}
_ => {}
},
Event::End(tag) => match tag {
Tag::Heading(_, _, _) => bold_depth -= 1,
Tag::CodeBlock(_) => current_language = None,
Tag::Emphasis => italic_depth -= 1,
Tag::Strong => bold_depth -= 1,
Tag::Link(_, _, _) => link_url = None,
Tag::List(_) => drop(list_stack.pop()),
_ => {}
},
Event::HardBreak => text.push('\n'),
Event::SoftBreak => text.push(' '),
_ => {}
}
}
}
pub fn highlight_code(
text: &mut String,
highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
content: &str,
language: &Arc<Language>,
) {
let prev_len = text.len();
text.push_str(content);
for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
let highlight = MarkdownHighlight::Code(highlight_id);
highlights.push((prev_len + range.start..prev_len + range.end, highlight));
}
}
pub fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
let mut is_subsequent_paragraph_of_list = false;
if let Some((_, has_content)) = list_stack.last_mut() {
if *has_content {
is_subsequent_paragraph_of_list = true;
} else {
*has_content = true;
return;
}
}
if !text.is_empty() {
if !text.ends_with('\n') {
text.push('\n');
}
text.push('\n');
}
for _ in 0..list_stack.len().saturating_sub(1) {
text.push_str(" ");
}
if is_subsequent_paragraph_of_list {
text.push_str(" ");
}
}