From ef49464040f963840836bb9b751a6b35f406a78c Mon Sep 17 00:00:00 2001 From: Gazareth Date: Mon, 30 Jan 2023 00:17:58 +0000 Subject: [PATCH 01/11] feat(sessions): Track currently open project inside the switcher --- lua/projections/session.lua | 8 --- lua/projections/switcher.lua | 71 ++++++++++++++++++----- lua/projections/utils.lua | 28 +++++++++ lua/telescope/_extensions/projections.lua | 2 +- 4 files changed, 85 insertions(+), 24 deletions(-) diff --git a/lua/projections/session.lua b/lua/projections/session.lua index 171495f..7bdeb07 100644 --- a/lua/projections/session.lua +++ b/lua/projections/session.lua @@ -112,12 +112,4 @@ function Session.latest() return latest_session end --- Restore latest session ----@return boolean -function Session.restore_latest() - local latest_session = Session.latest() - if latest_session == nil then return false end - return Session.restore_from_session_file(tostring(latest_session)) -end - return Session diff --git a/lua/projections/switcher.lua b/lua/projections/switcher.lua index 0c681e3..5b01c42 100644 --- a/lua/projections/switcher.lua +++ b/lua/projections/switcher.lua @@ -1,28 +1,69 @@ local Session = require("projections.session") +local Project = require("projections.project") local utils = require("projections.utils") +---@alias ProjectInfo { path: Path, project: Project } +local initial_project_info = { + project = "", + path = "", +} + local M = {} +M._current = initial_project_info; + +-- Attempts to return the current active project +----@return ProjectInfo +function M:get_current() + return self._current; +end + +-- Creates a table of all necessary information about a project +-- @param path string Path to the project's root directory +-- @param name string Name of the project +-- return nil | ProjectInfo +---@nodiscard +local create_project_info = function(path, name) + return name and path and { + path = path, + project = name, + } or nil +end + +-- Attempts to switch to the last loaded project +---@return boolean +function M:last() + local latest_session = Session.latest() + if latest_session ~= nil then + local project_dir = utils.project_dir_from_session_file(tostring(latest_session)) + return self:switch(project_dir) + end + return false +end + -- Attempts to switch projects and load the session file. ---@param spath string Path to project root ---@return boolean -M.switch = function(spath) - if utils._unsaved_buffers_present() then - vim.notify("projections: Unsaved buffers. Unable to switch projects", vim.log.levels.WARN) - return false - end - - local session_info = Session.info(spath) - if session_info == nil then return false end - - if vim.loop.cwd() ~= spath then Session.store(vim.loop.cwd()) end - vim.cmd("noautocmd cd " .. spath) - vim.cmd [[ +function M:switch(spath) + if utils._unsaved_buffers_present() then + vim.notify("projections: Unsaved buffers. Unable to switch projects", vim.log.levels.WARN) + return false + end + + local session_info = Session.info(spath) + if session_info == nil then return false end + + if vim.loop.cwd() ~= spath then Session.store(vim.loop.cwd()) end + vim.cmd("noautocmd cd " .. spath) + vim.cmd [[ silent! %bdelete clearjumps - ]] - Session.restore(spath) - return true + ]] + self._current = create_project_info(utils.project_name_from_session_filepath(spath)) + if Session.restore(spath) then + vim.schedule(function() print("Restored session for project: ", spath) end) + end + return true end return M diff --git a/lua/projections/utils.lua b/lua/projections/utils.lua index 6cecb59..6e7b40e 100644 --- a/lua/projections/utils.lua +++ b/lua/projections/utils.lua @@ -1,5 +1,33 @@ local M = {} +local split = function(inputString, sep) + local fields = {} + local pattern = string.format("([^%s]+)", sep) + local _ = string.gsub(inputString, pattern, function(c) + fields[#fields + 1] = c + end) + + return fields +end + +M.project_name_from_session_filepath = function(filepath) + local file_name = vim.fn.fnamemodify(filepath, ":p:t") + local split_name = split(file_name, "_") + -- Remove the session identifier (everyting beyond the last '_') + table.remove(split_name) + return table.concat(split_name) +end + +-- Gets a project directory from a session file +---@param filepath string Filepath for session file +---@return string +M.project_dir_from_session_file = function(filepath) + local session = vim.fn.readfile(filepath) + -- Directory for session is found on line 6. It is preceded by "cd ", so we take a substring + local project_dir = string.sub(session[6], 3, -1) + return project_dir +end + -- Checks if unsaved buffers are present ---@return boolean ---@nodiscard diff --git a/lua/telescope/_extensions/projections.lua b/lua/telescope/_extensions/projections.lua index f491c7b..e237aa5 100644 --- a/lua/telescope/_extensions/projections.lua +++ b/lua/telescope/_extensions/projections.lua @@ -65,7 +65,7 @@ local find_projects = function(opts) if opts.action == nil then opts.action = function(selected) if selected ~= nil and selected.value ~= vim.loop.cwd() then - switcher.switch(selected.value) + switcher:switch(selected.value) end end end From 6557040b76571f68bf79c5d11e709f6c7d075fab Mon Sep 17 00:00:00 2001 From: Gazareth Date: Mon, 30 Jan 2023 23:58:45 +0000 Subject: [PATCH 02/11] fix(switcher): Fix call to create_project_info Also changes the function to retrieve the name of the function from the file path itself. --- lua/projections/switcher.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/projections/switcher.lua b/lua/projections/switcher.lua index 5b01c42..8ee9510 100644 --- a/lua/projections/switcher.lua +++ b/lua/projections/switcher.lua @@ -23,7 +23,8 @@ end -- @param name string Name of the project -- return nil | ProjectInfo ---@nodiscard -local create_project_info = function(path, name) +local create_project_info = function(path) + local name = utils.project_name_from_session_filepath(path) return name and path and { path = path, project = name, @@ -59,7 +60,8 @@ function M:switch(spath) silent! %bdelete clearjumps ]] - self._current = create_project_info(utils.project_name_from_session_filepath(spath)) + self._current = create_project_info(spath) + if Session.restore(spath) then vim.schedule(function() print("Restored session for project: ", spath) end) end From 22535882887357484384a6441ab64a0afa5eb7dd Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sat, 4 Feb 2023 23:46:59 +0000 Subject: [PATCH 03/11] fix(session): No longer store session if not formally in a project --- lua/projections/session.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/projections/session.lua b/lua/projections/session.lua index 7bdeb07..868b7a4 100644 --- a/lua/projections/session.lua +++ b/lua/projections/session.lua @@ -58,7 +58,10 @@ end function Session.store(spath) Session._ensure_sessions_directory() local session_info = Session.info(spath) - if session_info == nil then return false end + local Switcher = require("projections.switcher") + local current_project = Switcher:get_current() + -- Don't store session if no session info or currently not in a project + if session_info == nil or current_project == nil or #current_project.path == 0 then return false end return Session.store_to_session_file(tostring(session_info.path)) end From 13ea0d2d59f02aed4a7fb829de0d4e3a11e1705b Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sat, 4 Feb 2023 23:47:52 +0000 Subject: [PATCH 04/11] fix(switcher): Expand project dir, helps convert the file path to work on Windows --- lua/projections/switcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/projections/switcher.lua b/lua/projections/switcher.lua index 8ee9510..76c2c41 100644 --- a/lua/projections/switcher.lua +++ b/lua/projections/switcher.lua @@ -37,7 +37,7 @@ function M:last() local latest_session = Session.latest() if latest_session ~= nil then local project_dir = utils.project_dir_from_session_file(tostring(latest_session)) - return self:switch(project_dir) + return self:switch(vim.fn.expand(project_dir)) end return false end From b2f6a3946243a8cd22159ffec0a4a73cc7e10737 Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sat, 4 Feb 2023 23:48:49 +0000 Subject: [PATCH 05/11] fix(utils): Strip extra space from file path after retrieving from session info --- lua/projections/utils.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/projections/utils.lua b/lua/projections/utils.lua index 6e7b40e..f867dd5 100644 --- a/lua/projections/utils.lua +++ b/lua/projections/utils.lua @@ -24,7 +24,7 @@ end M.project_dir_from_session_file = function(filepath) local session = vim.fn.readfile(filepath) -- Directory for session is found on line 6. It is preceded by "cd ", so we take a substring - local project_dir = string.sub(session[6], 3, -1) + local project_dir = string.sub(session[6], 4, -1) return project_dir end From b8665a278b4ba8b2466228e658b47cecc5edc4a1 Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sat, 4 Feb 2023 23:49:14 +0000 Subject: [PATCH 06/11] docs(session): Update docs to show new way to restore last session --- README.md | 3 ++- doc/projections.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4fd3382..b1f705e 100644 --- a/README.md +++ b/README.md @@ -140,13 +140,14 @@ If you are using the recommended configuration, make sure to remove the -- If in some project's root, attempt to restore that project's session -- If not, restore last session -- If no sessions, do nothing +local Switcher = require("projections.switcher") local Session = require("projections.session") vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = function() if vim.fn.argc() ~= 0 then return end local session_info = Session.info(vim.loop.cwd()) if session_info == nil then - Session.restore_latest() + Switcher.last() else Session.restore(vim.loop.cwd()) end diff --git a/doc/projections.txt b/doc/projections.txt index e2c0a34..76e248c 100644 --- a/doc/projections.txt +++ b/doc/projections.txt @@ -171,13 +171,14 @@ Automatically restore last session The following lines setup an autocmd to -- If in some project's root, attempt to restore that project's session -- If not, restore last session -- If no sessions, do nothing + local Switcher = require("projections.switcher") local Session = require("projections.session") vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = function() if vim.fn.argc() ~= 0 then return end local session_info = Session.info(vim.loop.cwd()) if session_info == nil then - Session.restore_latest() + Switcher.last() else Session.restore(vim.loop.cwd()) end From 62af93a66094edfd37397c1e7d33ea6b1e0fb774 Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sun, 5 Feb 2023 01:04:20 +0000 Subject: [PATCH 07/11] refactor(switcher): Remove unnecessary 'ProjectInfo' pseudoclass, use 'Project' instead --- lua/projections/project.lua | 14 ++++++++++ lua/projections/session.lua | 3 ++ lua/projections/switcher.lua | 54 +++++++++++++++++------------------- lua/projections/utils.lua | 8 ------ 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/lua/projections/project.lua b/lua/projections/project.lua index d025902..3e13d5a 100644 --- a/lua/projections/project.lua +++ b/lua/projections/project.lua @@ -16,6 +16,20 @@ function Project.new(name, workspace) return project end +-- Alternate constructor (using a given project dir) +---@param dir_path string Full path for session file +---@return Project +---@nodiscard +function Project.new_from_dir(dir_path) + local path = Path.new(dir_path) + local name = path:basename() + local workspace = path:parent() + local project = setmetatable({}, Project) + project.name = name + project.workspace = workspace + return project +end + -- Returns the path to the project ---@return Path ---@nodiscard diff --git a/lua/projections/session.lua b/lua/projections/session.lua index 868b7a4..7d107f5 100644 --- a/lua/projections/session.lua +++ b/lua/projections/session.lua @@ -94,6 +94,9 @@ function Session.restore_from_session_file(spath) -- TODO: correctly indicate errors here! vim.cmd("silent! source " .. spath) if config.restore_hooks.post ~= nil then config.restore_hooks.post() end + -- If successful, formally set project + local Switcher = require("projections.switcher") + Switcher:set_current() return true end diff --git a/lua/projections/switcher.lua b/lua/projections/switcher.lua index 76c2c41..49477df 100644 --- a/lua/projections/switcher.lua +++ b/lua/projections/switcher.lua @@ -2,15 +2,11 @@ local Session = require("projections.session") local Project = require("projections.project") local utils = require("projections.utils") ----@alias ProjectInfo { path: Path, project: Project } -local initial_project_info = { - project = "", - path = "", -} +local initial_project_info = {} local M = {} -M._current = initial_project_info; +M._current = Project; -- Attempts to return the current active project ----@return ProjectInfo @@ -18,17 +14,12 @@ function M:get_current() return self._current; end --- Creates a table of all necessary information about a project --- @param path string Path to the project's root directory --- @param name string Name of the project --- return nil | ProjectInfo ----@nodiscard -local create_project_info = function(path) - local name = utils.project_name_from_session_filepath(path) - return name and path and { - path = path, - project = name, - } or nil +-- Attempts to set the current active project, with no args passed, unsets current project +-- @param project_info ProjectInfo table of information about the project to set as the current one +----@return boolean +local M:set_current(project_info) + self._current = project_info or initial_project_info + return true end -- Attempts to switch to the last loaded project @@ -37,33 +28,40 @@ function M:last() local latest_session = Session.latest() if latest_session ~= nil then local project_dir = utils.project_dir_from_session_file(tostring(latest_session)) - return self:switch(vim.fn.expand(project_dir)) + -- "expand" for OS compatiblity (Windows) + project_dir = vim.fn.expand(project_dir) + return self:switch(Project.new_from_dir(project_dir)) end return false end -- Attempts to switch projects and load the session file. ----@param spath string Path to project root +---@param new_project Project table describing project to switch to ---@return boolean -function M:switch(spath) +function M:switch(new_project) + local new_path = new_project:path() if utils._unsaved_buffers_present() then vim.notify("projections: Unsaved buffers. Unable to switch projects", vim.log.levels.WARN) return false end - local session_info = Session.info(spath) - if session_info == nil then return false end - - if vim.loop.cwd() ~= spath then Session.store(vim.loop.cwd()) end - vim.cmd("noautocmd cd " .. spath) + -- Attempt to store current session before moving on to a new project + if new_path ~= self._current:path() then Session.store(vim.loop.cwd()) end + vim.cmd("noautocmd cd " .. new_path) vim.cmd [[ silent! %bdelete clearjumps ]] - self._current = create_project_info(spath) - if Session.restore(spath) then - vim.schedule(function() print("Restored session for project: ", spath) end) + -- Formally set the current project + self._current = new_project + + -- If there's a session for the project we're switching to, attempt to restore it + local session_info = Session.info(new_path) + if session_info == nil then return false expand + + if Session.restore(new_path) then + vim.schedule(function() print("Restored session for project: ", new_path) end) end return true end diff --git a/lua/projections/utils.lua b/lua/projections/utils.lua index f867dd5..f57c23f 100644 --- a/lua/projections/utils.lua +++ b/lua/projections/utils.lua @@ -10,14 +10,6 @@ local split = function(inputString, sep) return fields end -M.project_name_from_session_filepath = function(filepath) - local file_name = vim.fn.fnamemodify(filepath, ":p:t") - local split_name = split(file_name, "_") - -- Remove the session identifier (everyting beyond the last '_') - table.remove(split_name) - return table.concat(split_name) -end - -- Gets a project directory from a session file ---@param filepath string Filepath for session file ---@return string From fa303f05ce2781a0ed9c52d930d7beb32a264e31 Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sun, 5 Feb 2023 12:31:20 +0000 Subject: [PATCH 08/11] docs(autocmds): Update docs to conform to new method of calling switch() --- README.md | 2 +- doc/projections.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b1f705e..322f2cb 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ use({ local switcher = require("projections.switcher") vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = function() - if vim.fn.argc() == 0 then switcher.switch(vim.loop.cwd()) end + if vim.fn.argc() == 0 then switcher:switch(vim.loop.cwd()) end end, }) end diff --git a/doc/projections.txt b/doc/projections.txt index 76e248c..1153014 100644 --- a/doc/projections.txt +++ b/doc/projections.txt @@ -140,7 +140,7 @@ The recommended setup does the following: local switcher = require("projections.switcher") vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = function() - if vim.fn.argc() == 0 then switcher.switch(vim.loop.cwd()) end + if vim.fn.argc() == 0 then switcher:switch(vim.loop.cwd()) end end, }) end From 935c4911464440a90a4809d64bc87e1e06c9930c Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sun, 5 Feb 2023 12:37:01 +0000 Subject: [PATCH 09/11] fix(switcher): Update switcher to allow passing in a Project class instance - Convert switcher:switch() into being a wrapper function around switcher:switch_project() which constructs a Project instance internally - Fix 'workspace' not being created properly in Project:new_from_dir - Update switcher:set_current()to be safer - Reorder logic of switcher:switch() to improve safety and terseness --- lua/projections/project.lua | 3 +- lua/projections/session.lua | 2 +- lua/projections/switcher.lua | 59 +++++++++++++++++++++++++----------- lua/projections/utils.lua | 18 +++++++++-- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/lua/projections/project.lua b/lua/projections/project.lua index 3e13d5a..6e28891 100644 --- a/lua/projections/project.lua +++ b/lua/projections/project.lua @@ -23,7 +23,7 @@ end function Project.new_from_dir(dir_path) local path = Path.new(dir_path) local name = path:basename() - local workspace = path:parent() + local workspace = require("projections.workspace").new(path:parent()) local project = setmetatable({}, Project) project.name = name project.workspace = workspace @@ -34,6 +34,7 @@ end ---@return Path ---@nodiscard function Project:path() + if not self.workspace or not self.name then return Path.new("") end return self.workspace.path .. self.name end diff --git a/lua/projections/session.lua b/lua/projections/session.lua index 7d107f5..dd05ea8 100644 --- a/lua/projections/session.lua +++ b/lua/projections/session.lua @@ -61,7 +61,7 @@ function Session.store(spath) local Switcher = require("projections.switcher") local current_project = Switcher:get_current() -- Don't store session if no session info or currently not in a project - if session_info == nil or current_project == nil or #current_project.path == 0 then return false end + if session_info == nil or current_project == nil or #current_project:path() == 0 then return false end return Session.store_to_session_file(tostring(session_info.path)) end diff --git a/lua/projections/switcher.lua b/lua/projections/switcher.lua index 49477df..7005644 100644 --- a/lua/projections/switcher.lua +++ b/lua/projections/switcher.lua @@ -2,8 +2,6 @@ local Session = require("projections.session") local Project = require("projections.project") local utils = require("projections.utils") -local initial_project_info = {} - local M = {} M._current = Project; @@ -17,8 +15,12 @@ end -- Attempts to set the current active project, with no args passed, unsets current project -- @param project_info ProjectInfo table of information about the project to set as the current one ----@return boolean -local M:set_current(project_info) - self._current = project_info or initial_project_info +function M:set_current(project_info) + if project_info == nil then + self._current = Project + else + self._current = project_info + end return true end @@ -30,40 +32,61 @@ function M:last() local project_dir = utils.project_dir_from_session_file(tostring(latest_session)) -- "expand" for OS compatiblity (Windows) project_dir = vim.fn.expand(project_dir) - return self:switch(Project.new_from_dir(project_dir)) + return self:switch(project_dir) end return false end + -- Attempts to switch projects and load the session file. ---@param new_project Project table describing project to switch to ---@return boolean -function M:switch(new_project) - local new_path = new_project:path() +function M:switch_project(new_project) + local new_path = tostring(new_project:path()) + local current_path = tostring(self._current and self._current:path() or Path.new("")) + if #new_path == 0 or new_path == current_path then return false end + if utils._unsaved_buffers_present() then vim.notify("projections: Unsaved buffers. Unable to switch projects", vim.log.levels.WARN) return false end - -- Attempt to store current session before moving on to a new project - if new_path ~= self._current:path() then Session.store(vim.loop.cwd()) end + + -- Store current session before moving on to the new project + Session.store(vim.loop.cwd()) + + -- Update current dir to new project dir vim.cmd("noautocmd cd " .. new_path) - vim.cmd [[ - silent! %bdelete - clearjumps - ]] + + + -- Close any existing buffers + if utils._num_valid_buffers() > 0 then + vim.cmd [[ + silent! %bdelete + clearjumps + ]] + end + -- Formally set the current project self._current = new_project -- If there's a session for the project we're switching to, attempt to restore it - local session_info = Session.info(new_path) - if session_info == nil then return false expand + if Session.info(new_path) ~= nil then + if Session.restore(new_path) then + vim.schedule(function() vim.notify("projections: Restored session for project - " .. new_project.name, vim.log.levels.INFO) end) + end + else return false end - if Session.restore(new_path) then - vim.schedule(function() print("Restored session for project: ", new_path) end) - end return true end +-- Attempts to switch projects using only a path to the desired project dir +---@param spath string Path to project root +---@return boolean +function M:switch(spath) + return self:switch_project(Project.new_from_dir(spath)) +end + + return M diff --git a/lua/projections/utils.lua b/lua/projections/utils.lua index f57c23f..09235eb 100644 --- a/lua/projections/utils.lua +++ b/lua/projections/utils.lua @@ -1,9 +1,13 @@ local M = {} -local split = function(inputString, sep) +-- Splits a string at all instances of provided 'separator' +---@param input_string string String to separate +---@param sep string Separator to split on +---@return Table +local split = function(input_string, sep) local fields = {} local pattern = string.format("([^%s]+)", sep) - local _ = string.gsub(inputString, pattern, function(c) + local _ = string.gsub(input_string, pattern, function(c) fields[#fields + 1] = c end) @@ -32,6 +36,16 @@ M._unsaved_buffers_present = function() return false end +-- Gets number of valid buffers currently open +---@return integer +M._num_valid_buffers = function() + local get_ls = vim.tbl_filter(function(buf) + return vim.api.nvim_buf_is_valid(buf) + and vim.api.nvim_buf_get_option(buf, 'buflisted') + end, vim.api.nvim_list_bufs()) + return #get_ls +end + -- Calculate fnv1a hash ---@param s string String to hash ---@return integer From 99e23e10b50170a4f69b3ec4b757fe8ea82c8c53 Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sun, 5 Feb 2023 21:16:20 +0000 Subject: [PATCH 10/11] refactor(project): Revert to original method for creating instances of the Project class - Remove Project.new_from_dir() - Extract 'find workspace' logic into Workspace.find() - Add explicit function for detecting if we are in a project: Switcher:in_project() - Expand paths in Switcher:switch_project() so that comparisons provide consistent results --- lua/projections/project.lua | 14 ---------- lua/projections/session.lua | 17 +++--------- lua/projections/switcher.lua | 49 +++++++++++++++++++++++------------ lua/projections/workspace.lua | 16 ++++++++++++ 4 files changed, 53 insertions(+), 43 deletions(-) diff --git a/lua/projections/project.lua b/lua/projections/project.lua index 6e28891..5215389 100644 --- a/lua/projections/project.lua +++ b/lua/projections/project.lua @@ -16,20 +16,6 @@ function Project.new(name, workspace) return project end --- Alternate constructor (using a given project dir) ----@param dir_path string Full path for session file ----@return Project ----@nodiscard -function Project.new_from_dir(dir_path) - local path = Path.new(dir_path) - local name = path:basename() - local workspace = require("projections.workspace").new(path:parent()) - local project = setmetatable({}, Project) - project.name = name - project.workspace = workspace - return project -end - -- Returns the path to the project ---@return Path ---@nodiscard diff --git a/lua/projections/session.lua b/lua/projections/session.lua index dd05ea8..5704e7e 100644 --- a/lua/projections/session.lua +++ b/lua/projections/session.lua @@ -19,14 +19,7 @@ function Session.info(spath) local path = Path.new(spath) local project_name = path:basename() local workspace_path = path:parent() - local all_workspaces = Workspace.get_workspaces() - local workspace = nil - for _, ws in ipairs(all_workspaces) do - if workspace_path == ws.path then - workspace = ws - break - end - end + local workspace = Workspace.find(workspace_path) if workspace == nil or not workspace:is_project(project_name) then return nil end local filename = Session.session_filename(tostring(workspace_path), project_name) @@ -59,9 +52,10 @@ function Session.store(spath) Session._ensure_sessions_directory() local session_info = Session.info(spath) local Switcher = require("projections.switcher") - local current_project = Switcher:get_current() -- Don't store session if no session info or currently not in a project - if session_info == nil or current_project == nil or #current_project:path() == 0 then return false end + if session_info == nil or not Switcher:in_project() then + return false + end return Session.store_to_session_file(tostring(session_info.path)) end @@ -94,9 +88,6 @@ function Session.restore_from_session_file(spath) -- TODO: correctly indicate errors here! vim.cmd("silent! source " .. spath) if config.restore_hooks.post ~= nil then config.restore_hooks.post() end - -- If successful, formally set project - local Switcher = require("projections.switcher") - Switcher:set_current() return true end diff --git a/lua/projections/switcher.lua b/lua/projections/switcher.lua index 7005644..4b35aef 100644 --- a/lua/projections/switcher.lua +++ b/lua/projections/switcher.lua @@ -12,6 +12,11 @@ function M:get_current() return self._current; end +-- Returns whether or not we are currently in a project +function M:in_project() + return (#tostring(self._current:path()) > 0) +end + -- Attempts to set the current active project, with no args passed, unsets current project -- @param project_info ProjectInfo table of information about the project to set as the current one ----@return boolean @@ -42,8 +47,8 @@ end ---@param new_project Project table describing project to switch to ---@return boolean function M:switch_project(new_project) - local new_path = tostring(new_project:path()) - local current_path = tostring(self._current and self._current:path() or Path.new("")) + local new_path = vim.fn.expand(tostring(new_project:path())) + local current_path = vim.fn.expand(tostring(self._current and self._current:path() or Path.new(""))) if #new_path == 0 or new_path == current_path then return false end if utils._unsaved_buffers_present() then @@ -51,13 +56,10 @@ function M:switch_project(new_project) return false end - -- Store current session before moving on to the new project - Session.store(vim.loop.cwd()) - - -- Update current dir to new project dir - vim.cmd("noautocmd cd " .. new_path) - + if new_path ~= vim.loop.cwd() then + Session.store(vim.loop.cwd()) + end -- Close any existing buffers if utils._num_valid_buffers() > 0 then @@ -67,16 +69,23 @@ function M:switch_project(new_project) ]] end - - -- Formally set the current project - self._current = new_project - -- If there's a session for the project we're switching to, attempt to restore it - if Session.info(new_path) ~= nil then + local session_info = Session.info(new_path) + if session_info ~= nil then if Session.restore(new_path) then - vim.schedule(function() vim.notify("projections: Restored session for project - " .. new_project.name, vim.log.levels.INFO) end) + vim.schedule(function() vim.notify("[projections.nvim]: Restored session for project - " .. new_project.name, vim.log.levels.INFO) end) + else + -- Something went wrong restoring session + return false end - else return false end + else + -- Otherwise, project with no existing session + -- Update current dir to new project dir + vim.cmd("noautocmd cd " .. new_path) + end + + -- Formally set the current project + self._current = new_project return true end @@ -85,7 +94,15 @@ end ---@param spath string Path to project root ---@return boolean function M:switch(spath) - return self:switch_project(Project.new_from_dir(spath)) + local Workspace = require("projections.workspace") + -- check if path is some project's root + local path = Path.new(spath) + local project_name = path:basename() + local workspace_path = path:parent() + local workspace = Workspace.find(workspace_path) + if workspace == nil or not workspace:is_project(project_name) then return nil end + + return self:switch_project(Project.new(project_name, workspace)) end diff --git a/lua/projections/workspace.lua b/lua/projections/workspace.lua index db8ae7f..b35c6bf 100644 --- a/lua/projections/workspace.lua +++ b/lua/projections/workspace.lua @@ -153,6 +153,22 @@ function Workspace.get_workspaces() return utils._unique_workspaces(workspaces) end +-- Returns the workspace for a given workspace path +---@param workspace_path Path Workspace path of workspace to search for +---@return Workspace[] +---@nodiscard +function Workspace.find(workspace_path) + local all_workspaces = Workspace.get_workspaces() + local workspace = nil + for _, ws in ipairs(all_workspaces) do + if workspace_path == ws.path then + workspace = ws + break + end + end + return workspace +end + -- Add workspace to workspaces file ---@param spath string String representation of path. Can be unnormalized ---@param patterns Patterns The patterns for workspace From e9b996eb81633315fbdcb08dfa0719d7aaab9b8b Mon Sep 17 00:00:00 2001 From: Gazareth Date: Sun, 5 Feb 2023 21:23:22 +0000 Subject: [PATCH 11/11] fix(docs): Revert changes to doc file It is generated automatically from the README so doesn't need to be updated --- doc/projections.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/projections.txt b/doc/projections.txt index 1153014..e2c0a34 100644 --- a/doc/projections.txt +++ b/doc/projections.txt @@ -140,7 +140,7 @@ The recommended setup does the following: local switcher = require("projections.switcher") vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = function() - if vim.fn.argc() == 0 then switcher:switch(vim.loop.cwd()) end + if vim.fn.argc() == 0 then switcher.switch(vim.loop.cwd()) end end, }) end @@ -171,14 +171,13 @@ Automatically restore last session The following lines setup an autocmd to -- If in some project's root, attempt to restore that project's session -- If not, restore last session -- If no sessions, do nothing - local Switcher = require("projections.switcher") local Session = require("projections.session") vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = function() if vim.fn.argc() ~= 0 then return end local session_info = Session.info(vim.loop.cwd()) if session_info == nil then - Switcher.last() + Session.restore_latest() else Session.restore(vim.loop.cwd()) end