Hookspace defines your Neovim workspace entirely as a set of hooks. This makes workspaces endlessly flexible.
In your init.lua
-- see instructions for your specific plugin manager
require('packer').use 'midrare/hookspace.nvim'
A workspace is a folder that contains a .hookspace
subfolder. Typically, a workspace is the path to a project repo. You can do three things to a workspace: initialize it, open it, or close it.
:HookspaceInit ~/hello_world
orrequire("hookspace").init("~/hello_world")
:HookspaceOpen ~/hello_world
orrequire("hookspace").open("~/hello_world")
:HookspaceClose
orrequire("hookspace").close()
Each workspace event (e.g. init
, open
, close
) triggers an array of hooks in a well-defined order. Every event, aside from init
(which creates .hookspace
and populates it with some basic metadata files), does nothing by itself, but relies entirely on its hooks to do all of the work. These hooks are supplied by you when you call hookspace.setup()
.
By default, hookspace does nothing. You need to add hooks for it to do something. Here's a minimal starter setup using the built-in hooks. This setup uses hooks to provide what you'd expect from a normal session manager: cd
in/out of the workspace dir, and save/restore the associated sessions. Put this code in your init.lua
.
local cwd = require('hookspace.hooks.cwd')
local session = require('hookspace.hooks.session')
-- NOTE the order of hooks is important!
require('hookspace').setup({
on_init = {
},
on_open = {
session.on_open,
cwd.on_open,
},
on_close = {
session.on_close,
cwd.on_close,
},
})
Optionally, you can load the telescope extension for a workspace picker.
require('telescope').load_extension("hookspace")
Now it's time to initialize your workspace. Run :HookspaceInit ~/Projects/hello_world
to create the .hookspace
subfolder and populate it with some basic workspace metadata files. You can initialize an existing folder as well as an empty folder. To open the workspace, run :HookspaceOpen ~/Projects/hello_world
. (If you run pwd
, you'll notice your working dir has changed. This was the work of the cwd
hook you set up earlier.) When you're done working in your workspace, run :HookspaceClose
to close it. If you quit Neovim without closing your workspace, it will close itself automatically.
A hook is a function with the signature: function(workspace)
. workspace
is an object providing metadata and file paths pertaining to the workspace.
The members of the workspace
object you want to pay attention to are the following.
workspace.rootdir()
is astring
pointing to the root of the workspace (e.g.~/Projects/hello_world
).workspace.datadir()
is astring
pointing to the.hookspace
subdir (e..g~/Projects/hello_world/.hookspace
). Files that are workspace-specific but not user-specific should go here. Any files put here should be considered public: safe to be checked into VCS. For safety, do not write hooks that read sensitive data or run arbitrary code from$datadir
.workspace.localdir()
is astring
pointing to the local instance-specific data dir (e.g.$LOCALAPPDATA/nvim-data/hookspace/cgwjRTcWKp.wkspc
). Files that contain sensitive data or arbitrary code (including session data) should go here.
$localdir
needs some explanation. $localdir
is folder on your local machine, outside of the workspace. $localdir
is not per-workspace, but per-instance. If you take a workspace and copy it, each of the two workspaces will have its own unique $localdir
even though the two workspaces' content files and metadata files are identical. (Internally, $localdir
is calculated from FileID
on Windows and inode
on NIX.) By storing its contents completely outside the workspace itself, $localdir
prevents you from accidentially adding sensitive files to VCS. Also, a malicious repo can't pre-package a malware session or script in a $localdir
. If you're writing a hook that makes use of sensitive data or can run arbitrary code, consider using $localdir
. To get the $localdir
for your workspace, open the workspace and run :HookspaceInfo
.
Let's walk through an example. This is the code for the built-in cwd
hook. The cwd
hook will cd
to the workspace root dir when the workspace is opened, and cd
back when the workspace is closed.
local old_cwd = nil
local function cwd_open(workspace)
old_cwd = vim.fn.getcwd(-1, -1)
vim.api.nvim_set_current_dir(workspace.rootdir())
end
local function cwd_close(workspace)
vim.api.nvim_set_current_dir(old_cwd or '~')
old_cwd = nil
end
Now we can register our hook by calling setup()
.
require('hookspace').setup({
on_init = {},
on_open = {cwd_open},
on_close = {cwd_close},
})
For more examples, look in hookspace.hooks
to see the code for the built-in hooks.
(Note: You might have noticed that there's a bundled utility module, hookspace.luamisc
. Do not use anything from hookspace.luamisc
. luamisc
is part of the private implementation and is subject to sudden breaking changes.)