3370 lines · 120.5 KB · Rust
RawDownload
pub mod model; use std::{    borrow::Cow,    collections::BTreeMap,    path::{Path, PathBuf},    str::FromStr,    sync::Arc,}; use anyhow::{Context as _, Result, bail};use collections::{HashMap, HashSet, IndexSet};use db::{    kvp::KEY_VALUE_STORE,    query,    sqlez::{connection::Connection, domain::Domain},    sqlez_macros::sql,};use gpui::{Axis, Bounds, Task, WindowBounds, WindowId, point, size};use project::{    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},    trusted_worktrees::{DbTrustedPaths, RemoteHostLocation},}; use language::{LanguageName, Toolchain, ToolchainScope};use remote::{    DockerConnectionOptions, RemoteConnectionOptions, SshConnectionOptions, WslConnectionOptions,};use serde::{Deserialize, Serialize};use sqlez::{    bindable::{Bind, Column, StaticColumnCount},    statement::Statement,    thread_safe_connection::ThreadSafeConnection,}; use ui::{App, SharedString, px};use util::{ResultExt, maybe, rel_path::RelPath};use uuid::Uuid; use crate::{    WorkspaceId,    path_list::{PathList, SerializedPathList},    persistence::model::RemoteConnectionKind,}; use model::{    GroupId, ItemId, PaneId, RemoteConnectionId, SerializedItem, SerializedPane,    SerializedPaneGroup, SerializedWorkspace,}; use self::model::{DockStructure, SerializedWorkspaceLocation}; // https://www.sqlite.org/limits.html// > <..> the maximum value of a host parameter number is SQLITE_MAX_VARIABLE_NUMBER,// > which defaults to <..> 32766 for SQLite versions after 3.32.0.const MAX_QUERY_PLACEHOLDERS: usize = 32000; #[derive(Copy, Clone, Debug, PartialEq)]pub(crate) struct SerializedAxis(pub(crate) gpui::Axis);impl sqlez::bindable::StaticColumnCount for SerializedAxis {}impl sqlez::bindable::Bind for SerializedAxis {    fn bind(        &self,        statement: &sqlez::statement::Statement,        start_index: i32,    ) -> anyhow::Result<i32> {        match self.0 {            gpui::Axis::Horizontal => "Horizontal",            gpui::Axis::Vertical => "Vertical",        }        .bind(statement, start_index)    }} impl sqlez::bindable::Column for SerializedAxis {    fn column(        statement: &mut sqlez::statement::Statement,        start_index: i32,    ) -> anyhow::Result<(Self, i32)> {        String::column(statement, start_index).and_then(|(axis_text, next_index)| {            Ok((                match axis_text.as_str() {                    "Horizontal" => Self(Axis::Horizontal),                    "Vertical" => Self(Axis::Vertical),                    _ => anyhow::bail!("Stored serialized item kind is incorrect"),                },                next_index,            ))        })    }} #[derive(Copy, Clone, Debug, PartialEq, Default)]pub(crate) struct SerializedWindowBounds(pub(crate) WindowBounds); impl StaticColumnCount for SerializedWindowBounds {    fn column_count() -> usize {        5    }} impl Bind for SerializedWindowBounds {    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {        match self.0 {            WindowBounds::Windowed(bounds) => {                let next_index = statement.bind(&"Windowed", start_index)?;                statement.bind(                    &(                        SerializedPixels(bounds.origin.x),                        SerializedPixels(bounds.origin.y),                        SerializedPixels(bounds.size.width),                        SerializedPixels(bounds.size.height),                    ),                    next_index,                )            }            WindowBounds::Maximized(bounds) => {                let next_index = statement.bind(&"Maximized", start_index)?;                statement.bind(                    &(                        SerializedPixels(bounds.origin.x),                        SerializedPixels(bounds.origin.y),                        SerializedPixels(bounds.size.width),                        SerializedPixels(bounds.size.height),                    ),                    next_index,                )            }            WindowBounds::Fullscreen(bounds) => {                let next_index = statement.bind(&"FullScreen", start_index)?;                statement.bind(                    &(                        SerializedPixels(bounds.origin.x),                        SerializedPixels(bounds.origin.y),                        SerializedPixels(bounds.size.width),                        SerializedPixels(bounds.size.height),                    ),                    next_index,                )            }        }    }} impl Column for SerializedWindowBounds {    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {        let (window_state, next_index) = String::column(statement, start_index)?;        let ((x, y, width, height), _): ((i32, i32, i32, i32), _) =            Column::column(statement, next_index)?;        let bounds = Bounds {            origin: point(px(x as f32), px(y as f32)),            size: size(px(width as f32), px(height as f32)),        };         let status = match window_state.as_str() {            "Windowed" | "Fixed" => SerializedWindowBounds(WindowBounds::Windowed(bounds)),            "Maximized" => SerializedWindowBounds(WindowBounds::Maximized(bounds)),            "FullScreen" => SerializedWindowBounds(WindowBounds::Fullscreen(bounds)),            _ => bail!("Window State did not have a valid string"),        };         Ok((status, next_index + 4))    }} const DEFAULT_WINDOW_BOUNDS_KEY: &str = "default_window_bounds"; pub fn read_default_window_bounds() -> Option<(Uuid, WindowBounds)> {    let json_str = KEY_VALUE_STORE        .read_kvp(DEFAULT_WINDOW_BOUNDS_KEY)        .log_err()        .flatten()?;     let (display_uuid, persisted) =        serde_json::from_str::<(Uuid, WindowBoundsJson)>(&json_str).ok()?;    Some((display_uuid, persisted.into()))} pub async fn write_default_window_bounds(    bounds: WindowBounds,    display_uuid: Uuid,) -> anyhow::Result<()> {    let persisted = WindowBoundsJson::from(bounds);    let json_str = serde_json::to_string(&(display_uuid, persisted))?;    KEY_VALUE_STORE        .write_kvp(DEFAULT_WINDOW_BOUNDS_KEY.to_string(), json_str)        .await?;    Ok(())} #[derive(Serialize, Deserialize)]pub enum WindowBoundsJson {    Windowed {        x: i32,        y: i32,        width: i32,        height: i32,    },    Maximized {        x: i32,        y: i32,        width: i32,        height: i32,    },    Fullscreen {        x: i32,        y: i32,        width: i32,        height: i32,    },} impl From<WindowBounds> for WindowBoundsJson {    fn from(b: WindowBounds) -> Self {        match b {            WindowBounds::Windowed(bounds) => {                let origin = bounds.origin;                let size = bounds.size;                WindowBoundsJson::Windowed {                    x: f32::from(origin.x).round() as i32,                    y: f32::from(origin.y).round() as i32,                    width: f32::from(size.width).round() as i32,                    height: f32::from(size.height).round() as i32,                }            }            WindowBounds::Maximized(bounds) => {                let origin = bounds.origin;                let size = bounds.size;                WindowBoundsJson::Maximized {                    x: f32::from(origin.x).round() as i32,                    y: f32::from(origin.y).round() as i32,                    width: f32::from(size.width).round() as i32,                    height: f32::from(size.height).round() as i32,                }            }            WindowBounds::Fullscreen(bounds) => {                let origin = bounds.origin;                let size = bounds.size;                WindowBoundsJson::Fullscreen {                    x: f32::from(origin.x).round() as i32,                    y: f32::from(origin.y).round() as i32,                    width: f32::from(size.width).round() as i32,                    height: f32::from(size.height).round() as i32,                }            }        }    }} impl From<WindowBoundsJson> for WindowBounds {    fn from(n: WindowBoundsJson) -> Self {        match n {            WindowBoundsJson::Windowed {                x,                y,                width,                height,            } => WindowBounds::Windowed(Bounds {                origin: point(px(x as f32), px(y as f32)),                size: size(px(width as f32), px(height as f32)),            }),            WindowBoundsJson::Maximized {                x,                y,                width,                height,            } => WindowBounds::Maximized(Bounds {                origin: point(px(x as f32), px(y as f32)),                size: size(px(width as f32), px(height as f32)),            }),            WindowBoundsJson::Fullscreen {                x,                y,                width,                height,            } => WindowBounds::Fullscreen(Bounds {                origin: point(px(x as f32), px(y as f32)),                size: size(px(width as f32), px(height as f32)),            }),        }    }} #[derive(Debug)]pub struct Breakpoint {    pub position: u32,    pub message: Option<Arc<str>>,    pub condition: Option<Arc<str>>,    pub hit_condition: Option<Arc<str>>,    pub state: BreakpointState,} /// Wrapper for DB type of a breakpointstruct BreakpointStateWrapper<'a>(Cow<'a, BreakpointState>); impl From<BreakpointState> for BreakpointStateWrapper<'static> {    fn from(kind: BreakpointState) -> Self {        BreakpointStateWrapper(Cow::Owned(kind))    }} impl StaticColumnCount for BreakpointStateWrapper<'_> {    fn column_count() -> usize {        1    }} impl Bind for BreakpointStateWrapper<'_> {    fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {        statement.bind(&self.0.to_int(), start_index)    }} impl Column for BreakpointStateWrapper<'_> {    fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {        let state = statement.column_int(start_index)?;         match state {            0 => Ok((BreakpointState::Enabled.into(), start_index + 1)),            1 => Ok((BreakpointState::Disabled.into(), start_index + 1)),            _ => anyhow::bail!("Invalid BreakpointState discriminant {state}"),        }    }} impl sqlez::bindable::StaticColumnCount for Breakpoint {    fn column_count() -> usize {        // Position, log message, condition message, and hit condition message        4 + BreakpointStateWrapper::column_count()    }} impl sqlez::bindable::Bind for Breakpoint {    fn bind(        &self,        statement: &sqlez::statement::Statement,        start_index: i32,    ) -> anyhow::Result<i32> {        let next_index = statement.bind(&self.position, start_index)?;        let next_index = statement.bind(&self.message, next_index)?;        let next_index = statement.bind(&self.condition, next_index)?;        let next_index = statement.bind(&self.hit_condition, next_index)?;        statement.bind(            &BreakpointStateWrapper(Cow::Borrowed(&self.state)),            next_index,        )    }} impl Column for Breakpoint {    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {        let position = statement            .column_int(start_index)            .with_context(|| format!("Failed to read BreakPoint at index {start_index}"))?            as u32;        let (message, next_index) = Option::<String>::column(statement, start_index + 1)?;        let (condition, next_index) = Option::<String>::column(statement, next_index)?;        let (hit_condition, next_index) = Option::<String>::column(statement, next_index)?;        let (state, next_index) = BreakpointStateWrapper::column(statement, next_index)?;         Ok((            Breakpoint {                position,                message: message.map(Arc::from),                condition: condition.map(Arc::from),                hit_condition: hit_condition.map(Arc::from),                state: state.0.into_owned(),            },            next_index,        ))    }} #[derive(Clone, Debug, PartialEq)]struct SerializedPixels(gpui::Pixels);impl sqlez::bindable::StaticColumnCount for SerializedPixels {} impl sqlez::bindable::Bind for SerializedPixels {    fn bind(        &self,        statement: &sqlez::statement::Statement,        start_index: i32,    ) -> anyhow::Result<i32> {        let this: i32 = u32::from(self.0) as _;        this.bind(statement, start_index)    }} pub struct WorkspaceDb(ThreadSafeConnection); impl Domain for WorkspaceDb {    const NAME: &str = stringify!(WorkspaceDb);     const MIGRATIONS: &[&str] = &[        sql!(            CREATE TABLE workspaces(                workspace_id INTEGER PRIMARY KEY,                workspace_location BLOB UNIQUE,                dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.                dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.                dock_pane INTEGER, // Deprecated.  Preserving so users can downgrade Zed.                left_sidebar_open INTEGER, // Boolean                timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,                FOREIGN KEY(dock_pane) REFERENCES panes(pane_id)            ) STRICT;             CREATE TABLE pane_groups(                group_id INTEGER PRIMARY KEY,                workspace_id INTEGER NOT NULL,                parent_group_id INTEGER, // NULL indicates that this is a root node                position INTEGER, // NULL indicates that this is a root node                axis TEXT NOT NULL, // Enum: 'Vertical' / 'Horizontal'                FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)                ON DELETE CASCADE                ON UPDATE CASCADE,                FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE            ) STRICT;             CREATE TABLE panes(                pane_id INTEGER PRIMARY KEY,                workspace_id INTEGER NOT NULL,                active INTEGER NOT NULL, // Boolean                FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)                ON DELETE CASCADE                ON UPDATE CASCADE            ) STRICT;             CREATE TABLE center_panes(                pane_id INTEGER PRIMARY KEY,                parent_group_id INTEGER, // NULL means that this is a root pane                position INTEGER, // NULL means that this is a root pane                FOREIGN KEY(pane_id) REFERENCES panes(pane_id)                ON DELETE CASCADE,                FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE            ) STRICT;             CREATE TABLE items(                item_id INTEGER NOT NULL, // This is the item's view id, so this is not unique                workspace_id INTEGER NOT NULL,                pane_id INTEGER NOT NULL,                kind TEXT NOT NULL,                position INTEGER NOT NULL,                active INTEGER NOT NULL,                FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)                ON DELETE CASCADE                ON UPDATE CASCADE,                FOREIGN KEY(pane_id) REFERENCES panes(pane_id)                ON DELETE CASCADE,                PRIMARY KEY(item_id, workspace_id)            ) STRICT;        ),        sql!(            ALTER TABLE workspaces ADD COLUMN window_state TEXT;            ALTER TABLE workspaces ADD COLUMN window_x REAL;            ALTER TABLE workspaces ADD COLUMN window_y REAL;            ALTER TABLE workspaces ADD COLUMN window_width REAL;            ALTER TABLE workspaces ADD COLUMN window_height REAL;            ALTER TABLE workspaces ADD COLUMN display BLOB;        ),        // Drop foreign key constraint from workspaces.dock_pane to panes table.        sql!(            CREATE TABLE workspaces_2(                workspace_id INTEGER PRIMARY KEY,                workspace_location BLOB UNIQUE,                dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.                dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.                dock_pane INTEGER, // Deprecated.  Preserving so users can downgrade Zed.                left_sidebar_open INTEGER, // Boolean                timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,                window_state TEXT,                window_x REAL,                window_y REAL,                window_width REAL,                window_height REAL,                display BLOB            ) STRICT;            INSERT INTO workspaces_2 SELECT * FROM workspaces;            DROP TABLE workspaces;            ALTER TABLE workspaces_2 RENAME TO workspaces;        ),        // Add panels related information        sql!(            ALTER TABLE workspaces ADD COLUMN left_dock_visible INTEGER; //bool            ALTER TABLE workspaces ADD COLUMN left_dock_active_panel TEXT;            ALTER TABLE workspaces ADD COLUMN right_dock_visible INTEGER; //bool            ALTER TABLE workspaces ADD COLUMN right_dock_active_panel TEXT;            ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool            ALTER TABLE workspaces ADD COLUMN bottom_dock_active_panel TEXT;        ),        // Add panel zoom persistence        sql!(            ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool            ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool            ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool        ),        // Add pane group flex data        sql!(            ALTER TABLE pane_groups ADD COLUMN flexes TEXT;        ),        // Add fullscreen field to workspace

Lines 1–500 of 3370.