diff --git a/.gitignore b/.gitignore index d55c126cd3..c13bdd143c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode .lapce +.idea lib/ target/ diff --git a/core/src/explorer.rs b/core/src/explorer.rs index 9e9ee6d82a..ef2b956275 100644 --- a/core/src/explorer.rs +++ b/core/src/explorer.rs @@ -2,10 +2,15 @@ use std::{cmp, path::PathBuf}; use std::{str::FromStr, sync::Arc}; use druid::{ - piet::PietTextLayout, widget::SvgData, Affine, Command, Env, Event, EventCtx, - PaintCtx, Point, Rect, RenderContext, Size, Target, TextLayout, Vec2, Widget, - WidgetId, WindowId, + piet::{Text, TextLayout as PietTextLayout, TextLayoutBuilder}, + theme, + widget::{CrossAxisAlignment, Flex, FlexParams, Label, Scroll, SvgData}, + Affine, BoxConstraints, Color, Command, Cursor, Data, Env, Event, EventCtx, + FontFamily, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Point, Rect, + RenderContext, Size, Target, TextLayout, UpdateCtx, Vec2, Widget, WidgetExt, + WidgetId, WidgetPod, WindowId, }; + use include_dir::{include_dir, Dir}; use lapce_proxy::dispatch::FileNodeItem; use parking_lot::Mutex; @@ -25,6 +30,7 @@ pub struct FileExplorerState { // pub widget_id: WidgetId, window_id: WindowId, tab_id: WidgetId, + pub widget_id: WidgetId, // cwd: PathBuf, pub items: Vec, index: usize, @@ -32,7 +38,91 @@ pub struct FileExplorerState { position: PanelPosition, } +pub struct FileExplorer { + window_id: WindowId, + tab_id: WidgetId, + widget_id: WidgetId, +} + +impl FileExplorer { + pub fn new(window_id: WindowId, tab_id: WidgetId, widget_id: WidgetId) -> Self { + Self { + window_id, + tab_id, + widget_id, + } + } +} + +impl Widget for FileExplorer { + fn id(&self) -> Option { + Some(self.widget_id) + } + + fn event( + &mut self, + ctx: &mut EventCtx, + event: &Event, + data: &mut LapceUIState, + env: &Env, + ) { + match event { + Event::Command(cmd) => match cmd { + _ if cmd.is(LAPCE_UI_COMMAND) => { + let command = cmd.get_unchecked(LAPCE_UI_COMMAND); + match command { + LapceUICommand::RequestPaint => { + ctx.request_paint(); + } + _ => (), + } + } + _ => (), + }, + _ => (), + } + } + + fn lifecycle( + &mut self, + ctx: &mut LifeCycleCtx, + event: &LifeCycle, + data: &LapceUIState, + env: &Env, + ) { + } + + fn update( + &mut self, + ctx: &mut UpdateCtx, + old_data: &LapceUIState, + data: &LapceUIState, + env: &Env, + ) { + } + + fn layout( + &mut self, + ctx: &mut LayoutCtx, + bc: &BoxConstraints, + data: &LapceUIState, + env: &Env, + ) -> Size { + bc.max() + } + + fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) { + let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); + let explorer = state.file_explorer.lock(); + explorer.paint(ctx, data, env); + } +} + impl PanelProperty for FileExplorerState { + fn widget_id(&self) -> WidgetId { + self.widget_id + } + fn position(&self) -> &PanelPosition { &self.position } @@ -46,17 +136,35 @@ impl PanelProperty for FileExplorerState { } fn paint(&self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) { + let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT); + + let size = ctx.size(); + let header_height = line_height; + let header_rect = Rect::ZERO.with_size(Size::new(size.width, header_height)); + if let Some(background) = LAPCE_APP_STATE.theme.get("background") { + ctx.fill(header_rect, background); + } + ctx.fill( + Size::new(size.width, size.height - header_height) + .to_rect() + .with_origin(Point::new(0.0, header_height)), + &env.get(LapceTheme::EDITOR_CURRENT_LINE_BACKGROUND), + ); + + let text_layout = ctx + .text() + .new_text_layout("Explorer") + .font(FontFamily::SYSTEM_UI, 14.0) + .text_color(env.get(LapceTheme::EDITOR_FOREGROUND)); + let text_layout = text_layout.build().unwrap(); + ctx.draw_text(&text_layout, Point::new(20.0, 5.0)); + let rects = ctx.region().rects().to_vec(); let size = ctx.size(); - let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); - let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT); let width = size.width; let index = self.index; for rect in rects { - if let Some(background) = LAPCE_APP_STATE.theme.get("background") { - ctx.fill(rect, background); - } let min = (rect.y0 / line_height).floor() as usize; let max = (rect.y1 / line_height) as usize + 1; let mut i = 0; @@ -89,6 +197,7 @@ impl FileExplorerState { FileExplorerState { window_id, tab_id, + widget_id: WidgetId::next(), items, index: 0, count: 0, @@ -201,28 +310,31 @@ impl FileExplorerState { state.clone().proxy.lock().as_ref().unwrap().read_dir( &path_buf, Box::new(move |result| { - let mut file_explorer = state.file_explorer.lock(); - let current_item = file_explorer.get_item(index); - if current_item != Some(&mut item) { - return; - } - let current_item = current_item.unwrap(); - current_item.open = true; - current_item.read = true; - if let Ok(res) = result { - let resp: Result< - Vec, - serde_json::Error, - > = serde_json::from_value(res); - if let Ok(items) = resp { - current_item.children = items; + std::thread::spawn(move || { + let mut file_explorer = + state.file_explorer.lock(); + let current_item = file_explorer.get_item(index); + if current_item != Some(&mut item) { + return; + } + let current_item = current_item.unwrap(); + current_item.open = true; + current_item.read = true; + if let Ok(res) = result { + let resp: Result< + Vec, + serde_json::Error, + > = serde_json::from_value(res); + if let Ok(items) = resp { + current_item.children = items; + } } - } - file_explorer.update_count(); - LAPCE_APP_STATE.submit_ui_command( - LapceUICommand::RequestPaint, - file_explorer.widget_id(), - ); + file_explorer.update_count(); + LAPCE_APP_STATE.submit_ui_command( + LapceUICommand::RequestPaint, + file_explorer.widget_id(), + ); + }); }), ); } @@ -237,7 +349,7 @@ impl FileExplorerState { ctx.submit_command(Command::new( LAPCE_UI_COMMAND, LapceUICommand::RequestPaint, - Target::Widget(self.widget_id()), + Target::Widget(self.widget_id), )); } @@ -259,14 +371,19 @@ impl FileExplorerState { } if i >= min && i <= max { if i == index { - ctx.fill( - Rect::ZERO - .with_origin(Point::new(0.0, i as f64 * line_height)) - .with_size(Size::new(width, line_height)), - &env.get(LapceTheme::EDITOR_CURRENT_LINE_BACKGROUND), - ); + if let Some(color) = LAPCE_APP_STATE.theme.get("selection") { + ctx.fill( + Rect::ZERO + .with_origin(Point::new( + 0.0, + i as f64 * line_height + line_height, + )) + .with_size(Size::new(width, line_height)), + color, + ); + } } - let y = i as f64 * line_height; + let y = i as f64 * line_height + line_height; let svg_y = y + 4.0; let mut text_layout = TextLayout::::from_text( item.path_buf.file_name().unwrap().to_str().unwrap(), @@ -362,12 +479,6 @@ impl FileExplorerState { } i } - - pub fn widget_id(&self) -> WidgetId { - let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); - let panel = state.panel.lock(); - panel.widget_id(&self.position) - } } fn get_item_count(item: &FileNodeItem) -> usize { diff --git a/core/src/outline.rs b/core/src/outline.rs index c4ba6cd9f6..c4c8108b3a 100644 --- a/core/src/outline.rs +++ b/core/src/outline.rs @@ -1,13 +1,19 @@ -use druid::{Env, PaintCtx}; +use druid::{Env, PaintCtx, WidgetId}; use crate::{ panel::{PanelPosition, PanelProperty}, state::LapceUIState, }; -pub struct OutlineState {} +pub struct OutlineState { + widget_id: WidgetId, +} impl PanelProperty for OutlineState { + fn widget_id(&self) -> WidgetId { + self.widget_id + } + fn position(&self) -> &PanelPosition { &PanelPosition::RightTop } @@ -25,6 +31,8 @@ impl PanelProperty for OutlineState { impl OutlineState { pub fn new() -> Self { - Self {} + Self { + widget_id: WidgetId::next(), + } } } diff --git a/core/src/palette.rs b/core/src/palette.rs index 659c12221c..3e93f6773b 100644 --- a/core/src/palette.rs +++ b/core/src/palette.rs @@ -13,13 +13,11 @@ use druid::{ }; use druid::{ piet::{Text, TextLayout as PietTextLayout, TextLayoutBuilder}, - theme, BoxConstraints, Color, Cursor, Data, Env, Event, EventCtx, LayoutCtx, - LifeCycle, LifeCycleCtx, PaintCtx, Point, RenderContext, Size, UpdateCtx, - Widget, WidgetExt, WidgetPod, -}; -use druid::{ + theme, widget::{CrossAxisAlignment, Flex, FlexParams, Label, Scroll}, - TextLayout, + BoxConstraints, Color, Cursor, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, + LifeCycleCtx, PaintCtx, Point, RenderContext, Size, TextLayout, UpdateCtx, + Widget, WidgetExt, WidgetPod, }; use fzyr::{has_match, locate, Score}; use lsp_types::{DocumentSymbolResponse, Location, Position, SymbolKind}; diff --git a/core/src/panel.rs b/core/src/panel.rs index eb7c722dc3..ca87b50c62 100644 --- a/core/src/panel.rs +++ b/core/src/panel.rs @@ -26,6 +26,7 @@ pub enum PanelPosition { } pub trait PanelProperty: Send { + fn widget_id(&self) -> WidgetId; fn position(&self) -> &PanelPosition; fn active(&self) -> usize; fn size(&self) -> (f64, f64); @@ -35,14 +36,14 @@ pub trait PanelProperty: Send { pub struct PanelState { window_id: WindowId, tab_id: WidgetId, - pub panels: Vec>>, + pub panels: HashMap>>, pub shown: HashMap, - widgets: HashMap, + pub widgets: HashMap, } impl PanelState { pub fn new(window_id: WindowId, tab_id: WidgetId) -> Self { - let mut panels = Vec::new(); + let panels = HashMap::new(); // panels.push(Arc::new(Mutex::new(Box::new(FileExplorerState::new( // window_id, tab_id, // )) as Box))); @@ -54,6 +55,7 @@ impl PanelState { // ))); let mut shown = HashMap::new(); shown.insert(PanelPosition::LeftTop, true); + shown.insert(PanelPosition::LeftBottom, true); shown.insert(PanelPosition::BottomLeft, true); shown.insert(PanelPosition::RightTop, true); @@ -77,8 +79,12 @@ impl PanelState { *self.shown.get(position).unwrap_or(&false) } - pub fn add(&mut self, panel: Arc>) { - self.panels.push(panel); + pub fn add( + &mut self, + widget_id: WidgetId, + panel: Arc>, + ) { + self.panels.insert(widget_id, panel); } pub fn widget_id(&self, position: &PanelPosition) -> WidgetId { @@ -88,7 +94,7 @@ impl PanelState { pub fn size(&self, position: &PanelPosition) -> Option<(f64, f64)> { let mut active_panel = None; let mut active = 0; - for panel in self.panels.iter() { + for (_, panel) in self.panels.iter() { let local_panel = panel.clone(); let panel = panel.lock(); if panel.position() == position { @@ -107,7 +113,7 @@ impl PanelState { position: &PanelPosition, ) -> Option>> { let mut active_panel = None; - for panel in self.panels.iter() { + for (_, panel) in self.panels.iter() { let local_panel = panel.clone(); let (current_position, active) = { let panel = panel.lock(); @@ -138,7 +144,7 @@ impl PanelState { } } - for panel in self.panels.iter() { + for (_, panel) in self.panels.iter() { let local_panel = panel.clone(); let (position, active) = { let panel = panel.lock(); diff --git a/core/src/proxy.rs b/core/src/proxy.rs index e5b12c0215..bd8e895e5f 100644 --- a/core/src/proxy.rs +++ b/core/src/proxy.rs @@ -326,19 +326,22 @@ impl Handler for ProxyHandler { file_explorer.update_count(); LAPCE_APP_STATE.submit_ui_command( LapceUICommand::RequestPaint, - file_explorer.widget_id(), + file_explorer.widget_id, ); } Notification::DiffFiles { files } => { println!("get diff files {:?}", files); - let state = - LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); - let mut source_control = state.source_control.lock(); - source_control.diff_files = files; - LAPCE_APP_STATE.submit_ui_command( - LapceUICommand::RequestPaint, - source_control.widget_id(), - ); + let window_id = self.window_id; + let tab_id = self.tab_id; + std::thread::spawn(move || { + let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id); + let mut source_control = state.source_control.lock(); + source_control.diff_files = files; + LAPCE_APP_STATE.submit_ui_command( + LapceUICommand::RequestPaint, + source_control.widget_id(), + ); + }); } Notification::PublishDiagnostics { diagnostics } => { let state = diff --git a/core/src/source_control.rs b/core/src/source_control.rs index 1fdb147e47..6716256be5 100644 --- a/core/src/source_control.rs +++ b/core/src/source_control.rs @@ -1,25 +1,115 @@ use std::path::PathBuf; use druid::{ - piet::{Text, TextLayout, TextLayoutBuilder}, - Affine, Env, FontFamily, PaintCtx, Point, RenderContext, WidgetId, WindowId, + piet::{Text, TextLayout as PietTextLayout, TextLayoutBuilder}, + theme, + widget::{CrossAxisAlignment, Flex, FlexParams, Label, Scroll}, + Affine, BoxConstraints, Color, Cursor, Data, Env, Event, EventCtx, FontFamily, + LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Point, Rect, RenderContext, Size, + TextLayout, UpdateCtx, Widget, WidgetExt, WidgetId, WidgetPod, WindowId, }; use crate::{ + command::{LapceUICommand, LAPCE_UI_COMMAND}, palette::{file_svg, svg_tree_size}, panel::{PanelPosition, PanelProperty}, state::{LapceUIState, LAPCE_APP_STATE}, theme::LapceTheme, }; +pub struct SourceControl { + window_id: WindowId, + tab_id: WidgetId, + widget_id: WidgetId, +} + +impl SourceControl { + pub fn new(window_id: WindowId, tab_id: WidgetId, widget_id: WidgetId) -> Self { + Self { + window_id, + tab_id, + widget_id, + } + } +} + +impl Widget for SourceControl { + fn id(&self) -> Option { + Some(self.widget_id) + } + + fn event( + &mut self, + ctx: &mut EventCtx, + event: &Event, + data: &mut LapceUIState, + env: &Env, + ) { + match event { + Event::Command(cmd) => match cmd { + _ if cmd.is(LAPCE_UI_COMMAND) => { + let command = cmd.get_unchecked(LAPCE_UI_COMMAND); + match command { + LapceUICommand::RequestPaint => { + ctx.request_paint(); + } + _ => (), + } + } + _ => (), + }, + _ => (), + } + } + + fn lifecycle( + &mut self, + ctx: &mut LifeCycleCtx, + event: &LifeCycle, + data: &LapceUIState, + env: &Env, + ) { + } + + fn update( + &mut self, + ctx: &mut UpdateCtx, + old_data: &LapceUIState, + data: &LapceUIState, + env: &Env, + ) { + } + + fn layout( + &mut self, + ctx: &mut LayoutCtx, + bc: &BoxConstraints, + data: &LapceUIState, + env: &Env, + ) -> Size { + bc.max() + } + + fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) { + let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); + let source_control = state.source_control.lock(); + source_control.paint(ctx, data, env); + } +} + pub struct SourceControlState { window_id: WindowId, tab_id: WidgetId, + pub widget_id: WidgetId, position: PanelPosition, pub diff_files: Vec, } impl PanelProperty for SourceControlState { + fn widget_id(&self) -> WidgetId { + self.widget_id + } + fn position(&self) -> &PanelPosition { &self.position } @@ -34,15 +124,33 @@ impl PanelProperty for SourceControlState { fn paint(&self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) { let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT); + + let size = ctx.size(); + let header_height = line_height; + let header_rect = Rect::ZERO.with_size(Size::new(size.width, header_height)); + if let Some(background) = LAPCE_APP_STATE.theme.get("background") { + ctx.fill(header_rect, background); + } + ctx.fill( + Size::new(size.width, size.height - header_height) + .to_rect() + .with_origin(Point::new(0.0, header_height)), + &env.get(LapceTheme::EDITOR_CURRENT_LINE_BACKGROUND), + ); + + let text_layout = ctx + .text() + .new_text_layout("Source Control") + .font(FontFamily::SYSTEM_UI, 14.0) + .text_color(env.get(LapceTheme::EDITOR_FOREGROUND)); + let text_layout = text_layout.build().unwrap(); + ctx.draw_text(&text_layout, Point::new(20.0, 5.0)); + let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); let workspace_path = state.workspace.lock().path.clone(); let rects = ctx.region().rects().to_vec(); for rect in rects { - if let Some(background) = LAPCE_APP_STATE.theme.get("background") { - ctx.fill(rect, background); - } - for (line, file) in self.diff_files.iter().enumerate() { let file_name = file.file_name().unwrap().to_str().unwrap().to_string(); @@ -75,7 +183,7 @@ impl PanelProperty for SourceControlState { 0.0, scale, 1.0, - line as f64 * line_height + 5.0, + line as f64 * line_height + 5.0 + header_height, ]); svg_data.to_piet(affine, ctx); } @@ -87,7 +195,10 @@ impl PanelProperty for SourceControlState { let text_layout = text_layout.build().unwrap(); ctx.draw_text( &text_layout, - Point::new(20.0, line as f64 * line_height + 4.0), + Point::new( + 20.0, + line as f64 * line_height + 4.0 + header_height, + ), ); let text_x = text_layout.hit_test_text_position(file_name.len()).point.x; @@ -102,7 +213,10 @@ impl PanelProperty for SourceControlState { .unwrap(); ctx.draw_text( &text_layout, - Point::new(20.0 + text_x + 4.0, line as f64 * line_height + 5.0), + Point::new( + 20.0 + text_x + 4.0, + line as f64 * line_height + 5.0 + header_height, + ), ); } } @@ -114,8 +228,9 @@ impl SourceControlState { Self { window_id, tab_id, + widget_id: WidgetId::next(), diff_files: Vec::new(), - position: PanelPosition::BottomLeft, + position: PanelPosition::LeftBottom, } } diff --git a/core/src/state.rs b/core/src/state.rs index 888341a911..7fb1c8d0ce 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -353,8 +353,6 @@ impl LapceTabState { panel: Arc::new(Mutex::new(PanelState::new(window_id, tab_id))), proxy: Arc::new(Mutex::new(None)), }; - state.panel.lock().add(state.file_explorer.clone()); - state.panel.lock().add(state.source_control.clone()); let local_state = state.clone(); thread::spawn(move || { local_state.start_proxy(); diff --git a/core/src/window.rs b/core/src/window.rs index 7147663925..4c8a7a15da 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -3,7 +3,9 @@ use crate::{ command::LAPCE_UI_COMMAND, container::LapceContainer, editor::EditorUIState, + explorer::{FileExplorer, FileExplorerState}, panel::{LapcePanel, PanelPosition, PanelProperty}, + source_control::{SourceControl, SourceControlState}, split::LapceSplit, state::{ LapceTabState, LapceUIState, LapceWindowState, LapceWorkspaceType, @@ -18,6 +20,7 @@ use druid::{ LifeCycleCtx, PaintCtx, Point, Rect, RenderContext, Size, TextLayout, UpdateCtx, Widget, WidgetId, WidgetPod, WindowId, }; +use parking_lot::Mutex; use std::{collections::HashMap, sync::Arc}; pub struct LapceTab { @@ -25,7 +28,9 @@ pub struct LapceTab { tab_id: WidgetId, main_split: WidgetPod, status: WidgetPod, - panels: HashMap>, + // panels: HashMap>, + panel_widgets: + HashMap>>>, } impl LapceTab { @@ -34,79 +39,31 @@ impl LapceTab { let container_id = WidgetId::next(); let container = LapceContainer::new(window_id.clone(), tab_id.clone()) .with_id(container_id.clone()); - let main_split = LapceSplit::new(window_id, tab_id, true) - // .with_child( - // FileExplorer::new(window_id.clone(), tab_id.clone()) - // .with_id(file_explorer_widget_id), - // 300.0, - // ) - .with_flex_child(container, 1.0); + let main_split = + LapceSplit::new(window_id, tab_id, true).with_flex_child(container, 1.0); let status = LapceStatus::new(window_id.clone(), tab_id.clone()); - let panels = { - let panel_state = state.panel.lock(); - let mut panels = HashMap::new(); - panels.insert( - PanelPosition::LeftTop, - WidgetPod::new(LapcePanel::new( - panel_state.widget_id(&PanelPosition::LeftTop), - window_id, - tab_id, - PanelPosition::LeftTop, - )), - ); - panels.insert( - PanelPosition::LeftBottom, - WidgetPod::new(LapcePanel::new( - panel_state.widget_id(&PanelPosition::LeftBottom), - window_id, - tab_id, - PanelPosition::LeftBottom, - )), - ); - panels.insert( - PanelPosition::BottomLeft, - WidgetPod::new(LapcePanel::new( - panel_state.widget_id(&PanelPosition::BottomLeft), - window_id, - tab_id, - PanelPosition::BottomLeft, - )), - ); - panels.insert( - PanelPosition::BottomRight, - WidgetPod::new(LapcePanel::new( - panel_state.widget_id(&PanelPosition::BottomRight), - window_id, - tab_id, - PanelPosition::BottomRight, - )), - ); - panels.insert( - PanelPosition::RightTop, - WidgetPod::new(LapcePanel::new( - panel_state.widget_id(&PanelPosition::RightTop), - window_id, - tab_id, - PanelPosition::RightTop, - )), - ); - panels.insert( - PanelPosition::RightBottom, - WidgetPod::new(LapcePanel::new( - panel_state.widget_id(&PanelPosition::RightBottom), - window_id, - tab_id, - PanelPosition::RightBottom, - )), - ); - panels + let panel_widgets = { + let mut panel_state = state.panel.lock(); + let mut widgets = HashMap::new(); + + let widget_id = { state.file_explorer.lock().widget_id }; + let explorer = FileExplorer::new(window_id, tab_id, widget_id); + panel_state.add(widget_id, state.file_explorer.clone()); + widgets.insert(widget_id, WidgetPod::new(explorer).boxed()); + + let widget_id = { state.source_control.lock().widget_id }; + let source_control = SourceControl::new(window_id, tab_id, widget_id); + panel_state.add(widget_id, state.source_control.clone()); + widgets.insert(widget_id, WidgetPod::new(source_control).boxed()); + + widgets }; LapceTab { window_id, tab_id, main_split: WidgetPod::new(main_split), status: WidgetPod::new(status), - panels, + panel_widgets, } } } @@ -189,9 +146,9 @@ impl Widget for LapceTab { let panel_state = state.panel.lock(); panel_state.shown_panels() }; - for (p, _) in shown_panels { - self.panels - .get_mut(&p) + for (_, panel) in shown_panels { + self.panel_widgets + .get_mut(&panel.lock().widget_id()) .unwrap() .event(ctx, event, data, env); } @@ -206,7 +163,7 @@ impl Widget for LapceTab { ) { self.main_split.lifecycle(ctx, event, data, env); self.status.lifecycle(ctx, event, data, env); - for (p, panel) in self.panels.iter_mut() { + for (p, panel) in self.panel_widgets.iter_mut() { panel.lifecycle(ctx, event, data, env); } } @@ -220,7 +177,7 @@ impl Widget for LapceTab { ) { self.main_split.update(ctx, data, env); self.status.update(ctx, data, env); - for (p, panel) in self.panels.iter_mut() { + for (p, panel) in self.panel_widgets.iter_mut() { panel.update(ctx, data, env); } } @@ -235,7 +192,7 @@ impl Widget for LapceTab { let self_size = bc.max(); let status_size = self.status.layout(ctx, bc, data, env); - for (p, panel) in self.panels.iter_mut() { + for (p, panel) in self.panel_widgets.iter_mut() { panel.layout( ctx, &BoxConstraints::new(Size::ZERO, Size::ZERO), @@ -321,36 +278,93 @@ impl Widget for LapceTab { let mut main_split_width = self_size.width; if let Some(left) = left { main_split_width -= left; - let panel = self.panels.get_mut(&PanelPosition::LeftTop).unwrap(); - panel.layout( - ctx, - &BoxConstraints::tight(Size::new( - left, - self_size.height - status_size.height, - )), - data, - env, - ); - panel.set_origin(ctx, data, env, Point::ZERO); + let height = self_size.height - status_size.height; + if let Some(top_panel_state) = shown_panels.get(&PanelPosition::LeftTop) + { + if let Some(bottom_panel_state) = + shown_panels.get(&PanelPosition::LeftBottom) + { + let bottom_panel_state = bottom_panel_state.lock(); + let (_, ratio) = bottom_panel_state.size(); + let bottom_height = height * ratio; + let top_height = height - bottom_height; + let bottom_panel = self + .panel_widgets + .get_mut(&bottom_panel_state.widget_id()) + .unwrap(); + bottom_panel.layout( + ctx, + &BoxConstraints::tight(Size::new(left, bottom_height)), + data, + env, + ); + bottom_panel.set_origin( + ctx, + data, + env, + Point::new(0.0, top_height), + ); + + let top_panel = self + .panel_widgets + .get_mut(&top_panel_state.lock().widget_id()) + .unwrap(); + top_panel.layout( + ctx, + &BoxConstraints::tight(Size::new(left, top_height)), + data, + env, + ); + top_panel.set_origin(ctx, data, env, Point::ZERO); + } else { + let top_panel = self + .panel_widgets + .get_mut(&top_panel_state.lock().widget_id()) + .unwrap(); + top_panel.layout( + ctx, + &BoxConstraints::tight(Size::new(left, height)), + data, + env, + ); + top_panel.set_origin(ctx, data, env, Point::ZERO); + } + } else { + if let Some(bottom_panel_state) = + shown_panels.get(&PanelPosition::LeftBottom) + { + let bottom_panel = self + .panel_widgets + .get_mut(&bottom_panel_state.lock().widget_id()) + .unwrap(); + bottom_panel.layout( + ctx, + &BoxConstraints::tight(Size::new(left, height)), + data, + env, + ); + bottom_panel.set_origin(ctx, data, env, Point::ZERO); + } + } } if let Some(right) = right { main_split_width -= right; - let panel = self.panels.get_mut(&PanelPosition::RightTop).unwrap(); - panel.layout( - ctx, - &BoxConstraints::tight(Size::new( - right, - self_size.height - status_size.height, - )), - data, - env, - ); - panel.set_origin( - ctx, - data, - env, - Point::new(self_size.width - right, 0.0), - ); + // let panel = self.panels.get_mut(&PanelPosition::RightTop).unwrap(); + // panel.layout( + // ctx, + // &BoxConstraints::tight(Size::new( + // right, + // self_size.height - status_size.height, + // )), + // data, + // env, + // ); + // panel.set_origin( + // ctx, + // data, + // env, + // Point::new(self_size.width - right, 0.0), + // ); } let mut main_split_origin = Point::ZERO; @@ -361,22 +375,22 @@ impl Widget for LapceTab { let mut main_split_height = self_size.height - status_size.height; if let Some(bottom) = bottom { main_split_height -= bottom; - let panel = self.panels.get_mut(&PanelPosition::BottomLeft).unwrap(); - panel.layout( - ctx, - &BoxConstraints::new( - Size::ZERO, - Size::new(main_split_width, bottom), - ), - data, - env, - ); - panel.set_origin( - ctx, - data, - env, - main_split_origin + (0.0, main_split_height), - ); + // let panel = self.panels.get_mut(&PanelPosition::BottomLeft).unwrap(); + // panel.layout( + // ctx, + // &BoxConstraints::new( + // Size::ZERO, + // Size::new(main_split_width, bottom), + // ), + // data, + // env, + // ); + // panel.set_origin( + // ctx, + // data, + // env, + // main_split_origin + (0.0, main_split_height), + // ); } let main_split_size = Size::new(main_split_width, main_split_height); @@ -414,23 +428,11 @@ impl Widget for LapceTab { panel_state.shown_panels() }; for (p, panel) in shown_panels.iter() { - let panel = self.panels.get_mut(p).unwrap(); + let panel = self + .panel_widgets + .get_mut(&panel.lock().widget_id()) + .unwrap(); panel.paint(ctx, data, env); - let mut rect = panel.layout_rect(); - match p { - PanelPosition::LeftTop | PanelPosition::LeftBottom => { - rect.x0 = rect.x1 - 1.0; - ctx.fill(rect, &env.get(theme::BORDER_LIGHT)); - } - PanelPosition::BottomLeft | PanelPosition::BottomRight => { - rect.y1 = rect.y0 + 1.0; - ctx.fill(rect, &env.get(theme::BORDER_LIGHT)); - } - PanelPosition::RightTop | PanelPosition::RightBottom => { - rect.x1 = rect.x0 + 1.0; - ctx.fill(rect, &env.get(theme::BORDER_LIGHT)); - } - } } self.status.paint(ctx, data, env); diff --git a/icons/file_type_rust.svg b/icons/file_type_rust.svg index fa59601305..f62525ee8c 100644 --- a/icons/file_type_rust.svg +++ b/icons/file_type_rust.svg @@ -1 +1 @@ -file_type_rust \ No newline at end of file + \ No newline at end of file diff --git a/proxy/src/dispatch.rs b/proxy/src/dispatch.rs index 63abc6e5a1..ebfee81c0f 100644 --- a/proxy/src/dispatch.rs +++ b/proxy/src/dispatch.rs @@ -4,7 +4,7 @@ use crate::lsp::LspCatalog; use crate::plugin::PluginCatalog; use anyhow::{anyhow, Result}; use crossbeam_channel::{unbounded, Receiver, Sender}; -use git2::{DiffOptions, Repository}; +use git2::{DiffOptions, Oid, Repository}; use jsonrpc_lite::{self, JsonRpc}; use lapce_rpc::{self, Call, RequestId, RpcObject}; use lsp_types::{Position, TextDocumentContentChangeEvent}; @@ -13,16 +13,17 @@ use parking_lot::Mutex; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::json; use serde_json::Value; -use std::path::PathBuf; -use std::sync::Arc; -use std::thread; use std::{cmp, fs}; use std::{collections::HashMap, io}; use std::{collections::HashSet, io::BufRead}; +use std::{path::PathBuf, sync::atomic::AtomicBool}; +use std::{sync::atomic, thread}; +use std::{sync::Arc, time::Duration}; use xi_core_lib::watcher::{EventQueue, FileWatcher, Notify, WatchToken}; use xi_rope::{RopeDelta, RopeInfo}; -pub const OPEN_FILE_EVENT_TOKEN: WatchToken = WatchToken(2); +pub const OPEN_FILE_EVENT_TOKEN: WatchToken = WatchToken(1); +pub const GIT_EVENT_TOKEN: WatchToken = WatchToken(2); #[derive(Clone)] pub struct Dispatcher { @@ -34,6 +35,7 @@ pub struct Dispatcher { plugins: Arc>, pub lsp: Arc>, pub watcher: Arc>>, + pub workspace_updated: Arc, } impl Notify for Dispatcher { @@ -44,44 +46,54 @@ impl Notify for Dispatcher { { dispatcher.watcher.lock().as_mut().unwrap().take_events() } .drain(..) { - match event { - DebouncedEvent::Write(path) | DebouncedEvent::Create(path) => { - if let Some(buffer_id) = { - dispatcher - .open_files - .lock() - .get(&path.to_str().unwrap().to_string()) - } { - if let Some(buffer) = - dispatcher.buffers.lock().get_mut(buffer_id) - { - if get_mod_time(&buffer.path) == buffer.mod_time { - return; - } - if !buffer.dirty { - buffer.reload(); - dispatcher.lsp.lock().update( - buffer, - &TextDocumentContentChangeEvent { - range: None, - range_length: None, - text: buffer.get_document(), - }, - buffer.rev, - ); - dispatcher.sender.send(json!({ - "method": "reload_buffer", - "params": { - "buffer_id": buffer_id, - "rev": buffer.rev, - "new_content": buffer.get_document(), - }, - })); + match token { + OPEN_FILE_EVENT_TOKEN => match event { + DebouncedEvent::Write(path) + | DebouncedEvent::Create(path) => { + if let Some(buffer_id) = { + dispatcher + .open_files + .lock() + .get(&path.to_str().unwrap().to_string()) + } { + if let Some(buffer) = + dispatcher.buffers.lock().get_mut(buffer_id) + { + if get_mod_time(&buffer.path) == buffer.mod_time + { + return; + } + if !buffer.dirty { + buffer.reload(); + dispatcher.lsp.lock().update( + buffer, + &TextDocumentContentChangeEvent { + range: None, + range_length: None, + text: buffer.get_document(), + }, + buffer.rev, + ); + dispatcher.sender.send(json!({ + "method": "reload_buffer", + "params": { + "buffer_id": buffer_id, + "rev": buffer.rev, + "new_content": buffer.get_document(), + }, + })); + } } } } + _ => (), + }, + GIT_EVENT_TOKEN => { + dispatcher + .workspace_updated + .store(true, atomic::Ordering::Relaxed); } - _ => (), + WatchToken(_) => {} } } }); @@ -200,6 +212,7 @@ impl Dispatcher { plugins: Arc::new(Mutex::new(plugins)), lsp: Arc::new(Mutex::new(LspCatalog::new())), watcher: Arc::new(Mutex::new(None)), + workspace_updated: Arc::new(AtomicBool::new(false)), }; *dispatcher.watcher.lock() = Some(FileWatcher::new(dispatcher.clone())); dispatcher.lsp.lock().dispatcher = Some(dispatcher.clone()); @@ -209,6 +222,11 @@ impl Dispatcher { thread::spawn(move || { local_dispatcher.start_update_process(git_receiver); }); + + let local_dispatcher = dispatcher.clone(); + thread::spawn(move || { + local_dispatcher.monitor_workspace_update(); + }); dispatcher } @@ -231,6 +249,24 @@ impl Dispatcher { Ok(()) } + pub fn monitor_workspace_update(&self) -> Result<()> { + loop { + thread::sleep(Duration::from_secs(1)); + if self.workspace_updated.load(atomic::Ordering::Relaxed) { + self.workspace_updated + .store(false, atomic::Ordering::Relaxed); + if let Some(diff_files) = git_diff(&self.workspace.lock()) { + self.send_notification( + "diff_files", + json!({ + "files": diff_files, + }), + ); + } + } + } + } + pub fn start_update_process( &self, receiver: Receiver<(BufferId, u64)>, @@ -329,17 +365,17 @@ impl Dispatcher { } } } - self.watcher.lock().as_mut().unwrap().watch( - workspace.as_path(), - true, - OPEN_FILE_EVENT_TOKEN, - ); self.send_notification( "list_dir", json!({ "items": items, }), ); + self.watcher.lock().as_mut().unwrap().watch( + &workspace, + true, + GIT_EVENT_TOKEN, + ); if let Some(diff_files) = git_diff(&workspace) { self.send_notification( "diff_files", @@ -366,6 +402,11 @@ impl Dispatcher { fn handle_request(&self, id: RequestId, rpc: Request) { match rpc { Request::NewBuffer { buffer_id, path } => { + self.watcher.lock().as_mut().unwrap().watch( + &path, + true, + OPEN_FILE_EVENT_TOKEN, + ); self.open_files .lock() .insert(path.to_str().unwrap().to_string(), buffer_id); @@ -517,14 +558,25 @@ pub struct DiffHunk { fn git_diff(workspace_path: &PathBuf) -> Option> { let repo = Repository::open(workspace_path.to_str()?).ok()?; - let diff = repo - .diff_index_to_workdir( + let mut diff_files = HashSet::new(); + let diff = repo.diff_index_to_workdir(None, None).ok()?; + for delta in diff.deltas() { + if let Some(path) = delta.new_file().path() { + if let Some(s) = path.to_str() { + diff_files.insert(workspace_path.join(s).to_str()?.to_string()); + } + } + } + let cached_diff = repo + .diff_tree_to_index( + repo.find_tree(repo.revparse_single("HEAD^{tree}").ok()?.id()) + .ok() + .as_ref(), + None, None, - Some(DiffOptions::new().include_untracked(true)), ) .ok()?; - let mut diff_files = HashSet::new(); - for delta in diff.deltas() { + for delta in cached_diff.deltas() { if let Some(path) = delta.new_file().path() { if let Some(s) = path.to_str() { diff_files.insert(workspace_path.join(s).to_str()?.to_string());