Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support workspaces config option #155

Merged
merged 31 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
41cfd7f
feat: support workspaces config option
weskoerber Jul 21, 2023
070ef9f
chore: revert changes to doc/obsidian.txt
weskoerber Jul 21, 2023
2aaf7ff
refactor: default_workspace and workspaces table
weskoerber Aug 3, 2023
d3313a3
feat: add command to switch workspace
weskoerber Aug 3, 2023
ee7a3f7
chore: run stylua
weskoerber Aug 3, 2023
9c10132
chore: update changelog
weskoerber Aug 3, 2023
731fd49
fix: not setting workspace in new_from_dir
weskoerber Aug 4, 2023
8d71474
chore: sync doc changes with main
weskoerber Aug 4, 2023
c5a3a8f
chore: maintain current workspace in client
weskoerber Aug 4, 2023
170dddb
refactor: workspace command changes
weskoerber Aug 4, 2023
9c19046
chore: commit stylua changes
weskoerber Aug 4, 2023
591a116
chore: update readme
weskoerber Aug 4, 2023
d780e10
fix: default_workspace type in readme
weskoerber Aug 4, 2023
acd5f23
chore: workspace class
weskoerber Aug 8, 2023
a5931ba
refactor: use workspace class
weskoerber Aug 8, 2023
c31a654
fix: run luacheck and stylua
weskoerber Aug 8, 2023
d3a93c5
chore: update readme
weskoerber Aug 8, 2023
417eb91
fix: passing wrong table to vim.tbl_extend
weskoerber Aug 8, 2023
c741398
chore: clarify table type in workspace class
weskoerber Aug 9, 2023
46b4a83
chore: add workspace tests
weskoerber Aug 9, 2023
598bc0a
chore: add comments clarifying workspace path normalization
weskoerber Oct 17, 2023
48ae1f1
chore: revert changes to doc/obsidian.txt to upstream/main
weskoerber Oct 17, 2023
e190304
chore: add function to create new workspace from dir
weskoerber Oct 17, 2023
1e1ba52
chore: default workspace name to the name of the directory
weskoerber Oct 17, 2023
04dd838
fix: backward incompatibility
weskoerber Oct 17, 2023
852895f
chore: run stylua and luacheck
weskoerber Oct 17, 2023
79118e1
refactor: use dir name for default vault instead of "."
weskoerber Oct 17, 2023
f422f98
fix: backward incompatibility
weskoerber Oct 17, 2023
9402d59
chore: update readme
weskoerber Oct 17, 2023
bc228a0
fix: failed test using wrong workspace name for current directory
weskoerber Oct 17, 2023
242fdbe
update type hints
epwalsh Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- The `overwrite_mappings` option, which sets the mappings in the config even if they already exist
- Added support for multiple vaults (#128)
- Added command to switch between vaults (#60)

### Fixed

- Eliminated silent runtime errors on validation errors in note.from_lines
- Eliminated silent runtime errors on validation errors in note.from_lines

## [v1.14.2](https://github.com/epwalsh/obsidian.nvim/releases/tag/v1.14.2) - 2023-09-25

Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Built for people who love the concept of Obsidian -- a simple, markdown-based no
This command has one optional argument: the ID, path, or alias of the note to link to. If not given, the selected text will be used to find the note with a matching ID, path, or alias.
- `:ObsidianLinkNew` to create a new note and link it to an in-line visual selection of text.
This command has one optional argument: the title of the new note. If not given, the selected text will be used as the title.
- `:ObsidianWorkspace` to switch to another workspace.

### Demo

Expand Down Expand Up @@ -86,7 +87,16 @@ return {
-- see below for full list of optional dependencies 👇
},
opts = {
dir = "~/my-vault", -- no need to call 'vim.fn.expand' here
workspaces = {
{
name = "personal",
path = "~/vaults/personal",
},
{
name = "work",
path = "~/vaults/work",
},
},

-- see below for full list of options 👇
},
Expand All @@ -106,7 +116,16 @@ use({
},
config = function()
require("obsidian").setup({
dir = "~/my-vault",
workspaces = {
{
name = "personal",
path = "~/vaults/personal",
},
{
name = "work",
path = "~/vaults/work",
},
},

-- see below for full list of options 👇
})
Expand All @@ -132,8 +151,21 @@ This is a complete list of all of the options that can be passed to `require("ob

```lua
{
-- Required, the path to your vault directory.
dir = "~/my-vault",
-- Optional, list of vault names and paths.
workspaces = {
{
name = "personal",
path = "~/vaults/personal",
},
{
name = "work",
path = "~/vaults/work",
},
},

-- Optional, set to true to use the current directory as a vault; otherwise,
-- the first workspace is opened by default
detect_cwd = false,

-- Optional, if you keep notes in a specific subdirectory of your vault.
notes_subdir = "notes",
Expand Down
77 changes: 10 additions & 67 deletions doc/obsidian.txt
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file gets updated automatically from the README. So could you please update the README instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh sorry, I might have resolved conflicts in this file poorly when I rebased.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 48ae1f1

Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ REQUIREMENTS *obsidian-requirements*
- NeoVim >= 0.8.0 (this plugin uses `vim.fs` which was only added in 0.8).
- If you want completion and search features (recommended) you’ll also need ripgrep <https://github.com/BurntSushi/ripgrep> to be installed and on your `$PATH`.
See ripgrep#installation <https://github.com/BurntSushi/ripgrep> for install options.
- If you using WSL, you’ll also need wsl-open <https://gitlab.com/4U6U57/wsl-open>

Search functionality (e.g. via the `:ObsidianSearch` and
`:ObsidianQuickSwitch` commands) also requires telescope.nvim
Expand All @@ -97,12 +96,9 @@ USING LAZY.NVIM ~
return {
"epwalsh/obsidian.nvim",
lazy = true,
event = {
-- If you want to use the home shortcut '~' here you need to call 'vim.fn.expand'.
-- E.g. "BufReadPre " .. vim.fn.expand "~" .. "/my-vault/**.md"
"BufReadPre path/to/my-vault/**.md",
"BufNewFile path/to/my-vault/**.md",
},
event = { "BufReadPre path/to/my-vault/**.md" },
-- If you want to use the home shortcut '~' here you need to call 'vim.fn.expand':
-- event = { "BufReadPre " .. vim.fn.expand "~" .. "/my-vault/**.md" },
dependencies = {
-- Required.
"nvim-lua/plenary.nvim",
Expand Down Expand Up @@ -176,12 +172,8 @@ This is a complete list of all of the options that can be passed to
daily_notes = {
-- Optional, if you keep daily notes in a separate directory.
folder = "notes/dailies",
-- Optional, if you want to change the date format for the ID of daily notes.
date_format = "%Y-%m-%d",
-- Optional, if you want to change the date format of the default alias of daily notes.
alias_format = "%B %-d, %Y"
-- Optional, if you want to automatically insert a template from your template directory like 'daily.md'
template = nil
-- Optional, if you want to change the date format for daily notes.
date_format = "%Y-%m-%d"
},

-- Optional, completion.
Expand All @@ -203,19 +195,9 @@ This is a complete list of all of the options that can be passed to
-- Optional, key mappings.
mappings = {
-- Overrides the 'gf' mapping to work on markdown/wiki links within your vault.
["gf"] = {
action = function()
return require("obsidian").util.gf_passthrough()
end,
opts = { noremap = false, expr = true, buffer = true },
},
["gf"] = require("obsidian.mapping").gf_passthrough(),
},

-- Optional, if set to true, the specified mappings in the `mappings`
-- table will overwrite existing ones. Otherwise a warning is printed
-- and the mappings are not applied.
overwrite_mappings = false,

-- Optional, customize how names/IDs for new notes are created.
note_id_func = function(title)
-- Create note IDs in a Zettelkasten format with a timestamp and a suffix.
Expand Down Expand Up @@ -254,18 +236,8 @@ This is a complete list of all of the options that can be passed to
-- Optional, for templates (see below).
templates = {
subdir = "templates",
date_format = "%Y-%m-%d",
date_format = "%Y-%m-%d-%a",
time_format = "%H:%M",
-- A map for custom variables, the key should be the variable and the value a function
substitutions = {}
},

-- Optional, customize the backlinks interface.
backlinks = {
-- The default height of the backlinks pane.
height = 10,
-- Whether or not to wrap lines.
wrap = true,
},

-- Optional, by default when you use `:ObsidianFollowLink` on a link to an external
Expand All @@ -291,12 +263,6 @@ This is a complete list of all of the options that can be passed to
-- remaining finders will be attempted in the original order.
finder = "telescope.nvim",

-- Optional, sort search results by "path", "modified", "accessed", or "created".
-- The recommend value is "modified" and `true` for `sort_reversed`, which means, for example `:ObsidianQuickSwitch`
-- will show the notes sorted by latest modified time
sort_by = "modified",
sort_reversed = true,

-- Optional, determines whether to open notes in a horizontal split, a vertical split,
-- or replacing the current buffer (default)
-- Accepted values are "current", "hsplit" and "vsplit"
Expand Down Expand Up @@ -366,7 +332,7 @@ Then make sure to comment out the `gf` keybinding in your obsidian.nvim config:

>lua
mappings = {
-- ["gf"] = ...
-- ["gf"] = require("obsidian.mapping").gf_passthrough(),
},
<

Expand All @@ -375,12 +341,7 @@ different key:

>lua
mappings = {
["fo"] = {
action = function()
return require("obsidian").util.gf_passthrough()
end,
opts = { noremap = false, expr = true, buffer = true },
},
["fo"] = require("obsidian.mapping").gf_passthrough(),
},
<

Expand All @@ -402,7 +363,7 @@ For example, with the following configuration
templates = {
subdir = "my-templates-folder",
date_format = "%Y-%m-%d-%a",
time_format = "%H:%M",
time_format = "%H:%M"
},
}
<
Expand All @@ -411,7 +372,6 @@ and the file `~/my-vault/my-templates-folder/note template.md`:

>markdown
# {{title}}

Date created: {{date}}
<

Expand All @@ -426,23 +386,6 @@ will insert

above the cursor position.

You can also define custom template substitutions with the configuration field
`templates.substitutions`. For example, to automatically substitute the
template variable `{{yesterday}}` when inserting a template, you could add this
to your config:

>lua
{
-- other fields ...
templates = {
substitutions = {
yesterday = function()
return os.date("%Y-%m-%d", os.time() - 86400)
end
}
}
<


==============================================================================
4. Known Issues *obsidian-known-issues*
Expand Down
28 changes: 28 additions & 0 deletions lua/obsidian/command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,33 @@ command.check_health = function(client, _)
end
end

command.switch_workspace = function(client, data)
if not data.args or #data.args == 0 then
echo.info(
"Current workspace: " .. client.current_workspace.name .. " @ " .. tostring(client.dir),
client.opts.log_level
)
return
end

local workspace = nil
for _, value in pairs(client.opts.workspaces) do
if data.args == value.name then
workspace = value
end
end

if not workspace then
echo.err("Workspace '" .. data.args .. "' does not exist", client.opts.log_level)
return
end

client.current_workspace = workspace

echo.info("Switching to workspace '" .. workspace.name .. "' (" .. workspace.path .. ")", client.opts.log_level)
client.dir = Path:new(workspace.path)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion here about normalizing:

Suggested change
client.dir = Path:new(workspace.path)
client.dir = Path:new(vim.fs.normalize(tostring(workspace.path)))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment in a previous suggestion

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok fair enough! Could you add a comment here at least:

Suggested change
client.dir = Path:new(workspace.path)
-- NOTE: workspace.path has already been normalized
client.dir = Path:new(workspace.path)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 598bc0a

end

local commands = {
ObsidianCheck = { func = command.check, opts = { nargs = 0 } },
ObsidianTemplate = { func = command.template, opts = { nargs = "?" } },
Expand All @@ -665,6 +692,7 @@ local commands = {
ObsidianLinkNew = { func = command.link_new, opts = { nargs = "?", range = true } },
ObsidianFollowLink = { func = command.follow, opts = { nargs = 0 } },
ObsidianCheckHealth = { func = command.check_health, opts = { nargs = 0 } },
ObsidianWorkspace = { func = command.switch_workspace, opts = { nargs = "?" } },
}

---Register all commands.
Expand Down
11 changes: 8 additions & 3 deletions lua/obsidian/config.lua
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to keep backwards compatibility for when people upgrade. This should be fairly easy:

Just check if dir is set instead of workspaces and convert dir into a workspace object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f422f98

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ local config = {}
---[[ Options specs ]]---

---@class obsidian.config.ClientOpts
---@field dir string
---@field workspaces table
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this has the obisidan.Workspace type, right?

Suggested change
---@field workspaces table
---@field workspaces obsidian.Workspace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a table of obsidian.workspaces:

require('obsidian').setup({
    -- ...

    workspaces = {
        {
            name = 'personal',
            path = '~/Documents/notes/personal',
        },
        {
            name = 'work',
            path = '~/Documents/notes/acsd',
        },
    }
    -- ...
)}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, then we can give it a type hint like this

Suggested change
---@field workspaces table
---@field workspaces obsidian.Workspace[]|?

---@field detect_cwd boolean
---@field log_level integer|?
---@field notes_subdir string|?
---@field templates obsidian.config.TemplateOpts
Expand All @@ -30,7 +31,8 @@ config.ClientOpts = {}
---@return obsidian.config.ClientOpts
config.ClientOpts.default = function()
return {
dir = vim.fs.normalize "./",
workspaces = {},
detect_cwd = false,
log_level = nil,
notes_subdir = nil,
templates = config.TemplateOpts.default(),
Expand Down Expand Up @@ -64,13 +66,16 @@ config.ClientOpts.normalize = function(opts)
opts.mappings = opts.mappings and opts.mappings or config.MappingOpts.default()
opts.daily_notes = vim.tbl_extend("force", config.DailyNotesOpts.default(), opts.daily_notes)
opts.templates = vim.tbl_extend("force", config.TemplateOpts.default(), opts.templates)
opts.dir = vim.fs.normalize(tostring(opts.dir))

-- Validate.
if opts.sort_by ~= nil and not vim.tbl_contains({ "path", "modified", "accessed", "created" }, opts.sort_by) then
echo.err("invalid 'sort_by' option '" .. opts.sort_by .. "'")
end

for key, value in pairs(opts.workspaces) do
opts.workspaces[key].path = vim.fs.normalize(tostring(value.path))
end

return opts
end

Expand Down
8 changes: 6 additions & 2 deletions lua/obsidian/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ obsidian.completion = require "obsidian.completion"
obsidian.note = require "obsidian.note"
obsidian.util = require "obsidian.util"
obsidian.mapping = require "obsidian.mapping"
obsidian.workspace = require "obsidian.workspace"

---@class obsidian.Client
---@field current_workspace obsidian.Workspace
---@field dir Path
---@field templates_dir Path|?
---@field opts obsidian.config.ClientOpts
Expand All @@ -24,7 +26,9 @@ local client = {}
---@return obsidian.Client
obsidian.new = function(opts)
local self = setmetatable({}, { __index = client })
self.dir = Path:new(vim.fs.normalize(tostring(opts.dir and opts.dir or "./")))

self.current_workspace = obsidian.workspace.get_from_opts(opts)
self.dir = Path:new(self.current_workspace.path)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should still normalize dir to avoid breaking changes.

Suggested change
self.dir = Path:new(self.current_workspace.path)
self.dir = Path:new(vim.fs.normalize(tostring(self.current_workspace.path)))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! I finally have time to swing back around on these...

The workspace paths should already be normalized.

Here, in workspace.new(), the path is normalized:

workspace.new = function(name, path)
  local self = setmetatable({}, { __index = workspace })

  self.name = name
  self.path = vim.fs.normalize(path)

  return self
end

Paths are also normalized when reading from the config in client.ClientOpts.normalize():

  for key, value in pairs(opts.workspaces) do
    opts.workspaces[key].path = vim.fs.normalize(tostring(value.path))
  end

There could be an edge-case I haven't fully considered, but I think it's safe to omit the redundant path normalization. However, if you still prefer normalizing the paths where you suggested I won't argue with you 😄

self.opts = opts
self.backlinks_namespace = vim.api.nvim_create_namespace "ObsidianBacklinks"

Expand All @@ -37,7 +41,7 @@ end
---@return obsidian.Client
obsidian.new_from_dir = function(dir)
local opts = config.ClientOpts.default()
opts.dir = vim.fs.normalize(dir)
opts.workspaces = vim.tbl_extend("force", { obsidian.workspace.new("test_vault", dir) }, opts.workspaces)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe name it by the name of the directory instead of "test_vault"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 1e1ba52

return obsidian.new(opts)
end

Expand Down
14 changes: 14 additions & 0 deletions lua/obsidian/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ util.contains = function(table, val)
return false
end

---Check if a table (list) contains a key.
---
---@param table table
---@param needle any
---@return boolean
util.contains_key = function(table, needle)
for key, _ in pairs(table) do
if key == needle then
return true
end
end
return false
end

---Return a new table (list) with only the unique values of the original.
---
---@param table table
Expand Down
Loading