From a8ed95e1dcce910ec3b4bb8298885d2b67a7ea8a Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sat, 19 Nov 2022 15:14:13 -0800 Subject: [PATCH] Implementing persistence for the terminal working directory, found an issue with my current data model. :( --- Cargo.lock | 2 +- crates/db/src/db.rs | 12 ++-- crates/editor/src/persistence.rs | 7 ++- crates/terminal/Cargo.toml | 1 + crates/terminal/src/persistence.rs | 61 +++++++++++++++++++ crates/terminal/src/terminal.rs | 21 ++++++- .../terminal/src/terminal_container_view.rs | 18 ++++-- crates/workspace/Cargo.toml | 1 - crates/workspace/src/persistence.rs | 3 +- crates/workspace/src/persistence/model.rs | 8 +-- 10 files changed, 113 insertions(+), 21 deletions(-) create mode 100644 crates/terminal/src/persistence.rs diff --git a/Cargo.lock b/Cargo.lock index d53e91aa71..e887dfee66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5889,6 +5889,7 @@ dependencies = [ "anyhow", "client", "context_menu", + "db", "dirs 4.0.0", "editor", "futures 0.3.25", @@ -7659,7 +7660,6 @@ dependencies = [ "serde_json", "settings", "smallvec", - "sqlez", "theme", "util", ] diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs index 39891718fb..6e4e6e0619 100644 --- a/crates/db/src/db.rs +++ b/crates/db/src/db.rs @@ -1,11 +1,15 @@ pub mod kvp; +// Re-export indoc and sqlez so clients only need to include us +pub use indoc::indoc; +pub use lazy_static; +pub use sqlez; + use std::fs::{create_dir_all, remove_dir_all}; use std::path::Path; #[cfg(any(test, feature = "test-support"))] use anyhow::Result; -use indoc::indoc; #[cfg(any(test, feature = "test-support"))] use sqlez::connection::Connection; use sqlez::domain::{Domain, Migrator}; @@ -54,17 +58,17 @@ pub fn write_db_to>( #[macro_export] macro_rules! connection { ($id:ident: $t:ident<$d:ty>) => { - pub struct $t(::sqlez::thread_safe_connection::ThreadSafeConnection<$d>); + pub struct $t(::db::sqlez::thread_safe_connection::ThreadSafeConnection<$d>); impl ::std::ops::Deref for $t { - type Target = ::sqlez::thread_safe_connection::ThreadSafeConnection<$d>; + type Target = ::db::sqlez::thread_safe_connection::ThreadSafeConnection<$d>; fn deref(&self) -> &Self::Target { &self.0 } } - lazy_static! { + ::db::lazy_static::lazy_static! { pub static ref $id: $t = $t(if cfg!(any(test, feature = "test-support")) { ::db::open_memory_db(None) } else { diff --git a/crates/editor/src/persistence.rs b/crates/editor/src/persistence.rs index 2c190d8608..5870bc71e5 100644 --- a/crates/editor/src/persistence.rs +++ b/crates/editor/src/persistence.rs @@ -3,7 +3,6 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; use db::connection; use indoc::indoc; -use lazy_static::lazy_static; use sqlez::domain::Domain; use workspace::{ItemId, Workspace, WorkspaceId}; @@ -22,7 +21,11 @@ impl Domain for Editor { item_id INTEGER NOT NULL, workspace_id BLOB NOT NULL, path BLOB NOT NULL, - PRIMARY KEY(item_id, workspace_id) + PRIMARY KEY(item_id, workspace_id), + FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) + ON DELETE CASCADE + ON UPDATE CASCADE + ) STRICT; "}] } diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 785cf3365b..5593ee92d4 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -17,6 +17,7 @@ settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } +db = { path = "../db" } alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "a51dbe25d67e84d6ed4261e640d3954fbdd9be45" } procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false } smallvec = { version = "1.6", features = ["union"] } diff --git a/crates/terminal/src/persistence.rs b/crates/terminal/src/persistence.rs new file mode 100644 index 0000000000..c7808b0dbf --- /dev/null +++ b/crates/terminal/src/persistence.rs @@ -0,0 +1,61 @@ +use std::path::{Path, PathBuf}; + +use db::{connection, indoc, sqlez::domain::Domain}; +use util::{iife, ResultExt}; +use workspace::{ItemId, Workspace, WorkspaceId}; + +use crate::Terminal; + +connection!(TERMINAL_CONNECTION: TerminalDb<(Workspace, Terminal)>); + +impl Domain for Terminal { + fn name() -> &'static str { + "terminal" + } + + fn migrations() -> &'static [&'static str] { + &[indoc! {" + CREATE TABLE terminals ( + item_id INTEGER, + workspace_id BLOB, + working_directory BLOB, + PRIMARY KEY(item_id, workspace_id), + FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) + ON DELETE CASCADE + ON UPDATE CASCADE + ) STRICT; + "}] + } +} + +impl TerminalDb { + pub fn save_working_directory( + &self, + item_id: ItemId, + workspace_id: &WorkspaceId, + working_directory: &Path, + ) { + iife!({ + self.exec_bound::<(ItemId, &WorkspaceId, &Path)>(indoc! {" + INSERT OR REPLACE INTO terminals(item_id, workspace_id, working_directory) + VALUES (?, ?, ?) + "})?((item_id, workspace_id, working_directory)) + }) + .log_err(); + } + + pub fn get_working_directory( + &self, + item_id: ItemId, + workspace_id: &WorkspaceId, + ) -> Option { + iife!({ + self.select_row_bound::<(ItemId, &WorkspaceId), PathBuf>(indoc! {" + SELECT working_directory + FROM terminals + WHERE item_id = ? workspace_id = ?"})?((item_id, workspace_id)) + }) + .log_err() + .flatten() + } +} diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 7e469e19fe..088729ff02 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1,4 +1,5 @@ pub mod mappings; +mod persistence; pub mod terminal_container_view; pub mod terminal_element; pub mod terminal_view; @@ -32,9 +33,11 @@ use mappings::mouse::{ alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report, }; +use persistence::TERMINAL_CONNECTION; use procinfo::LocalProcessInfo; use settings::{AlternateScroll, Settings, Shell, TerminalBlink}; use util::ResultExt; +use workspace::{ItemId, WorkspaceId}; use std::{ cmp::min, @@ -281,6 +284,8 @@ impl TerminalBuilder { blink_settings: Option, alternate_scroll: &AlternateScroll, window_id: usize, + item_id: ItemId, + workspace_id: WorkspaceId, ) -> Result { let pty_config = { let alac_shell = shell.clone().and_then(|shell| match shell { @@ -385,6 +390,8 @@ impl TerminalBuilder { last_mouse_position: None, next_link_id: 0, selection_phase: SelectionPhase::Ended, + workspace_id, + item_id, }; Ok(TerminalBuilder { @@ -528,6 +535,8 @@ pub struct Terminal { scroll_px: f32, next_link_id: usize, selection_phase: SelectionPhase, + item_id: ItemId, + workspace_id: WorkspaceId, } impl Terminal { @@ -567,7 +576,17 @@ impl Terminal { cx.emit(Event::Wakeup); if self.update_process_info() { - cx.emit(Event::TitleChanged) + cx.emit(Event::TitleChanged); + + if let Some(foreground_info) = self.foreground_process_info { + cx.background().spawn(async move { + TERMINAL_CONNECTION.save_working_directory( + self.item_id, + &self.workspace_id, + &foreground_info.cwd, + ); + }); + } } } AlacTermEvent::ColorRequest(idx, fun_ptr) => { diff --git a/crates/terminal/src/terminal_container_view.rs b/crates/terminal/src/terminal_container_view.rs index 49b6ae341f..2789f81676 100644 --- a/crates/terminal/src/terminal_container_view.rs +++ b/crates/terminal/src/terminal_container_view.rs @@ -1,3 +1,4 @@ +use crate::persistence::TERMINAL_CONNECTION; use crate::terminal_view::TerminalView; use crate::{Event, Terminal, TerminalBuilder, TerminalError}; @@ -13,7 +14,7 @@ use workspace::{ item::{Item, ItemEvent}, ToolbarItemLocation, Workspace, }; -use workspace::{register_deserializable_item, Pane}; +use workspace::{register_deserializable_item, ItemId, Pane, WorkspaceId}; use project::{LocalWorktree, Project, ProjectPath}; use settings::{AlternateScroll, Settings, WorkingDirectory}; @@ -89,6 +90,8 @@ impl TerminalContainer { pub fn new( working_directory: Option, modal: bool, + item_id: ItemId, + workspace_id: WorkspaceId, cx: &mut ViewContext, ) -> Self { let settings = cx.global::(); @@ -115,6 +118,8 @@ impl TerminalContainer { settings.terminal_overrides.blinking.clone(), scroll, cx.window_id(), + item_id, + workspace_id, ) { Ok(terminal) => { let terminal = cx.add_model(|cx| terminal.subscribe(cx)); @@ -386,13 +391,14 @@ impl Item for TerminalContainer { fn deserialize( _project: ModelHandle, _workspace: WeakViewHandle, - _workspace_id: workspace::WorkspaceId, - _item_id: workspace::ItemId, + workspace_id: workspace::WorkspaceId, + item_id: workspace::ItemId, cx: &mut ViewContext, ) -> Task>> { - // TODO: Pull the current working directory out of the DB. - - Task::ready(Ok(cx.add_view(|cx| TerminalContainer::new(None, false, cx)))) + let working_directory = TERMINAL_CONNECTION.get_working_directory(item_id, &workspace_id); + Task::ready(Ok(cx.add_view(|cx| { + TerminalContainer::new(working_directory, false, cx) + }))) } } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 0ce3bc220b..b67ccdeeb7 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -30,7 +30,6 @@ language = { path = "../language" } menu = { path = "../menu" } project = { path = "../project" } settings = { path = "../settings" } -sqlez = { path = "../sqlez" } theme = { path = "../theme" } util = { path = "../util" } async-recursion = "1.0.0" diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index b8beaa0e6d..372c4cafce 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -9,10 +9,9 @@ use anyhow::{anyhow, bail, Result, Context}; use db::connection; use gpui::Axis; use indoc::indoc; -use lazy_static::lazy_static; -use sqlez::domain::Domain; +use db::sqlez::domain::Domain; use util::{iife, unzip_option, ResultExt}; use crate::dock::DockPosition; diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index 0d4aade867..5f046d76ee 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -8,12 +8,12 @@ use anyhow::Result; use async_recursion::async_recursion; use gpui::{AsyncAppContext, Axis, ModelHandle, Task, ViewHandle}; -use project::Project; -use settings::DockAnchor; -use sqlez::{ +use db::sqlez::{ bindable::{Bind, Column}, statement::Statement, }; +use project::Project; +use settings::DockAnchor; use util::ResultExt; use crate::{dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace}; @@ -228,8 +228,8 @@ impl Column for DockPosition { #[cfg(test)] mod tests { + use db::sqlez::connection::Connection; use settings::DockAnchor; - use sqlez::connection::Connection; use super::WorkspaceId;