improve file explorer watch

This commit is contained in:
Dongdong Zhou 2022-05-09 14:26:01 +01:00
parent 510274f3a9
commit 6380a4dfa4
10 changed files with 214 additions and 370 deletions

View File

@ -383,7 +383,7 @@ pub enum LapceUICommand {
FilterKeymaps(String, Arc<Vec<KeyMap>>, Arc<Vec<LapceCommand>>),
UpdatePickerPwd(PathBuf),
UpdatePickerItems(PathBuf, HashMap<PathBuf, FileNodeItem>),
UpdateExplorerItems(usize, PathBuf, Vec<FileNodeItem>),
UpdateExplorerItems(PathBuf, HashMap<PathBuf, FileNodeItem>, bool),
UpdateInstalledPlugins(HashMap<String, PluginDescription>),
UpdatePluginDescriptions(Vec<PluginDescription>),
RequestLayout,

View File

@ -25,8 +25,7 @@
selection::Selection,
};
use lapce_rpc::{
file::FileNodeItem, plugin::PluginDescription, source_control::FileDiff,
terminal::TermId,
plugin::PluginDescription, source_control::FileDiff, terminal::TermId,
};
use lsp_types::{
CodeActionOrCommand, Diagnostic, Position, ProgressToken, TextEdit,
@ -1039,37 +1038,12 @@ pub fn run_workbench_command(
if let Some(node) = picker.root.get_file_node(&picker.pwd) {
if !node.read {
let tab_id = self.id;
let path = node.path_buf.clone();
let event_sink = ctx.get_external_handle();
self.proxy.read_dir(
FilePickerData::read_dir(
&node.path_buf,
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<
Vec<FileNodeItem>,
serde_json::Error,
> = serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdatePickerItems(
path,
items
.iter()
.map(|item| {
(
item.path_buf
.clone(),
item.clone(),
)
})
.collect(),
),
Target::Widget(tab_id),
);
}
}
}),
tab_id,
&self.proxy,
event_sink,
);
}
}
@ -1110,37 +1084,12 @@ pub fn run_workbench_command(
if let Some(node) = picker.root.get_file_node(&picker.pwd) {
if !node.read {
let tab_id = self.id;
let path = node.path_buf.clone();
let event_sink = ctx.get_external_handle();
self.proxy.read_dir(
FilePickerData::read_dir(
&node.path_buf,
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<
Vec<FileNodeItem>,
serde_json::Error,
> = serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdatePickerItems(
path,
items
.iter()
.map(|item| {
(
item.path_buf
.clone(),
item.clone(),
)
})
.collect(),
),
Target::Widget(tab_id),
);
}
}
}),
tab_id,
&self.proxy,
event_sink,
);
}
}
@ -1668,30 +1617,7 @@ pub fn read_picker_pwd(&mut self, ctx: &mut EventCtx) {
let path = self.picker.pwd.clone();
let event_sink = ctx.get_external_handle();
let tab_id = self.id;
self.proxy.read_dir(
&path.clone(),
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<Vec<FileNodeItem>, serde_json::Error> =
serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdatePickerItems(
path,
items
.iter()
.map(|item| {
(item.path_buf.clone(), item.clone())
})
.collect(),
),
Target::Widget(tab_id),
);
}
}
}),
);
FilePickerData::read_dir(&path, tab_id, &self.proxy, event_sink);
}
pub fn set_picker_pwd(&mut self, pwd: PathBuf) {
@ -1729,27 +1655,39 @@ pub fn set_picker_pwd(&mut self, pwd: PathBuf) {
}
}
pub fn handle_file_change(&mut self, event: &notify::Event) {
if let Some(workspace) =
Arc::make_mut(&mut self.file_explorer).workspace.as_mut()
{
pub fn handle_file_change(&mut self, ctx: &mut EventCtx, event: &notify::Event) {
if self.file_explorer.workspace.is_some() {
match &event.kind {
notify::EventKind::Create(creat_kind) => {
notify::EventKind::Create(_)
| notify::EventKind::Modify(notify::event::ModifyKind::Name(_))
| notify::EventKind::Remove(_) => {
for path in event.paths.iter() {
workspace.add_child(
path,
creat_kind == &notify::event::CreateKind::Folder,
);
}
}
notify::EventKind::Remove(_) => {
for path in event.paths.iter() {
workspace.remove_child(path);
if let Some(path) = path.parent() {
FileExplorerData::read_dir(
path,
false,
self.id,
&self.proxy,
ctx.get_external_handle(),
);
}
}
}
_ => {}
}
}
// let doc = self
// .main_split
// .local_docs
// .get_mut(&LocalBufferKind::Search)
// .unwrap();
// let pattern = doc.buffer().text().to_string();
// ctx.submit_command(Command::new(
// LAPCE_UI_COMMAND,
// LapceUICommand::UpdateSearch(pattern),
// Target::Widget(self.id),
// ));
}
}

View File

@ -8,6 +8,7 @@
use include_dir::{include_dir, Dir};
use lapce_rpc::file::FileNodeItem;
use lapce_rpc::proxy::ReadDirResponse;
use crate::data::LapceWorkspace;
use crate::proxy::LapceProxy;
@ -46,29 +47,9 @@ pub fn new(
children: HashMap::new(),
children_open_count: 0,
});
let index = 0;
let path = path.clone();
std::thread::spawn(move || {
proxy.read_dir(
&path.clone(),
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<Vec<FileNodeItem>, serde_json::Error> =
serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdateExplorerItems(
index,
path.clone(),
items,
),
Target::Widget(tab_id),
);
}
}
}),
);
Self::read_dir(&path, true, tab_id, &proxy, event_sink);
});
}
Self {
@ -133,6 +114,72 @@ pub fn get_node_mut(&mut self, path: &Path) -> Option<&mut FileNodeItem> {
}
Some(node)
}
pub fn update_children(
&mut self,
path: &Path,
children: HashMap<PathBuf, FileNodeItem>,
expand: bool,
) -> Option<()> {
let node = self.workspace.as_mut()?.get_file_node_mut(path)?;
let removed_paths: Vec<PathBuf> = node
.children
.keys()
.filter(|p| !children.contains_key(*p))
.map(PathBuf::from)
.collect();
for path in removed_paths {
node.children.remove(&path);
}
for (path, child) in children.into_iter() {
if !node.children.contains_key(&path) {
node.children.insert(child.path_buf.clone(), child);
}
}
node.read = true;
if expand {
node.open = true;
}
for p in path.ancestors() {
self.update_node_count(p);
}
Some(())
}
pub fn read_dir(
path: &Path,
expand: bool,
tab_id: WidgetId,
proxy: &LapceProxy,
event_sink: ExtEventSink,
) {
let path = PathBuf::from(path);
let local_path = path.clone();
proxy.read_dir(
&local_path,
Box::new(move |result| {
if let Ok(res) = result {
let path = path.clone();
let resp: Result<ReadDirResponse, serde_json::Error> =
serde_json::from_value(res);
if let Ok(resp) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdateExplorerItems(
path, resp.items, expand,
),
Target::Widget(tab_id),
);
}
}
}),
);
}
}
pub fn get_item_children(

View File

@ -3,8 +3,13 @@
path::{Path, PathBuf},
};
use druid::WidgetId;
use lapce_rpc::file::FileNodeItem;
use druid::{ExtEventSink, Target, WidgetId};
use lapce_rpc::{file::FileNodeItem, proxy::ReadDirResponse};
use crate::{
command::{LapceUICommand, LAPCE_UI_COMMAND},
proxy::LapceProxy,
};
#[derive(Clone)]
pub struct FilePickerData {
@ -73,6 +78,33 @@ pub fn init_home(&mut self, home: &Path) {
self.root = current_file_node;
self.pwd = home.to_path_buf();
}
pub fn read_dir(
path: &Path,
tab_id: WidgetId,
proxy: &LapceProxy,
event_sink: ExtEventSink,
) {
let path = PathBuf::from(path);
let local_path = path.clone();
proxy.read_dir(
&local_path,
Box::new(move |result| {
if let Ok(res) = result {
let path = path.clone();
let resp: Result<ReadDirResponse, serde_json::Error> =
serde_json::from_value(res);
if let Ok(resp) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdatePickerItems(path, resp.items),
Target::Widget(tab_id),
);
}
}
}),
);
}
}
impl Default for FilePickerData {

View File

@ -15,7 +15,7 @@
use lapce_rpc::buffer::{BufferHeadResponse, BufferId, NewBufferResponse};
use lapce_rpc::core::CoreNotification;
use lapce_rpc::file::FileNodeItem;
use lapce_rpc::proxy::{ProxyNotification, ProxyRequest};
use lapce_rpc::proxy::{ProxyNotification, ProxyRequest, ReadDirResponse};
use lapce_rpc::source_control::{DiffInfo, FileDiff};
use lapce_rpc::terminal::TermId;
use lapce_rpc::{self, Call, RequestId, RpcObject};
@ -95,12 +95,9 @@ fn handle_event(&mut self, event: notify::Result<notify::Event>) {
notify::EventKind::Create(_)
| notify::EventKind::Modify(_)
| notify::EventKind::Remove(_) => {
let notification =
serde_json::to_value(&CoreNotification::FileChange {
event,
})
.unwrap();
self.send_rpc_notification(notification);
self.send_rpc_notification(CoreNotification::FileChange {
event,
});
if let Some(workspace) = self.workspace.lock().clone() {
if let Some(diff) = git_diff_new(&workspace) {
@ -271,8 +268,28 @@ pub fn respond(&self, id: RequestId, result: Result<Value>) {
let _ = self.sender.send(resp);
}
pub fn send_rpc_notification(&self, notification: Value) {
let _ = self.sender.send(notification);
pub fn respond_rpc<T: serde::Serialize>(
&self,
id: RequestId,
result: Result<T>,
) {
let mut resp = json!({ "id": id });
match result {
Ok(v) => resp["result"] = serde_json::to_value(v).unwrap(),
Err(e) => {
resp["error"] = json!({
"code": 0,
"message": format!("{}",e),
})
}
}
let _ = self.sender.send(resp);
}
pub fn send_rpc_notification<T: serde::Serialize>(&self, notification: T) {
let _ = self
.sender
.send(serde_json::to_value(notification).unwrap());
}
pub fn send_notification(&self, method: &str, params: Value) {
@ -510,21 +527,27 @@ fn handle_request(&self, id: RequestId, rpc: ProxyRequest) {
.into_iter()
.filter_map(|entry| {
entry
.map(|e| FileNodeItem {
path_buf: e.path(),
is_dir: e.path().is_dir(),
open: false,
read: false,
children: HashMap::new(),
children_open_count: 0,
.map(|e| {
(
e.path(),
FileNodeItem {
path_buf: e.path(),
is_dir: e.path().is_dir(),
open: false,
read: false,
children: HashMap::new(),
children_open_count: 0,
},
)
})
.ok()
})
.collect::<Vec<FileNodeItem>>();
serde_json::to_value(items).unwrap()
.collect::<HashMap<PathBuf, FileNodeItem>>();
ReadDirResponse { items }
})
.map_err(|e| anyhow!(e));
local_dispatcher.respond(id, result);
local_dispatcher.respond_rpc(id, result);
});
}
GetFiles { .. } => {

View File

@ -110,15 +110,15 @@ pub fn get_file_node_mut(&mut self, path: &Path) -> Option<&mut FileNodeItem> {
node
}
pub fn remove_child(&mut self, path: &Path) -> Option<()> {
pub fn remove_child(&mut self, path: &Path) -> Option<FileNodeItem> {
let parent = path.parent()?;
let node = self.get_file_node_mut(parent)?;
node.children.remove(path)?;
let node = node.children.remove(path)?;
for p in path.ancestors() {
self.update_node_count(p);
}
Some(())
Some(node)
}
pub fn add_child(&mut self, path: &Path, is_dir: bool) -> Option<()> {

View File

@ -1,12 +1,12 @@
use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};
use lsp_types::{CompletionItem, Position};
use serde::{Deserialize, Serialize};
use xi_rope::RopeDelta;
use crate::{
buffer::BufferId, plugin::PluginDescription, source_control::FileDiff,
terminal::TermId,
buffer::BufferId, file::FileNodeItem, plugin::PluginDescription,
source_control::FileDiff, terminal::TermId,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -111,3 +111,8 @@ pub enum ProxyRequest {
buffer_id: BufferId,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadDirResponse {
pub items: HashMap<PathBuf, FileNodeItem>,
}

View File

@ -411,27 +411,13 @@ fn event(
node.open = !node.open;
} else {
let tab_id = data.id;
let path = node.path_buf.clone();
let event_sink = ctx.get_external_handle();
data.proxy.read_dir(
FileExplorerData::read_dir(
&node.path_buf,
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<
Vec<FileNodeItem>,
serde_json::Error,
> = serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdateExplorerItems(
index, path, items,
),
Target::Widget(tab_id),
);
}
}
}),
true,
tab_id,
&data.proxy,
event_sink,
);
}
let path = node.path_buf.clone();

View File

@ -1,8 +1,4 @@
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
};
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use druid::{
kurbo::Line,
@ -15,6 +11,7 @@
command::{LapceUICommand, LAPCE_UI_COMMAND},
config::{Config, LapceTheme},
data::LapceTabData,
picker::FilePickerData,
};
use lapce_rpc::file::FileNodeItem;
@ -26,131 +23,6 @@
tab::LapceButton,
};
#[derive(Clone)]
pub struct FilePickerData {
pub widget_id: WidgetId,
pub editor_view_id: WidgetId,
pub active: bool,
root: FileNodeItem,
pub home: PathBuf,
pub pwd: PathBuf,
}
impl FilePickerData {
pub fn new() -> Self {
let root = FileNodeItem {
path_buf: PathBuf::from("/"),
is_dir: true,
read: false,
open: false,
children: HashMap::new(),
children_open_count: 0,
};
let home = PathBuf::from("/");
let pwd = PathBuf::from("/");
Self {
widget_id: WidgetId::next(),
editor_view_id: WidgetId::next(),
active: false,
root,
home,
pwd,
}
}
pub fn set_item_children(
&mut self,
path: &Path,
children: HashMap<PathBuf, FileNodeItem>,
) {
if let Some(node) = self.get_file_node_mut(path) {
node.open = true;
node.read = true;
node.children = children;
}
for p in path.ancestors() {
self.update_node_count(&PathBuf::from(p));
}
}
pub fn init_home(&mut self, home: &Path) {
self.home = home.to_path_buf();
let mut current_file_node = FileNodeItem {
path_buf: home.to_path_buf(),
is_dir: true,
read: false,
open: false,
children: HashMap::new(),
children_open_count: 0,
};
let mut current_path = home.to_path_buf();
let mut ancestors = home.ancestors();
ancestors.next();
for p in ancestors {
let mut file_node = FileNodeItem {
path_buf: PathBuf::from(p),
is_dir: true,
read: false,
open: true,
children: HashMap::new(),
children_open_count: 0,
};
file_node
.children
.insert(current_path.clone(), current_file_node.clone());
current_file_node = file_node;
current_path = PathBuf::from(p);
}
self.root = current_file_node;
self.pwd = home.to_path_buf();
}
pub fn get_file_node_mut(&mut self, path: &Path) -> Option<&mut FileNodeItem> {
let mut node = Some(&mut self.root);
let ancestors = path.ancestors().collect::<Vec<&Path>>();
for p in ancestors[..ancestors.len() - 1].iter().rev() {
node = Some(node?.children.get_mut(&PathBuf::from(p))?);
}
node
}
pub fn get_file_node(&self, path: &Path) -> Option<&FileNodeItem> {
let mut node = Some(&self.root);
let ancestors = path.ancestors().collect::<Vec<&Path>>();
for p in ancestors[..ancestors.len() - 1].iter().rev() {
node = Some(node?.children.get(&PathBuf::from(p))?);
}
node
}
pub fn update_node_count(&mut self, path: &Path) -> Option<()> {
let node = self.get_file_node_mut(path)?;
if node.is_dir {
if node.open {
node.children_open_count = node
.children
.iter()
.map(|(_, item)| item.children_open_count + 1)
.sum::<usize>();
} else {
node.children_open_count = 0;
}
}
None
}
}
impl Default for FilePickerData {
fn default() -> Self {
Self::new()
}
}
pub struct FilePicker {
widget_id: WidgetId,
pwd: WidgetPod<LapceTabData, Box<dyn Widget<LapceTabData>>>,
@ -476,37 +348,12 @@ fn mouse_down(
node.open = !node.open;
} else {
let tab_id = data.id;
let path = node.path_buf.clone();
let event_sink = ctx.get_external_handle();
data.proxy.read_dir(
FilePickerData::read_dir(
&node.path_buf,
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<
Vec<FileNodeItem>,
serde_json::Error,
> = serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdatePickerItems(
path,
items
.iter()
.map(|item| {
(
item.path_buf
.clone(),
item.clone(),
)
})
.collect(),
),
Target::Widget(tab_id),
);
}
}
}),
tab_id,
&data.proxy,
event_sink,
);
}
}
@ -519,37 +366,12 @@ fn mouse_down(
// double click
self.last_left_click = None;
let tab_id = data.id;
let path = node.path_buf.clone();
let event_sink = ctx.get_external_handle();
data.proxy.read_dir(
FilePickerData::read_dir(
&node.path_buf,
Box::new(move |result| {
if let Ok(res) = result {
let resp: Result<
Vec<FileNodeItem>,
serde_json::Error,
> = serde_json::from_value(res);
if let Ok(items) = resp {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdatePickerItems(
path,
items
.iter()
.map(|item| {
(
item.path_buf
.clone(),
item.clone(),
)
})
.collect(),
),
Target::Widget(tab_id),
);
}
}
}),
tab_id,
&data.proxy,
event_sink,
);
let pwd = node.path_buf.clone();
picker.index = 0;

View File

@ -457,7 +457,7 @@ fn event(
ctx.set_handled();
}
LapceUICommand::FileChange(event) => {
data.handle_file_change(event);
data.handle_file_change(ctx, event);
ctx.set_handled();
}
LapceUICommand::CloseTerminal(id) => {
@ -957,22 +957,13 @@ fn event(
.set_item_children(path, items.clone());
ctx.set_handled();
}
LapceUICommand::UpdateExplorerItems(_index, path, items) => {
LapceUICommand::UpdateExplorerItems(path, items, expand) => {
let file_explorer = Arc::make_mut(&mut data.file_explorer);
if let Some(node) = file_explorer.get_node_mut(path) {
node.children = items
.iter()
.map(|item| (item.path_buf.clone(), item.clone()))
.collect();
node.read = true;
node.open = true;
node.children_open_count = node.children.len();
}
if let Some(paths) = file_explorer.node_tree(path) {
for path in paths.iter() {
file_explorer.update_node_count(path);
}
}
file_explorer.update_children(
path,
items.to_owned(),
*expand,
);
ctx.set_handled();
}
_ => (),