add preview editor

This commit is contained in:
Dongdong Zhou 2023-03-23 22:08:49 +00:00
parent 31109e6c54
commit 9ec0b77946
5 changed files with 202 additions and 104 deletions

View File

@ -11,9 +11,9 @@
parley::style::FontWeight,
peniko::kurbo::{Point, Rect, Size},
reactive::{
create_effect, create_memo, provide_context, ReadSignal, RwSignal,
SignalGet, SignalGetUntracked, SignalSet, SignalUpdate, SignalWith,
SignalWithUntracked,
create_effect, create_memo, create_rw_signal, provide_context, ReadSignal,
RwSignal, SignalGet, SignalGetUntracked, SignalSet, SignalUpdate,
SignalWith, SignalWithUntracked,
},
stack::stack,
style::{
@ -46,7 +46,7 @@
main_split::{MainSplitData, SplitContent, SplitData, SplitDirection},
palette::{
item::{PaletteItem, PaletteItemContent},
PaletteStatus,
PaletteData, PaletteStatus,
},
title::title,
window::WindowData,
@ -259,17 +259,8 @@ fn insert_cursor(
}
fn editor_gutter(cx: AppContext, editor: RwSignal<EditorData>) -> impl View {
let (doc, cursor, scroll_delta, viewport, gutter_viewport, config) = editor
.with(|editor| {
(
editor.doc.read_only(),
editor.cursor.read_only(),
editor.scroll_delta.read_only(),
editor.viewport,
editor.gutter_viewport,
editor.config,
)
});
let (viewport, gutter_viewport, config) = editor
.with(|editor| (editor.viewport, editor.gutter_viewport, editor.config));
stack(cx, |cx| {
(
@ -280,7 +271,10 @@ fn editor_gutter(cx: AppContext, editor: RwSignal<EditorData>) -> impl View {
..Default::default()
}),
label(cx, move || {
doc.with(|doc| (doc.buffer().last_line() + 1).to_string())
editor
.get()
.doc
.with(|doc| (doc.buffer().last_line() + 1).to_string())
})
.style(cx, || Style {
..Default::default()
@ -299,7 +293,7 @@ fn editor_gutter(cx: AppContext, editor: RwSignal<EditorData>) -> impl View {
virtual_list(
cx,
VirtualListDirection::Vertical,
move || doc.get(),
move || editor.get().doc.get(),
|line: &DocLine| line.line,
move |cx, line: DocLine| {
stack(cx, move |cx| {
@ -379,7 +373,7 @@ fn editor_gutter(cx: AppContext, editor: RwSignal<EditorData>) -> impl View {
fn editor_cursor(
cx: AppContext,
doc: ReadSignal<Document>,
editor: RwSignal<EditorData>,
cursor: ReadSignal<Cursor>,
viewport: ReadSignal<Rect>,
is_active: impl Fn() -> bool + 'static,
@ -394,7 +388,7 @@ fn editor_cursor(
let max_line = (viewport.y1 / line_height).ceil() as usize;
let is_active = is_active();
let doc = editor.get().doc;
doc.with(|doc| {
cursor.with(|cursor| match &cursor.mode {
CursorMode::Normal(offset) => {
@ -488,11 +482,10 @@ fn editor_cursor(
fn editor(
cx: AppContext,
active_editor_tab: ReadSignal<Option<EditorTabId>>,
is_active: impl Fn() -> bool + 'static,
editor: RwSignal<EditorData>,
) -> impl View {
let (
doc,
cursor,
scroll_delta,
scroll_to,
@ -502,7 +495,6 @@ fn editor(
config,
) = editor.with(|editor| {
(
editor.doc.read_only(),
editor.cursor.read_only(),
editor.scroll_delta.read_only(),
editor.scroll_to,
@ -513,12 +505,6 @@ fn editor(
)
});
let is_active = move || {
let active_editor_tab = active_editor_tab.get();
let editor_tab = editor.with(|editor| editor.editor_tab_id);
editor_tab.is_some() && editor_tab == active_editor_tab
};
let key_fn = |line: &DocLine| (line.rev, line.style_rev, line.line);
let view_fn = move |cx, line: DocLine| {
container(cx, |cx| {
@ -576,7 +562,7 @@ fn editor(
(
editor_cursor(
cx,
doc,
editor,
cursor,
viewport.read_only(),
is_active,
@ -588,7 +574,7 @@ fn editor(
virtual_list(
cx,
VirtualListDirection::Vertical,
move || doc.get(),
move || editor.get().doc.get(),
key_fn,
view_fn,
VirtualListItemSize::Fixed(line_height as f64),
@ -624,6 +610,8 @@ fn editor(
.on_ensure_visible(cx, move || {
let cursor = cursor.get();
let offset = cursor.offset();
let editor = editor.get();
let doc = editor.doc;
let caret = doc.with_untracked(|doc| {
cursor_caret(doc, offset, !cursor.is_insert())
});
@ -986,8 +974,14 @@ fn editor_tab_content(
let editor_data =
editors.with(|editors| editors.get(&editor_id).cloned());
if let Some(editor_data) = editor_data {
let is_active = move || {
let active_editor_tab = active_editor_tab.get();
let editor_tab =
editor_data.with(|editor| editor.editor_tab_id);
editor_tab.is_some() && editor_tab == active_editor_tab
};
container_box(cx, |cx| {
Box::new(editor(cx, active_editor_tab, editor_data))
Box::new(editor(cx, is_active, editor_data))
})
} else {
container_box(cx, |cx| {
@ -1462,8 +1456,8 @@ fn palette_item(
}
fn palette_input(cx: AppContext, window_tab_data: Arc<WindowTabData>) -> impl View {
let doc = window_tab_data.palette.editor.doc.read_only();
let cursor = window_tab_data.palette.editor.cursor.read_only();
let doc = window_tab_data.palette.input_editor.doc.read_only();
let cursor = window_tab_data.palette.input_editor.cursor.read_only();
let config = window_tab_data.palette.config;
let cursor_x = create_memo(cx.scope, move |_| {
let offset = cursor.get().offset();
@ -1558,6 +1552,7 @@ fn slice(&mut self, range: Range<usize>) -> Self::ItemIterator {
fn palette_content(
cx: AppContext,
window_tab_data: Arc<WindowTabData>,
layout_rect: ReadSignal<Rect>,
) -> impl View {
let items = window_tab_data.palette.filtered_items;
let index = window_tab_data.palette.index.read_only();
@ -1613,36 +1608,68 @@ fn palette_content(
)
})
.style(cx, move || Style {
// display: if items.with(|items| items.is_empty()) {
// Display::None
// } else {
// Display::Flex
// },
flex_direction: FlexDirection::Column,
width: Dimension::Percent(1.0),
min_height: Dimension::Points(0.0),
max_height: Dimension::Points(
(layout_rect.get().height() * 0.45 - 36.0).round() as f32,
),
padding_bottom: 5.0,
..Default::default()
})
}
fn palette_preview(cx: AppContext, palette_data: PaletteData) -> impl View {
let preview_editor = palette_data.preview_editor;
let has_preview = palette_data.has_preview;
container(cx, |cx| {
editor(cx, || true, preview_editor).style(cx, move || Style {
position: Position::Absolute,
width: Dimension::Percent(1.0),
height: Dimension::Percent(1.0),
..Default::default()
})
})
.style(cx, move || Style {
display: if has_preview.get() {
Display::Flex
} else {
Display::None
},
flex_grow: 1.0,
..Default::default()
})
}
fn palette(cx: AppContext, window_tab_data: Arc<WindowTabData>) -> impl View {
let keypress = window_tab_data.keypress.write_only();
let layout_rect = window_tab_data.layout_rect.read_only();
let palette_data = window_tab_data.palette.clone();
let status = palette_data.status.read_only();
let config = palette_data.config;
let has_preview = palette_data.has_preview.read_only();
container(cx, |cx| {
stack(cx, |cx| {
(
palette_input(cx, window_tab_data.clone()),
palette_content(cx, window_tab_data.clone()),
palette_content(cx, window_tab_data.clone(), layout_rect),
palette_preview(cx, palette_data),
)
})
.style(cx, move || Style {
width: Dimension::Points(500.0),
max_width: Dimension::Percent(0.9),
min_height: Dimension::Points(0.0),
max_height: Dimension::Percent(0.5),
// min_height: Dimension::Points(0.0),
// max_height: Dimension::Points(layout_rect.get().height() as f32 - 100.0),
max_height: if has_preview.get() {
Dimension::Auto
} else {
Dimension::Percent(1.0)
},
height: if has_preview.get() {
Dimension::Points(layout_rect.get().height() as f32 - 10.0)
} else {
Dimension::Auto
},
margin_top: Some(5.0),
border: 1.0,
border_radius: 6.0,

View File

@ -1016,6 +1016,52 @@ fn update_snippet_offset(&self, delta: &RopeDelta) {
}
}
pub fn go_to_location(
&self,
cx: AppContext,
location: EditorLocation,
new_doc: bool,
) {
if !new_doc {
if let Some(position) = location.position {
let editor = self.clone();
let send = create_ext_action(cx, move |position| {
editor.go_to_position(position, location.scroll_offset);
});
std::thread::spawn(move || {
send(position);
});
}
} else {
let buffer_id = self.doc.with_untracked(|doc| doc.buffer_id);
let set_doc = self.doc.write_only();
let editor = self.clone();
let send = create_ext_action(cx, move |content| {
set_doc.update(move |doc| {
doc.init_content(content);
});
if let Some(position) = location.position {
let editor = editor.clone();
let send = create_ext_action(cx, move |position| {
editor.go_to_position(position, location.scroll_offset);
});
std::thread::spawn(move || {
send(position);
});
}
});
self.proxy
.new_buffer(buffer_id, location.path, move |result| {
if let Ok(ProxyResponse::NewBufferResponse { content }) = result
{
send(Rope::from(content))
}
});
}
}
pub fn go_to_position(
&self,
position: EditorPosition,

View File

@ -205,10 +205,13 @@ pub fn jump_to_location(&self, cx: AppContext, location: EditorLocation) {
self.go_to_location(cx, location);
}
pub fn go_to_location(&self, cx: AppContext, location: EditorLocation) {
let path = location.path;
pub fn get_doc(
&self,
cx: AppContext,
path: PathBuf,
) -> (RwSignal<Document>, bool) {
let doc = self.docs.with_untracked(|docs| docs.get(&path).cloned());
let (doc, new) = if let Some(doc) = doc {
if let Some(doc) = doc {
(doc, false)
} else {
let doc =
@ -217,22 +220,7 @@ pub fn go_to_location(&self, cx: AppContext, location: EditorLocation) {
self.docs.update(|docs| {
docs.insert(path.clone(), doc);
});
(doc, true)
};
let editor = self.get_editor_or_new(cx, doc, &path);
if !new {
if let Some(position) = location.position {
let send = create_ext_action(cx, move |position| {
editor.with_untracked(|editor| {
editor.go_to_position(position, location.scroll_offset);
})
});
std::thread::spawn(move || {
send(position);
});
}
} else {
{
let proxy = self.proxy_rpc.clone();
create_effect(cx.scope, move |last| {
@ -245,33 +233,19 @@ pub fn go_to_location(&self, cx: AppContext, location: EditorLocation) {
});
}
let buffer_id = doc.with_untracked(|doc| doc.buffer_id);
let set_doc = doc.write_only();
let send = create_ext_action(cx, move |content| {
set_doc.update(move |doc| {
doc.init_content(content);
});
if let Some(position) = location.position {
let send = create_ext_action(cx, move |position| {
editor.with_untracked(|editor| {
editor.go_to_position(position, location.scroll_offset);
})
});
std::thread::spawn(move || {
send(position);
});
}
});
self.proxy_rpc.new_buffer(buffer_id, path, move |result| {
if let Ok(ProxyResponse::NewBufferResponse { content }) = result {
send(Rope::from(content))
}
});
(doc, true)
}
}
pub fn go_to_location(&self, cx: AppContext, location: EditorLocation) {
let path = location.path.clone();
let (doc, new_doc) = self.get_doc(cx, path.clone());
let editor = self.get_editor_or_new(cx, doc, &path);
let editor = editor.get_untracked();
editor.go_to_location(cx, location, new_doc);
}
fn get_editor_or_new(
&self,
cx: AppContext,

View File

@ -40,6 +40,7 @@
editor::{location::EditorLocation, EditorData},
id::EditorId,
keypress::{condition::Condition, KeyPressData, KeyPressFocus},
main_split::MainSplitData,
window_tab::Focus,
workspace::{LapceWorkspace, LapceWorkspaceType},
};
@ -88,12 +89,15 @@ pub struct PaletteData {
pub proxy_rpc: ProxyRpcHandler,
pub input: RwSignal<PaletteInput>,
kind: RwSignal<PaletteKind>,
pub editor: EditorData,
pub input_editor: EditorData,
pub preview_editor: RwSignal<EditorData>,
pub has_preview: RwSignal<bool>,
pub focus: RwSignal<Focus>,
pub keypress: ReadSignal<KeyPressData>,
pub config: ReadSignal<Arc<LapceConfig>>,
pub executed_commands: Rc<RefCell<HashMap<String, Instant>>>,
main_split: MainSplitData,
pub references: RwSignal<Vec<EditorLocation>>,
}
@ -101,6 +105,7 @@ impl PaletteData {
pub fn new(
cx: AppContext,
workspace: Arc<LapceWorkspace>,
main_split: MainSplitData,
proxy_rpc: ProxyRpcHandler,
register: RwSignal<Register>,
completion: RwSignal<CompletionData>,
@ -123,7 +128,7 @@ pub fn new(
},
);
let kind = create_rw_signal(cx.scope, PaletteKind::File);
let editor = EditorData::new_local(
let input_editor = EditorData::new_local(
cx,
EditorId::next(),
register,
@ -132,6 +137,17 @@ pub fn new(
proxy_rpc.clone(),
config,
);
let preview_editor = EditorData::new_local(
cx,
EditorId::next(),
register,
completion,
internal_command,
proxy_rpc.clone(),
config,
);
let preview_editor = create_rw_signal(cx.scope, preview_editor);
let has_preview = create_rw_signal(cx.scope, false);
let run_id = create_rw_signal(cx.scope, 0);
let run_id_counter = Arc::new(AtomicU64::new(0));
@ -198,6 +214,7 @@ pub fn new(
let palette = Self {
run_id_counter,
run_tx,
main_split,
window_command,
internal_command,
lapce_command,
@ -208,7 +225,9 @@ pub fn new(
index,
items,
filtered_items,
editor,
input_editor,
preview_editor,
has_preview,
input,
kind,
proxy_rpc,
@ -220,7 +239,7 @@ pub fn new(
{
let palette = palette.clone();
let doc = palette.editor.doc.read_only();
let doc = palette.input_editor.doc.read_only();
let input = palette.input.write_only();
let status = palette.status.read_only();
let preset_kind = palette.kind.read_only();
@ -267,6 +286,14 @@ pub fn new(
});
}
{
let palette = palette.clone();
create_effect(cx.scope, move |_| {
let _ = palette.index.get();
palette.preview(cx);
});
}
palette
}
@ -275,15 +302,16 @@ pub fn run(&self, cx: AppContext, kind: PaletteKind) {
self.status.set(PaletteStatus::Started);
let symbol = kind.symbol();
self.kind.set(kind);
self.editor
self.input_editor
.doc
.update(|doc| doc.reload(Rope::from(symbol), true));
self.editor
self.input_editor
.cursor
.update(|cursor| cursor.set_insert(Selection::caret(symbol.len())));
}
fn run_inner(&self, cx: AppContext, kind: PaletteKind) {
self.has_preview.set(false);
let run_id = self.run_id_counter.fetch_add(1, Ordering::Relaxed) + 1;
self.run_id.set(run_id);
match kind {
@ -477,14 +505,36 @@ fn select(&self, cx: AppContext) {
self.cancel(cx);
}
fn preview(&self, cx: AppContext) {
let index = self.index.get_untracked();
let items = self.filtered_items.get_untracked();
if let Some(item) = items.get(index) {
match &item.content {
PaletteItemContent::File { path, full_path } => {}
PaletteItemContent::Command { cmd } => {}
PaletteItemContent::Workspace { workspace } => {}
PaletteItemContent::Reference { path, location } => {
self.has_preview.set(true);
let (doc, new_doc) =
self.main_split.get_doc(cx, location.path.clone());
self.preview_editor.update(|preview_editor| {
preview_editor.doc = doc;
preview_editor.go_to_location(cx, location.clone(), new_doc);
});
}
}
}
}
fn cancel(&self, cx: AppContext) {
self.status.set(PaletteStatus::Inactive);
self.focus.set(Focus::Workbench);
self.has_preview.set(false);
self.items.update(|items| items.clear());
self.editor
self.input_editor
.doc
.update(|doc| doc.reload(Rope::from(""), true));
self.editor
self.input_editor
.cursor
.update(|cursor| cursor.set_insert(Selection::caret(0)));
}
@ -645,10 +695,10 @@ fn run_command(
match &command.kind {
CommandKind::Workbench(_) => todo!(),
CommandKind::Edit(_) => {
self.editor.run_command(cx, command, count, mods)
self.input_editor.run_command(cx, command, count, mods)
}
CommandKind::Move(_) => {
self.editor.run_command(cx, command, count, mods)
self.input_editor.run_command(cx, command, count, mods)
}
CommandKind::Focus(cmd) => self.run_focus_command(cx, cmd),
CommandKind::MotionMode(_) => todo!(),
@ -657,6 +707,6 @@ fn run_command(
}
fn receive_char(&self, cx: AppContext, c: &str) {
self.editor.receive_char(cx, c);
self.input_editor.receive_char(cx, c);
}
}

View File

@ -87,9 +87,19 @@ pub fn new(
let register = create_rw_signal(cx.scope, Register::default());
let main_split = MainSplitData::new(
cx,
proxy.rpc.clone(),
register,
completion,
set_internal_command,
config,
);
let palette = PaletteData::new(
cx,
workspace.clone(),
main_split.clone(),
proxy.rpc.clone(),
register,
completion,
@ -101,15 +111,6 @@ pub fn new(
config,
);
let main_split = MainSplitData::new(
cx,
proxy.rpc.clone(),
register,
completion,
set_internal_command,
config,
);
let window_tab_data = Self {
window_tab_id: WindowTabId::next(),
set_window_command,