WIP
This commit is contained in:
parent
583c36e24b
commit
7b712ac68f
25 changed files with 13799 additions and 13530 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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
|
@ -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> {
|
||||||
|
|
|
@ -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::{
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
@ -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(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -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
|
@ -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
|
@ -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()));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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::{
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
301
crates/language2/src/markdown.rs
Normal file
301
crates/language2/src/markdown.rs
Normal 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) = ¤t_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(" ");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue