diff --git a/Cargo.lock b/Cargo.lock index 96ac9a27..26b4cccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1645,6 +1645,7 @@ dependencies = [ "jsonrpc-lite", "lapce-rpc", "lsp-types", + "notify", "parking_lot 0.11.1", "serde", "serde_json", diff --git a/core/src/buffer.rs b/core/src/buffer.rs index ac44f789..3fc895a7 100644 --- a/core/src/buffer.rs +++ b/core/src/buffer.rs @@ -78,7 +78,6 @@ pub struct Buffer { tab_id: WidgetId, pub id: BufferId, pub rope: Rope, - highlight_config: Arc, highlight_names: Vec, pub semantic_tokens: Option>, pub highlights: Vec<(usize, usize, Highlight)>, @@ -104,7 +103,6 @@ pub fn new( tab_id: WidgetId, buffer_id: BufferId, path: &str, - event_sink: ExtEventSink, sender: Sender<(WindowId, WidgetId, BufferId, u64)>, ) -> Buffer { let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id); @@ -117,26 +115,14 @@ pub fn new( .unwrap(); let rope = Rope::from_str(&content).unwrap(); - // let rope = if let Ok(rope) = load_file(&window_id, &tab_id, path) { - // rope - // } else { - // Rope::from("") - // }; - let mut parser = new_parser(LapceLanguage::Rust); - let tree = parser.parse(&rope.to_string(), None).unwrap(); - let (highlight_config, highlight_names) = new_highlight_config(LapceLanguage::Rust); - let path_buf = PathBuf::from_str(path).unwrap(); - path_buf.extension().unwrap().to_str().unwrap().to_string(); - let mut buffer = Buffer { window_id: window_id.clone(), tab_id: tab_id.clone(), id: buffer_id.clone(), rope, - highlight_config: Arc::new(highlight_config), semantic_tokens: None, highlight_names, highlights: Vec::new(), @@ -155,27 +141,29 @@ pub fn new( tree: None, sender, }; - - let language_id = buffer.language_id.clone(); - - // state.plugins.lock().new_buffer(&PluginBufferInfo { - // buffer_id: buffer_id.clone(), - // language_id: buffer.language_id.clone(), - // path: path.to_string(), - // nb_lines: buffer.num_lines(), - // buf_size: buffer.len(), - // rev: buffer.rev, - // }); - // state.lsp.lock().new_buffer( - // &buffer_id, - // path, - // &buffer.language_id, - // buffer.rope.to_string(), - // ); buffer.update_highlights(); buffer } + pub fn reload(&mut self, rev: u64, new_content: &str) { + if self.rev + 1 != rev { + return; + } + self.rope = Rope::from_str(new_content).unwrap(); + self.semantic_tokens = None; + self.highlights = Vec::new(); + self.line_highlights = HashMap::new(); + self.undos = Vec::new(); + self.current_undo = 0; + self.code_actions = HashMap::new(); + self.rev += 1; + self.dirty = false; + self.diff = Vec::new(); + self.line_changes = HashMap::new(); + self.tree = None; + self.update_highlights(); + } + pub fn len(&self) -> usize { self.rope.len() } @@ -836,33 +824,33 @@ pub fn update_line_layouts( false } - pub fn get_text_layout( - &mut self, - text: &mut PietText, - theme: &HashMap, - line: usize, - line_content: String, - env: &Env, - ) -> HighlightTextLayout { - let mut layout_builder = text - .new_text_layout(line_content.clone()) - .font(env.get(LapceTheme::EDITOR_FONT).family, 13.0) - .text_color(env.get(LapceTheme::EDITOR_FOREGROUND)); - for (start, end, hl) in self.get_line_highligh(line) { - if let Some(color) = theme.get(hl) { - layout_builder = layout_builder.range_attribute( - start..end, - TextAttribute::TextColor(color.clone()), - ); - } - } - let layout = layout_builder.build().unwrap(); - HighlightTextLayout { - layout, - text: line_content, - highlights: self.get_line_highligh(line).clone(), - } - } + // pub fn get_text_layout( + // &mut self, + // text: &mut PietText, + // theme: &HashMap, + // line: usize, + // line_content: String, + // env: &Env, + // ) -> HighlightTextLayout { + // let mut layout_builder = text + // .new_text_layout(line_content.clone()) + // .font(env.get(LapceTheme::EDITOR_FONT).family, 13.0) + // .text_color(env.get(LapceTheme::EDITOR_FOREGROUND)); + // for (start, end, hl) in self.get_line_highligh(line) { + // if let Some(color) = theme.get(hl) { + // layout_builder = layout_builder.range_attribute( + // start..end, + // TextAttribute::TextColor(color.clone()), + // ); + // } + // } + // let layout = layout_builder.build().unwrap(); + // HighlightTextLayout { + // layout, + // text: line_content, + // highlights: self.get_line_highligh(line).clone(), + // } + // } pub fn get_document(&self) -> String { self.rope.to_string() diff --git a/core/src/command.rs b/core/src/command.rs index 7d9da2a8..1c25e296 100644 --- a/core/src/command.rs +++ b/core/src/command.rs @@ -205,6 +205,7 @@ pub enum LapceUICommand { UpdateHighlights(BufferId, u64, Vec<(usize, usize, Highlight)>), CenterOfWindow, UpdateLineChanges(BufferId), + ReloadBuffer(BufferId, u64, String), EnsureVisible((Rect, (f64, f64), Option)), EditorViewSize(Size), Scroll((f64, f64)), diff --git a/core/src/editor.rs b/core/src/editor.rs index cea4abec..e597b592 100644 --- a/core/src/editor.rs +++ b/core/src/editor.rs @@ -908,7 +908,6 @@ pub fn get_buffer_from_path( self.tab_id.clone(), buffer_id.clone(), path, - ctx.get_external_handle(), ui_state.highlight_sender.clone(), ); let num_lines = buffer.num_lines(); diff --git a/core/src/proxy.rs b/core/src/proxy.rs index 973b9d90..e5b12c02 100644 --- a/core/src/proxy.rs +++ b/core/src/proxy.rs @@ -225,6 +225,11 @@ pub enum Notification { line_changes: HashMap, rev: u64, }, + ReloadBuffer { + buffer_id: BufferId, + new_content: String, + rev: u64, + }, PublishDiagnostics { diagnostics: PublishDiagnosticsParams, }, @@ -295,6 +300,23 @@ fn handle_notification( self.tab_id, ); } + Notification::ReloadBuffer { + buffer_id, + new_content, + rev, + } => { + let state = + LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); + let mut editor_split = state.editor_split.lock(); + let buffer = editor_split.buffers.get_mut(&buffer_id).unwrap(); + if buffer.rev + 1 != rev { + return; + } + LAPCE_APP_STATE.submit_ui_command( + LapceUICommand::ReloadBuffer(buffer_id, rev, new_content), + self.tab_id, + ); + } Notification::ListDir { mut items } => { items.sort(); let state = diff --git a/core/src/window.rs b/core/src/window.rs index 2343b5f4..71476639 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -150,6 +150,19 @@ fn event( LapceUICommand::RequestPaint => { ctx.request_paint(); } + LapceUICommand::ReloadBuffer( + buffer_id, + rev, + new_content, + ) => { + let state = LAPCE_APP_STATE + .get_tab_state(&self.window_id, &self.tab_id); + let mut editor_split = state.editor_split.lock(); + let buffer = + editor_split.buffers.get_mut(buffer_id).unwrap(); + buffer.reload(*rev, new_content); + editor_split.notify_fill_text_layouts(ctx, buffer_id); + } LapceUICommand::UpdateLineChanges(buffer_id) => { let state = LAPCE_APP_STATE .get_tab_state(&self.window_id, &self.tab_id); @@ -501,7 +514,8 @@ fn event( let tab = LapceTab::new(self.window_id.clone(), tab_id); self.tabs.insert(index + 1, WidgetPod::new(tab)); *window_state.active.lock() = tab_id; - ctx.request_layout(); + ctx.children_changed(); + return; } LapceUICommand::CloseTab => { if self.tabs.len() <= 1 { @@ -525,7 +539,8 @@ fn event( self.tabs.remove(index); //window_state.states.lock().remove(&active); *active = new_active; - ctx.request_layout(); + ctx.children_changed(); + return; } LapceUICommand::NextTab => { let window_state = diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml index fd68fa44..b9562ed2 100644 --- a/proxy/Cargo.toml +++ b/proxy/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Dongdong Zhou "] edition = "2018" [dependencies] +notify = "4.0.16" lapce-rpc = { path = "../rpc" } xi-core-lib = "0.3.0" xi-rope = "0.3.0" diff --git a/proxy/src/buffer.rs b/proxy/src/buffer.rs index 5b805d90..747ea105 100644 --- a/proxy/src/buffer.rs +++ b/proxy/src/buffer.rs @@ -24,6 +24,7 @@ pub struct Buffer { pub rope: Rope, pub path: PathBuf, pub rev: u64, + pub dirty: bool, sender: Sender<(BufferId, u64)>, pub mod_time: Option, } @@ -48,14 +49,16 @@ pub fn new( language_id, rev: 0, sender, + dirty: false, mod_time, } } - pub fn save(&self, rev: u64) -> Result<()> { + pub fn save(&mut self, rev: u64) -> Result<()> { if self.rev != rev { return Err(anyhow!("not the right rev")); } + self.dirty = false; let tmp_extension = self.path.extension().map_or_else( || OsString::from("swp"), |ext| { @@ -71,9 +74,22 @@ pub fn save(&self, rev: u64) -> Result<()> { f.write_all(chunk.as_bytes())?; } fs::rename(tmp_path, &self.path)?; + self.mod_time = get_mod_time(&self.path); Ok(()) } + pub fn reload(&mut self) { + let rope = if let Ok(rope) = load_file(&self.path) { + rope + } else { + Rope::from("") + }; + + self.rope = rope; + self.rev += 1; + self.sender.send((self.id, self.rev)); + } + pub fn update( &mut self, delta: &RopeDelta, @@ -83,6 +99,7 @@ pub fn update( return None; } self.rev += 1; + self.dirty = true; let content_change = get_document_content_changes(delta, self); self.rope = delta.apply(&self.rope); let content_change = match content_change { @@ -190,7 +207,7 @@ fn get_document_content_changes( /// Returns the modification timestamp for the file at a given path, /// if present. -fn get_mod_time>(path: P) -> Option { +pub fn get_mod_time>(path: P) -> Option { File::open(path) .and_then(|f| f.metadata()) .and_then(|meta| meta.modified()) diff --git a/proxy/src/dispatch.rs b/proxy/src/dispatch.rs index 5b6d7ddf..63abc6e5 100644 --- a/proxy/src/dispatch.rs +++ b/proxy/src/dispatch.rs @@ -1,4 +1,4 @@ -use crate::buffer::{Buffer, BufferId}; +use crate::buffer::{get_mod_time, Buffer, BufferId}; use crate::core_proxy::CoreProxy; use crate::lsp::LspCatalog; use crate::plugin::PluginCatalog; @@ -7,7 +7,8 @@ use git2::{DiffOptions, Repository}; use jsonrpc_lite::{self, JsonRpc}; use lapce_rpc::{self, Call, RequestId, RpcObject}; -use lsp_types::Position; +use lsp_types::{Position, TextDocumentContentChangeEvent}; +use notify::DebouncedEvent; use parking_lot::Mutex; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::json; @@ -18,7 +19,7 @@ use std::{cmp, fs}; use std::{collections::HashMap, io}; use std::{collections::HashSet, io::BufRead}; -use xi_core_lib::watcher::{FileWatcher, Notify, WatchToken}; +use xi_core_lib::watcher::{EventQueue, FileWatcher, Notify, WatchToken}; use xi_rope::{RopeDelta, RopeInfo}; pub const OPEN_FILE_EVENT_TOKEN: WatchToken = WatchToken(2); @@ -29,6 +30,7 @@ pub struct Dispatcher { pub git_sender: Sender<(BufferId, u64)>, pub workspace: Arc>, pub buffers: Arc>>, + open_files: Arc>>, plugins: Arc>, pub lsp: Arc>, pub watcher: Arc>>, @@ -42,9 +44,42 @@ fn notify(&self) { { dispatcher.watcher.lock().as_mut().unwrap().take_events() } .drain(..) { - match token { - OPEN_FILE_EVENT_TOKEN => { - eprintln!("file watcher received event {:?}", event); + 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(), + }, + })); + } + } + } } _ => (), } @@ -161,6 +196,7 @@ pub fn new(sender: Sender) -> Dispatcher { git_sender, workspace: Arc::new(Mutex::new(PathBuf::new())), buffers: Arc::new(Mutex::new(HashMap::new())), + open_files: Arc::new(Mutex::new(HashMap::new())), plugins: Arc::new(Mutex::new(plugins)), lsp: Arc::new(Mutex::new(LspCatalog::new())), watcher: Arc::new(Mutex::new(None)), @@ -330,6 +366,9 @@ fn handle_notification(&self, rpc: Notification) { fn handle_request(&self, id: RequestId, rpc: Request) { match rpc { Request::NewBuffer { buffer_id, path } => { + self.open_files + .lock() + .insert(path.to_str().unwrap().to_string(), buffer_id); let buffer = Buffer::new(buffer_id, path, self.git_sender.clone()); let content = buffer.rope.to_string(); self.buffers.lock().insert(buffer_id, buffer); @@ -457,8 +496,8 @@ fn handle_request(&self, id: RequestId, rpc: Request) { } Request::Save { rev, buffer_id } => { eprintln!("receive save"); - let buffers = self.buffers.lock(); - let buffer = buffers.get(&buffer_id).unwrap(); + let mut buffers = self.buffers.lock(); + let buffer = buffers.get_mut(&buffer_id).unwrap(); let resp = buffer.save(rev).map(|r| json!({})); self.lsp.lock().save_buffer(buffer); self.respond(id, resp); diff --git a/src/main.rs b/src/main.rs index 4db4c6f7..dcbca925 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,52 +60,51 @@ fn window_removed( fn build_app(window_id: WindowId) -> impl Widget { let window = LapceWindow::new(window_id); - window - .env_scope(|env: &mut druid::Env, data: &LapceUIState| { - let theme = &LAPCE_APP_STATE.theme; - if let Some(line_highlight) = theme.get("line_highlight") { - env.set( - LapceTheme::EDITOR_CURRENT_LINE_BACKGROUND, - line_highlight.clone(), - ); - }; - if let Some(caret) = theme.get("caret") { - env.set(LapceTheme::EDITOR_CURSOR_COLOR, caret.clone()); - }; - if let Some(foreground) = theme.get("foreground") { - env.set(LapceTheme::EDITOR_FOREGROUND, foreground.clone()); - }; - if let Some(background) = theme.get("background") { - env.set(LapceTheme::EDITOR_BACKGROUND, background.clone()); - }; - if let Some(selection) = theme.get("selection") { - env.set(LapceTheme::EDITOR_SELECTION_COLOR, selection.clone()); - }; - if let Some(color) = theme.get("comment") { - env.set(LapceTheme::EDITOR_COMMENT, color.clone()); - }; - if let Some(color) = theme.get("error") { - env.set(LapceTheme::EDITOR_ERROR, color.clone()); - }; - if let Some(color) = theme.get("warn") { - env.set(LapceTheme::EDITOR_WARN, color.clone()); - }; - env.set(LapceTheme::EDITOR_LINE_HEIGHT, 25.0); - env.set(LapceTheme::PALETTE_BACKGROUND, Color::rgb8(125, 125, 125)); - env.set(LapceTheme::PALETTE_INPUT_FOREROUND, Color::rgb8(0, 0, 0)); + window.env_scope(|env: &mut druid::Env, data: &LapceUIState| { + let theme = &LAPCE_APP_STATE.theme; + if let Some(line_highlight) = theme.get("line_highlight") { env.set( - LapceTheme::PALETTE_INPUT_BACKGROUND, - Color::rgb8(255, 255, 255), + LapceTheme::EDITOR_CURRENT_LINE_BACKGROUND, + line_highlight.clone(), ); - env.set(LapceTheme::PALETTE_INPUT_BORDER, Color::rgb8(0, 0, 0)); - env.set( - LapceTheme::EDITOR_FONT, - FontDescriptor::new(FontFamily::new_unchecked("Cascadia Code")) - .with_size(13.0), - ); - env.set(theme::SCROLLBAR_COLOR, hex_to_color("#c4c4c4").unwrap()); - }) - .debug_invalidation() + }; + if let Some(caret) = theme.get("caret") { + env.set(LapceTheme::EDITOR_CURSOR_COLOR, caret.clone()); + }; + if let Some(foreground) = theme.get("foreground") { + env.set(LapceTheme::EDITOR_FOREGROUND, foreground.clone()); + }; + if let Some(background) = theme.get("background") { + env.set(LapceTheme::EDITOR_BACKGROUND, background.clone()); + }; + if let Some(selection) = theme.get("selection") { + env.set(LapceTheme::EDITOR_SELECTION_COLOR, selection.clone()); + }; + if let Some(color) = theme.get("comment") { + env.set(LapceTheme::EDITOR_COMMENT, color.clone()); + }; + if let Some(color) = theme.get("error") { + env.set(LapceTheme::EDITOR_ERROR, color.clone()); + }; + if let Some(color) = theme.get("warn") { + env.set(LapceTheme::EDITOR_WARN, color.clone()); + }; + env.set(LapceTheme::EDITOR_LINE_HEIGHT, 25.0); + env.set(LapceTheme::PALETTE_BACKGROUND, Color::rgb8(125, 125, 125)); + env.set(LapceTheme::PALETTE_INPUT_FOREROUND, Color::rgb8(0, 0, 0)); + env.set( + LapceTheme::PALETTE_INPUT_BACKGROUND, + Color::rgb8(255, 255, 255), + ); + env.set(LapceTheme::PALETTE_INPUT_BORDER, Color::rgb8(0, 0, 0)); + env.set( + LapceTheme::EDITOR_FONT, + FontDescriptor::new(FontFamily::new_unchecked("Cascadia Code")) + .with_size(13.0), + ); + env.set(theme::SCROLLBAR_COLOR, hex_to_color("#c4c4c4").unwrap()); + }) + // .debug_invalidation() } pub fn main() {