From 41cfd7f17332af2492a14512e43523bcb76849a5 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 21 Jul 2023 15:12:07 -0400 Subject: [PATCH 01/31] feat: support workspaces config option closes: #128 --- doc/obsidian.txt | 306 +++++++++++++--------------------------- lua/obsidian/config.lua | 14 +- lua/obsidian/init.lua | 2 +- 3 files changed, 114 insertions(+), 208 deletions(-) diff --git a/doc/obsidian.txt b/doc/obsidian.txt index d30ab8dae..e247fdcd5 100644 --- a/doc/obsidian.txt +++ b/doc/obsidian.txt @@ -23,10 +23,9 @@ Obsidian app. - ⚙️ |obsidian-setup| - |obsidian-requirements| - |obsidian-install-and-configure| - - |obsidian-plugin-dependencies| - |obsidian-configuration-options| - - |obsidian-notes-on-configuration| - - |obsidian-using-templates| + - |obsidian-templates-support| + - |obsidian-using-nvim-treesitter| - 🐞 |obsidian-known-issues| - ➕ |obsidian-contributing| @@ -35,29 +34,28 @@ Obsidian app. 2. Features *obsidian-features* - ▶️ Autocompletion for note references via nvim-cmp (triggered by typing `[[`) -- 🏃 Optional passthrough for `gf` to enable Obsidian links without interfering with existing functionality +- 🏃 Go to a note buffer with `gf` when cursor is on a reference - 💅 Additional markdown syntax highlighting and concealing for references COMMANDS *obsidian-commands* +- `:ObsidianBacklinks` for getting a location list of references to the current buffer. +- `:ObsidianToday` to create a new daily note. +- `:ObsidianYesterday` to open (eventually creating) the daily note for the previous working day. - `:ObsidianOpen` to open a note in the Obsidian app. This command has one optional argument: the ID, path, or alias of the note to open. If not given, the note corresponding to the current buffer is opened. - `:ObsidianNew` to create a new note. This command has one optional argument: the title of the new note. -- `:ObsidianQuickSwitch` to quickly switch to another notes in your vault, searching by its name using fzf.vim , fzf-lua or telescope.nvim . -- `:ObsidianFollowLink` to follow a note reference under the cursor. -- `:ObsidianBacklinks` for getting a location list of references to the current buffer. -- `:ObsidianToday` to create a new daily note. -- `:ObsidianYesterday` to open (eventually creating) the daily note for the previous working day. -- `:ObsidianTemplate` to insert a template from the templates folder, selecting from a list using telescope.nvim or one of the `fzf` alternatives. - See |obsidian-"using-templates"| for more information. - `:ObsidianSearch` to search for notes in your vault using ripgrep with fzf.vim , fzf-lua or telescope.nvim . This command has one optional argument: a search query to start with. +- `:ObsidianQuickSwitch` to quickly switch to another notes in your vault, searching by its name using fzf.vim , fzf-lua or telescope.nvim . - `:ObsidianLink` to link an in-line visual selection of text to a note. 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. +- `:ObsidianFollowLink` to follow a note reference under the cursor. +- `:ObsidianTemplate` to insert a template from the templates folder, selecting from a list using telescope.nvim or one of the `fzf` alternatives. DEMO *obsidian-demo* @@ -75,20 +73,18 @@ 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 to be installed and on your `$PATH`. See ripgrep#installation for install options. -- If you using WSL, you’ll also need wsl-open Search functionality (e.g. via the `:ObsidianSearch` and `:ObsidianQuickSwitch` commands) also requires telescope.nvim or one of the `fzf` -alternatives (see |obsidian-plugin-dependencies| below). +alternatives (see below). INSTALL AND CONFIGURE *obsidian-install-and-configure* -To configure obsidian.nvim you just need to call `require("obsidian").setup({ +To configure Obsidian.nvim you just need to call `require("obsidian").setup({ ... })` with the desired options. Here are some examples using different plugin -managers. The full set of |obsidian-plugin-dependencies| and -|obsidian-configuration-options| are listed below. +managers. The full set of configuration options are listed |obsidian-below|. USING LAZY.NVIM ~ @@ -97,23 +93,52 @@ 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", - - -- see below for full list of optional dependencies 👇 + + -- Optional, for completion. + "hrsh7th/nvim-cmp", + + -- Optional, for search and quick-switch functionality. + "nvim-telescope/telescope.nvim", + + -- Optional, an alternative to telescope for search and quick-switch functionality. + -- "ibhagwan/fzf-lua" + + -- Optional, another alternative to telescope for search and quick-switch functionality. + -- "junegunn/fzf", + -- "junegunn/fzf.vim" + + -- Optional, alternative to nvim-treesitter for syntax highlighting. + "godlygeek/tabular", + "preservim/vim-markdown", }, opts = { - dir = "~/my-vault", -- no need to call 'vim.fn.expand' here - + -- no need to call 'vim.fn.expand' here + workspaces = { + "~/notes/personal", + "~/notes/work", + } + -- see below for full list of options 👇 }, + config = function(_, opts) + require("obsidian").setup(opts) + + -- Optional, override the 'gf' keymap to utilize Obsidian's search functionality. + -- see also: 'follow_url_func' config option below. + vim.keymap.set("n", "gf", function() + if require("obsidian").util.cursor_on_markdown_link() then + return "ObsidianFollowLink" + else + return "gf" + end + end, { noremap = false, expr = true }) + end, } < @@ -123,16 +148,13 @@ USING PACKER.NVIM ~ >lua use({ "epwalsh/obsidian.nvim", - requires = { - -- Required. - "nvim-lua/plenary.nvim", - - -- see below for full list of optional dependencies 👇 - }, config = function() require("obsidian").setup({ - dir = "~/my-vault", - + workspaces = { + "~/notes/personal", + "~/notes/work", + } + -- see below for full list of options 👇 }) end, @@ -140,22 +162,6 @@ USING PACKER.NVIM ~ < -PLUGIN DEPENDENCIES *obsidian-plugin-dependencies* - -The only required plugin dependency is plenary.nvim -, but there are a number of optional -dependencies that enhance the obsidian.nvim experience: - -- hrsh7th/nvim-cmp : for completion of note references. -- nvim-telescope/telescope.nvim : for search and quick-switch functionality. -- ibhagwan/fzf-lua : an alternative to telescope for search and quick-switch functionality. -- junegunn/fzf and junegunn/fzf.vim : another alternative to telescope for search and quick-switch functionality. -- godlygeek/tabular and preservim/vim-markdown : alternative to nvim-treesitter for syntax highlighting. - -If you choose to use any of these you should include them in the "dependencies" -or "requires" field of the obsidian.nvim plugin spec for your package manager. - - CONFIGURATION OPTIONS *obsidian-configuration-options* This is a complete list of all of the options that can be passed to @@ -164,26 +170,25 @@ This is a complete list of all of the options that can be passed to >lua { -- Required, the path to your vault directory. - dir = "~/my-vault", - + workspaces = { + "~/notes/personal", + "~/notes/work", + } + -- Optional, if you keep notes in a specific subdirectory of your vault. notes_subdir = "notes", - - -- Optional, set the log level for obsidian.nvim. This is an integer corresponding to one of the log + + -- Optional, set the log level for Obsidian. This is an integer corresponding to one of the log -- levels defined by "vim.log.levels.*" or nil, which is equivalent to DEBUG (1). log_level = vim.log.levels.DEBUG, - + 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. completion = { -- If using nvim-cmp, otherwise set to false @@ -193,29 +198,9 @@ This is a complete list of all of the options that can be passed to -- Where to put new notes created from completion. Valid options are -- * "current_dir" - put new notes in same directory as the current buffer. -- * "notes_subdir" - put new notes in the default notes subdirectory. - new_notes_location = "current_dir", - - -- Whether to add the output of the node_id_func to new notes in autocompletion. - -- E.g. "[[Foo" completes to "[[foo|Foo]]" assuming "foo" is the ID of the note. - prepend_note_id = true + new_notes_location = "current_dir" }, - - -- 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 }, - }, - }, - - -- 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. @@ -233,10 +218,10 @@ This is a complete list of all of the options that can be passed to end return tostring(os.time()) .. "-" .. suffix end, - - -- Optional, set to true if you don't want obsidian.nvim to manage frontmatter. + + -- Optional, set to true if you don't want Obsidian to manage frontmatter. disable_frontmatter = false, - + -- Optional, alternatively you can customize the frontmatter data. note_frontmatter_func = function(note) -- This is equivalent to the default frontmatter function. @@ -250,24 +235,14 @@ This is a complete list of all of the options that can be passed to end return out end, - + -- 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 -- URL it will be ignored but you can customize this behavior here. follow_url_func = function(url) @@ -275,14 +250,14 @@ This is a complete list of all of the options that can be passed to vim.fn.jobstart({"open", url}) -- Mac OS -- vim.fn.jobstart({"xdg-open", url}) -- linux end, - + -- Optional, set to true if you use the Obsidian Advanced URI plugin. -- https://github.com/Vinzent03/obsidian-advanced-uri use_advanced_uri = true, - + -- Optional, set to true to force ':ObsidianOpen' to bring the app to the foreground. open_app_foreground = false, - + -- Optional, by default commands like `:ObsidianSearch` will attempt to use -- telescope.nvim, fzf-lua, and fzf.nvim (in that order), and use the -- first one they find. By setting this option to your preferred @@ -291,12 +266,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" @@ -304,88 +273,14 @@ This is a complete list of all of the options that can be passed to } < +**❗ Notes** -NOTES ON CONFIGURATION *obsidian-notes-on-configuration* - - -COMPLETION ~ - -obsidian.nvim will set itself up as an nvim-cmp source automatically when you -enter a markdown buffer within your vault directory, you do **not** need to -specify this plugin as a cmp source manually. - - -SYNTAX HIGHLIGHTING ~ - -If you use `vim-markdown` you’ll probably want to disable its frontmatter -syntax highlighting (`vim.g.vim_markdown_frontmatter = 1`) which I’ve found -doesn’t work very well. - -If you’re using nvim-treesitter - and -not vim-markdown , you’ll probably -want to enable `additional_vim_regex_highlighting` for markdown to benefit from -obsidian.nvim’s extra syntax improvements: - ->lua - require("nvim-treesitter.configs").setup({ - ensure_installed = { "markdown", "markdown_inline", ... }, - highlight = { - enable = true, - additional_vim_regex_highlighting = { "markdown" }, - }, - }) -< - - -NOTE NAMING AND LOCATION ~ - -The `notes_subdir` and `note_id_func` options are not mutually exclusive. You -can use them both. For example, using a combination of both of the above -settings, a new note called "My new note" will assigned a path like -`notes/1657296016-my-new-note.md`. - - -GF PASSTHROUGH ~ +- Obsidian.nvim will set itself up as an nvim-cmp source automatically when you enter a markdown buffer within your vault directory, you do **not** need to specify this plugin as a cmp source manually. +- If you use `vim-markdown` you’ll probably want to disable its frontmatter syntax highlighting (`vim.g.vim_markdown_frontmatter = 1`) which I’ve found doesn’t work very well. +- The `notes_subdir` and `note_id_func` options are not mutually exclusive. You can use them both. For example, using a combination of both of the above settings, a new note called "My new note" will assigned a path like `notes/1657296016-my-new-note.md`. -If you want the `gf` passthrough functionality but you’ve already overridden -the `gf` keybinding, just change your `gf` mapping definition to something like -this: ->lua - vim.keymap.set("n", "gf", function() - if require("obsidian").util.cursor_on_markdown_link() then - return "ObsidianFollowLink" - else - return "gf" - end - end, { noremap = false, expr = true }) -< - -Then make sure to comment out the `gf` keybinding in your obsidian.nvim config: - ->lua - mappings = { - -- ["gf"] = ... - }, -< - -Or alternatively you could map obsidian.nvim’s follow functionality to a -different key: - ->lua - mappings = { - ["fo"] = { - action = function() - return require("obsidian").util.gf_passthrough() - end, - opts = { noremap = false, expr = true, buffer = true }, - }, - }, -< - - -USING TEMPLATES *obsidian-using-templates* +TEMPLATES SUPPORT *obsidian-templates-support* To insert a template, run the command `:ObsidianTemplate`. This will open telescope.nvim or one of the @@ -398,11 +293,11 @@ For example, with the following configuration >lua { -- other fields ... - + templates = { subdir = "my-templates-folder", date_format = "%Y-%m-%d-%a", - time_format = "%H:%M", + time_format = "%H:%M" }, } < @@ -411,7 +306,6 @@ and the file `~/my-vault/my-templates-folder/note template.md`: >markdown # {{title}} - Date created: {{date}} < @@ -420,27 +314,29 @@ will insert >markdown # Configuring Neovim - + Date created: 2023-03-01-Wed < 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: + +USING NVIM-TREESITTER *obsidian-using-nvim-treesitter* + +If you’re using nvim-treesitter + and +not vim-markdown , you’ll probably +want to enable `additional_vim_regex_highlighting` for markdown to benefit from +Obsidian.nvim’s extra syntax improvements: >lua - { - -- other fields ... - templates = { - substitutions = { - yesterday = function() - return os.date("%Y-%m-%d", os.time() - 86400) - end - } - } + require("nvim-treesitter.configs").setup({ + ensure_installed = { "markdown", "markdown_inline", ... }, + highlight = { + enable = true, + additional_vim_regex_highlighting = { "markdown" }, + }, + }) < diff --git a/lua/obsidian/config.lua b/lua/obsidian/config.lua index f37c6a19a..4cd4a8898 100644 --- a/lua/obsidian/config.lua +++ b/lua/obsidian/config.lua @@ -5,6 +5,7 @@ local config = {} ---[[ Options specs ]]--- ---@class obsidian.config.ClientOpts +---@field workspaces table ---@field dir string ---@field log_level integer|? ---@field notes_subdir string|? @@ -30,7 +31,8 @@ config.ClientOpts = {} ---@return obsidian.config.ClientOpts config.ClientOpts.default = function() return { - dir = vim.fs.normalize "./", + workspaces = { "./" }, + dir = nil, log_level = nil, notes_subdir = nil, templates = config.TemplateOpts.default(), @@ -64,13 +66,21 @@ 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 _, value in pairs(opts.workspaces) do + local normalized_dir = vim.fs.normalize(tostring(value)) + if normalized_dir == vim.fn.getcwd() then + opts.dir = normalized_dir + break; + end + end + + print('dir: ' .. tostring(opts.dir)) return opts end diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 855aab785..7556d7965 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -37,7 +37,7 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - opts.dir = vim.fs.normalize(dir) + vim.tbl_extend('force', opts.workspaces, { vim.fs.normalize(dir) }) return obsidian.new(opts) end From 070ef9fb28e39c76520f33d6208a74a6def55654 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 21 Jul 2023 17:46:26 -0400 Subject: [PATCH 02/31] chore: revert changes to doc/obsidian.txt --- doc/obsidian.txt | 60 ++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/doc/obsidian.txt b/doc/obsidian.txt index e247fdcd5..c0bf78ef2 100644 --- a/doc/obsidian.txt +++ b/doc/obsidian.txt @@ -99,36 +99,32 @@ USING LAZY.NVIM ~ dependencies = { -- Required. "nvim-lua/plenary.nvim", - + -- Optional, for completion. "hrsh7th/nvim-cmp", - + -- Optional, for search and quick-switch functionality. "nvim-telescope/telescope.nvim", - + -- Optional, an alternative to telescope for search and quick-switch functionality. -- "ibhagwan/fzf-lua" - + -- Optional, another alternative to telescope for search and quick-switch functionality. -- "junegunn/fzf", -- "junegunn/fzf.vim" - + -- Optional, alternative to nvim-treesitter for syntax highlighting. "godlygeek/tabular", "preservim/vim-markdown", }, opts = { - -- no need to call 'vim.fn.expand' here - workspaces = { - "~/notes/personal", - "~/notes/work", - } - + dir = "~/my-vault", -- no need to call 'vim.fn.expand' here + -- see below for full list of options 👇 }, config = function(_, opts) require("obsidian").setup(opts) - + -- Optional, override the 'gf' keymap to utilize Obsidian's search functionality. -- see also: 'follow_url_func' config option below. vim.keymap.set("n", "gf", function() @@ -150,11 +146,8 @@ USING PACKER.NVIM ~ "epwalsh/obsidian.nvim", config = function() require("obsidian").setup({ - workspaces = { - "~/notes/personal", - "~/notes/work", - } - + dir = "~/my-vault", + -- see below for full list of options 👇 }) end, @@ -170,25 +163,22 @@ This is a complete list of all of the options that can be passed to >lua { -- Required, the path to your vault directory. - workspaces = { - "~/notes/personal", - "~/notes/work", - } - + dir = "~/my-vault", + -- Optional, if you keep notes in a specific subdirectory of your vault. notes_subdir = "notes", - + -- Optional, set the log level for Obsidian. This is an integer corresponding to one of the log -- levels defined by "vim.log.levels.*" or nil, which is equivalent to DEBUG (1). log_level = vim.log.levels.DEBUG, - + 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 daily notes. date_format = "%Y-%m-%d" }, - + -- Optional, completion. completion = { -- If using nvim-cmp, otherwise set to false @@ -200,7 +190,7 @@ This is a complete list of all of the options that can be passed to -- * "notes_subdir" - put new notes in the default notes subdirectory. new_notes_location = "current_dir" }, - + -- 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. @@ -218,10 +208,10 @@ This is a complete list of all of the options that can be passed to end return tostring(os.time()) .. "-" .. suffix end, - + -- Optional, set to true if you don't want Obsidian to manage frontmatter. disable_frontmatter = false, - + -- Optional, alternatively you can customize the frontmatter data. note_frontmatter_func = function(note) -- This is equivalent to the default frontmatter function. @@ -235,14 +225,14 @@ This is a complete list of all of the options that can be passed to end return out end, - + -- Optional, for templates (see below). templates = { subdir = "templates", date_format = "%Y-%m-%d-%a", time_format = "%H:%M", }, - + -- Optional, by default when you use `:ObsidianFollowLink` on a link to an external -- URL it will be ignored but you can customize this behavior here. follow_url_func = function(url) @@ -250,14 +240,14 @@ This is a complete list of all of the options that can be passed to vim.fn.jobstart({"open", url}) -- Mac OS -- vim.fn.jobstart({"xdg-open", url}) -- linux end, - + -- Optional, set to true if you use the Obsidian Advanced URI plugin. -- https://github.com/Vinzent03/obsidian-advanced-uri use_advanced_uri = true, - + -- Optional, set to true to force ':ObsidianOpen' to bring the app to the foreground. open_app_foreground = false, - + -- Optional, by default commands like `:ObsidianSearch` will attempt to use -- telescope.nvim, fzf-lua, and fzf.nvim (in that order), and use the -- first one they find. By setting this option to your preferred @@ -293,7 +283,7 @@ For example, with the following configuration >lua { -- other fields ... - + templates = { subdir = "my-templates-folder", date_format = "%Y-%m-%d-%a", @@ -314,7 +304,7 @@ will insert >markdown # Configuring Neovim - + Date created: 2023-03-01-Wed < From 2aaf7ffd833038d2f70682f8e9414fd525e35a3a Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Wed, 2 Aug 2023 21:35:22 -0400 Subject: [PATCH 03/31] refactor: default_workspace and workspaces table - if the cwd matches directory in `workspaces` table use it as the vault dir (`client.dir`) (#128) - if cwd does not match any value in `workspaces`, use cwd as the vault dir (#119) - rename `dir` to `default_workspace - contains an index into the `workspaces` table as a fallback --- lua/obsidian/config.lua | 15 +++++---------- lua/obsidian/init.lua | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lua/obsidian/config.lua b/lua/obsidian/config.lua index 4cd4a8898..eecf45c9a 100644 --- a/lua/obsidian/config.lua +++ b/lua/obsidian/config.lua @@ -6,7 +6,7 @@ local config = {} ---@class obsidian.config.ClientOpts ---@field workspaces table ----@field dir string +---@field default_workspace string ---@field log_level integer|? ---@field notes_subdir string|? ---@field templates obsidian.config.TemplateOpts @@ -31,8 +31,8 @@ config.ClientOpts = {} ---@return obsidian.config.ClientOpts config.ClientOpts.default = function() return { - workspaces = { "./" }, - dir = nil, + workspaces = {}, + default_workspace = nil, log_level = nil, notes_subdir = nil, templates = config.TemplateOpts.default(), @@ -72,15 +72,10 @@ config.ClientOpts.normalize = function(opts) echo.err("invalid 'sort_by' option '" .. opts.sort_by .. "'") end - for _, value in pairs(opts.workspaces) do - local normalized_dir = vim.fs.normalize(tostring(value)) - if normalized_dir == vim.fn.getcwd() then - opts.dir = normalized_dir - break; - end + for key, value in pairs(opts.workspaces) do + opts.workspaces[key] = vim.fs.normalize(tostring(value)) end - print('dir: ' .. tostring(opts.dir)) return opts end diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 7556d7965..893d712a6 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -24,7 +24,27 @@ 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 "./"))) + local cwd = vim.fn.getcwd() + local current_workspace = nil + if not opts.default_workspace then + for key, value in pairs(opts.workspaces) do + if value == cwd then + current_workspace = key + break + end + end + else + current_workspace = opts.default_workspace + end + + if not current_workspace then + current_workspace = '.' + opts.workspaces[current_workspace] = cwd + end + + echo.info('current_workspace: ' .. current_workspace .. ' @ ' .. opts.workspaces[current_workspace], opts.log_level) + + self.dir = Path:new(opts.workspaces[current_workspace]) self.opts = opts self.backlinks_namespace = vim.api.nvim_create_namespace "ObsidianBacklinks" From d3313a3d6676ddff06a1933c144158980472433a Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Wed, 2 Aug 2023 21:40:54 -0400 Subject: [PATCH 04/31] feat: add command to switch workspace related to: #60 --- lua/obsidian/command.lua | 11 +++++++++++ lua/obsidian/util.lua | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index 9144dd679..9c7dedf9d 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -651,6 +651,16 @@ command.check_health = function(client, _) end end +command.switch_workspace = function(client, data) + if not util.contains_key(client.opts.workspaces, data.args) then + echo.err("Workspace '" .. data.args .. "' does not exist", client.opts.log_level); + return + end + + echo.info("Switching to workspace '" .. data.args .. "' (" .. client.opts.workspaces[data.args] .. ")") + client.dir = client.opts.workspaces[data.args] +end + local commands = { ObsidianCheck = { func = command.check, opts = { nargs = 0 } }, ObsidianTemplate = { func = command.template, opts = { nargs = "?" } }, @@ -665,6 +675,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 = 1 } }, } ---Register all commands. diff --git a/lua/obsidian/util.lua b/lua/obsidian/util.lua index 828efcb73..b13ffb308 100644 --- a/lua/obsidian/util.lua +++ b/lua/obsidian/util.lua @@ -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 From ee7a3f786675010c00bef57884b9dde583c5dff2 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Wed, 2 Aug 2023 21:50:22 -0400 Subject: [PATCH 05/31] chore: run stylua --- lua/obsidian/command.lua | 2 +- lua/obsidian/init.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index 9c7dedf9d..9c3ddc48a 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -653,7 +653,7 @@ end command.switch_workspace = function(client, data) if not util.contains_key(client.opts.workspaces, data.args) then - echo.err("Workspace '" .. data.args .. "' does not exist", client.opts.log_level); + echo.err("Workspace '" .. data.args .. "' does not exist", client.opts.log_level) return end diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 893d712a6..311a6175b 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -38,11 +38,11 @@ obsidian.new = function(opts) end if not current_workspace then - current_workspace = '.' + current_workspace = "." opts.workspaces[current_workspace] = cwd end - echo.info('current_workspace: ' .. current_workspace .. ' @ ' .. opts.workspaces[current_workspace], opts.log_level) + echo.info("current_workspace: " .. current_workspace .. " @ " .. opts.workspaces[current_workspace], opts.log_level) self.dir = Path:new(opts.workspaces[current_workspace]) self.opts = opts @@ -57,7 +57,7 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - vim.tbl_extend('force', opts.workspaces, { vim.fs.normalize(dir) }) + vim.tbl_extend("force", opts.workspaces, { vim.fs.normalize(dir) }) return obsidian.new(opts) end From 9c1013281ba9951539894b2d90e9b34f48ffbbba Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Wed, 2 Aug 2023 21:51:31 -0400 Subject: [PATCH 06/31] chore: update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a88444f26..45b836d1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 From 731fd4978689cf65d68fe7d7a47fc1e9dcf82fcb Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 16:05:15 -0400 Subject: [PATCH 07/31] fix: not setting workspace in new_from_dir --- lua/obsidian/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 311a6175b..8fc7bf601 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -57,7 +57,8 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - vim.tbl_extend("force", opts.workspaces, { vim.fs.normalize(dir) }) + opts.workspaces = vim.tbl_extend("force", opts.workspaces, { test_vault = vim.fs.normalize(dir) }) + opts.default_workspace = "test_vault" return obsidian.new(opts) end From 8d71474e3b8ba948eadafd632fdc78d33edc8366 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 16:19:55 -0400 Subject: [PATCH 08/31] chore: sync doc changes with main --- doc/obsidian.txt | 193 ++++++++++++++++++++++++++++++----------------- 1 file changed, 125 insertions(+), 68 deletions(-) diff --git a/doc/obsidian.txt b/doc/obsidian.txt index c0bf78ef2..964a9f6b9 100644 --- a/doc/obsidian.txt +++ b/doc/obsidian.txt @@ -23,9 +23,10 @@ Obsidian app. - ⚙️ |obsidian-setup| - |obsidian-requirements| - |obsidian-install-and-configure| + - |obsidian-plugin-dependencies| - |obsidian-configuration-options| - - |obsidian-templates-support| - - |obsidian-using-nvim-treesitter| + - |obsidian-notes-on-configuration| + - |obsidian-using-templates| - 🐞 |obsidian-known-issues| - ➕ |obsidian-contributing| @@ -34,28 +35,29 @@ Obsidian app. 2. Features *obsidian-features* - ▶️ Autocompletion for note references via nvim-cmp (triggered by typing `[[`) -- 🏃 Go to a note buffer with `gf` when cursor is on a reference +- 🏃 Optional passthrough for `gf` to enable Obsidian links without interfering with existing functionality - 💅 Additional markdown syntax highlighting and concealing for references COMMANDS *obsidian-commands* -- `:ObsidianBacklinks` for getting a location list of references to the current buffer. -- `:ObsidianToday` to create a new daily note. -- `:ObsidianYesterday` to open (eventually creating) the daily note for the previous working day. - `:ObsidianOpen` to open a note in the Obsidian app. This command has one optional argument: the ID, path, or alias of the note to open. If not given, the note corresponding to the current buffer is opened. - `:ObsidianNew` to create a new note. This command has one optional argument: the title of the new note. +- `:ObsidianQuickSwitch` to quickly switch to another notes in your vault, searching by its name using fzf.vim , fzf-lua or telescope.nvim . +- `:ObsidianFollowLink` to follow a note reference under the cursor. +- `:ObsidianBacklinks` for getting a location list of references to the current buffer. +- `:ObsidianToday` to create a new daily note. +- `:ObsidianYesterday` to open (eventually creating) the daily note for the previous working day. +- `:ObsidianTemplate` to insert a template from the templates folder, selecting from a list using telescope.nvim or one of the `fzf` alternatives. + See |obsidian-"using-templates"| for more information. - `:ObsidianSearch` to search for notes in your vault using ripgrep with fzf.vim , fzf-lua or telescope.nvim . This command has one optional argument: a search query to start with. -- `:ObsidianQuickSwitch` to quickly switch to another notes in your vault, searching by its name using fzf.vim , fzf-lua or telescope.nvim . - `:ObsidianLink` to link an in-line visual selection of text to a note. 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. -- `:ObsidianFollowLink` to follow a note reference under the cursor. -- `:ObsidianTemplate` to insert a template from the templates folder, selecting from a list using telescope.nvim or one of the `fzf` alternatives. DEMO *obsidian-demo* @@ -77,14 +79,15 @@ REQUIREMENTS *obsidian-requirements* Search functionality (e.g. via the `:ObsidianSearch` and `:ObsidianQuickSwitch` commands) also requires telescope.nvim or one of the `fzf` -alternatives (see below). +alternatives (see |obsidian-plugin-dependencies| below). INSTALL AND CONFIGURE *obsidian-install-and-configure* -To configure Obsidian.nvim you just need to call `require("obsidian").setup({ +To configure obsidian.nvim you just need to call `require("obsidian").setup({ ... })` with the desired options. Here are some examples using different plugin -managers. The full set of configuration options are listed |obsidian-below|. +managers. The full set of |obsidian-plugin-dependencies| and +|obsidian-configuration-options| are listed below. USING LAZY.NVIM ~ @@ -100,41 +103,13 @@ USING LAZY.NVIM ~ -- Required. "nvim-lua/plenary.nvim", - -- Optional, for completion. - "hrsh7th/nvim-cmp", - - -- Optional, for search and quick-switch functionality. - "nvim-telescope/telescope.nvim", - - -- Optional, an alternative to telescope for search and quick-switch functionality. - -- "ibhagwan/fzf-lua" - - -- Optional, another alternative to telescope for search and quick-switch functionality. - -- "junegunn/fzf", - -- "junegunn/fzf.vim" - - -- Optional, alternative to nvim-treesitter for syntax highlighting. - "godlygeek/tabular", - "preservim/vim-markdown", + -- see below for full list of optional dependencies 👇 }, opts = { dir = "~/my-vault", -- no need to call 'vim.fn.expand' here -- see below for full list of options 👇 }, - config = function(_, opts) - require("obsidian").setup(opts) - - -- Optional, override the 'gf' keymap to utilize Obsidian's search functionality. - -- see also: 'follow_url_func' config option below. - vim.keymap.set("n", "gf", function() - if require("obsidian").util.cursor_on_markdown_link() then - return "ObsidianFollowLink" - else - return "gf" - end - end, { noremap = false, expr = true }) - end, } < @@ -144,6 +119,12 @@ USING PACKER.NVIM ~ >lua use({ "epwalsh/obsidian.nvim", + requires = { + -- Required. + "nvim-lua/plenary.nvim", + + -- see below for full list of optional dependencies 👇 + }, config = function() require("obsidian").setup({ dir = "~/my-vault", @@ -155,6 +136,22 @@ USING PACKER.NVIM ~ < +PLUGIN DEPENDENCIES *obsidian-plugin-dependencies* + +The only required plugin dependency is plenary.nvim +, but there are a number of optional +dependencies that enhance the obsidian.nvim experience: + +- hrsh7th/nvim-cmp : for completion of note references. +- nvim-telescope/telescope.nvim : for search and quick-switch functionality. +- ibhagwan/fzf-lua : an alternative to telescope for search and quick-switch functionality. +- junegunn/fzf and junegunn/fzf.vim : another alternative to telescope for search and quick-switch functionality. +- godlygeek/tabular and preservim/vim-markdown : alternative to nvim-treesitter for syntax highlighting. + +If you choose to use any of these you should include them in the "dependencies" +or "requires" field of the obsidian.nvim plugin spec for your package manager. + + CONFIGURATION OPTIONS *obsidian-configuration-options* This is a complete list of all of the options that can be passed to @@ -168,7 +165,7 @@ This is a complete list of all of the options that can be passed to -- Optional, if you keep notes in a specific subdirectory of your vault. notes_subdir = "notes", - -- Optional, set the log level for Obsidian. This is an integer corresponding to one of the log + -- Optional, set the log level for obsidian.nvim. This is an integer corresponding to one of the log -- levels defined by "vim.log.levels.*" or nil, which is equivalent to DEBUG (1). log_level = vim.log.levels.DEBUG, @@ -188,7 +185,17 @@ This is a complete list of all of the options that can be passed to -- Where to put new notes created from completion. Valid options are -- * "current_dir" - put new notes in same directory as the current buffer. -- * "notes_subdir" - put new notes in the default notes subdirectory. - new_notes_location = "current_dir" + new_notes_location = "current_dir", + + -- Whether to add the output of the node_id_func to new notes in autocompletion. + -- E.g. "[[Foo" completes to "[[foo|Foo]]" assuming "foo" is the ID of the note. + prepend_note_id = true + }, + + -- Optional, key mappings. + mappings = { + -- Overrides the 'gf' mapping to work on markdown/wiki links within your vault. + ["gf"] = require("obsidian.mapping").gf_passthrough(), }, -- Optional, customize how names/IDs for new notes are created. @@ -209,7 +216,7 @@ This is a complete list of all of the options that can be passed to return tostring(os.time()) .. "-" .. suffix end, - -- Optional, set to true if you don't want Obsidian to manage frontmatter. + -- Optional, set to true if you don't want obsidian.nvim to manage frontmatter. disable_frontmatter = false, -- Optional, alternatively you can customize the frontmatter data. @@ -263,14 +270,83 @@ This is a complete list of all of the options that can be passed to } < -**❗ Notes** -- Obsidian.nvim will set itself up as an nvim-cmp source automatically when you enter a markdown buffer within your vault directory, you do **not** need to specify this plugin as a cmp source manually. -- If you use `vim-markdown` you’ll probably want to disable its frontmatter syntax highlighting (`vim.g.vim_markdown_frontmatter = 1`) which I’ve found doesn’t work very well. -- The `notes_subdir` and `note_id_func` options are not mutually exclusive. You can use them both. For example, using a combination of both of the above settings, a new note called "My new note" will assigned a path like `notes/1657296016-my-new-note.md`. +NOTES ON CONFIGURATION *obsidian-notes-on-configuration* + + +COMPLETION ~ + +obsidian.nvim will set itself up as an nvim-cmp source automatically when you +enter a markdown buffer within your vault directory, you do **not** need to +specify this plugin as a cmp source manually. + + +SYNTAX HIGHLIGHTING ~ + +If you use `vim-markdown` you’ll probably want to disable its frontmatter +syntax highlighting (`vim.g.vim_markdown_frontmatter = 1`) which I’ve found +doesn’t work very well. + +If you’re using nvim-treesitter + and +not vim-markdown , you’ll probably +want to enable `additional_vim_regex_highlighting` for markdown to benefit from +obsidian.nvim’s extra syntax improvements: + +>lua + require("nvim-treesitter.configs").setup({ + ensure_installed = { "markdown", "markdown_inline", ... }, + highlight = { + enable = true, + additional_vim_regex_highlighting = { "markdown" }, + }, + }) +< + + +NOTE NAMING AND LOCATION ~ +The `notes_subdir` and `note_id_func` options are not mutually exclusive. You +can use them both. For example, using a combination of both of the above +settings, a new note called "My new note" will assigned a path like +`notes/1657296016-my-new-note.md`. -TEMPLATES SUPPORT *obsidian-templates-support* + +GF PASSTHROUGH ~ + +If you want the `gf` passthrough functionality but you’ve already overridden +the `gf` keybinding, just change your `gf` mapping definition to something like +this: + +>lua + vim.keymap.set("n", "gf", function() + if require("obsidian").util.cursor_on_markdown_link() then + return "ObsidianFollowLink" + else + return "gf" + end + end, { noremap = false, expr = true }) +< + +Then make sure to comment out the `gf` keybinding in your obsidian.nvim config: + +>lua + mappings = { + -- ["gf"] = require("obsidian.mapping").gf_passthrough(), + }, +< + +Or alternatively you could map obsidian.nvim’s follow functionality to a +different key: + +>lua + mappings = { + ["fo"] = require("obsidian.mapping").gf_passthrough(), + }, +< + + +USING TEMPLATES *obsidian-using-templates* To insert a template, run the command `:ObsidianTemplate`. This will open telescope.nvim or one of the @@ -311,25 +387,6 @@ will insert above the cursor position. -USING NVIM-TREESITTER *obsidian-using-nvim-treesitter* - -If you’re using nvim-treesitter - and -not vim-markdown , you’ll probably -want to enable `additional_vim_regex_highlighting` for markdown to benefit from -Obsidian.nvim’s extra syntax improvements: - ->lua - require("nvim-treesitter.configs").setup({ - ensure_installed = { "markdown", "markdown_inline", ... }, - highlight = { - enable = true, - additional_vim_regex_highlighting = { "markdown" }, - }, - }) -< - - ============================================================================== 4. Known Issues *obsidian-known-issues* From c5a3a8fc9a240edc13d7d3d47438bd854d85aae8 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 17:30:39 -0400 Subject: [PATCH 09/31] chore: maintain current workspace in client --- lua/obsidian/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 8fc7bf601..6affbf639 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -12,6 +12,7 @@ obsidian.util = require "obsidian.util" obsidian.mapping = require "obsidian.mapping" ---@class obsidian.Client +---@field current_workspace string ---@field dir Path ---@field templates_dir Path|? ---@field opts obsidian.config.ClientOpts @@ -42,8 +43,7 @@ obsidian.new = function(opts) opts.workspaces[current_workspace] = cwd end - echo.info("current_workspace: " .. current_workspace .. " @ " .. opts.workspaces[current_workspace], opts.log_level) - + self.current_workspace = current_workspace self.dir = Path:new(opts.workspaces[current_workspace]) self.opts = opts self.backlinks_namespace = vim.api.nvim_create_namespace "ObsidianBacklinks" From 170dddba9380ba3c54e3aaac708b24a25577ec02 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 17:30:53 -0400 Subject: [PATCH 10/31] refactor: workspace command changes - when called with no arguments, print the current workspace name and path - when called with a single argument, try to switch to the workspace --- lua/obsidian/command.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index 9c3ddc48a..01487ac12 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -652,13 +652,18 @@ command.check_health = function(client, _) end command.switch_workspace = function(client, data) + if not data.args or #data.args == 0 then + echo.info("Current workspace: " .. client.current_workspace .. ' @ ' .. tostring(client.dir), client.opts.log_level) + return + end + if not util.contains_key(client.opts.workspaces, data.args) then echo.err("Workspace '" .. data.args .. "' does not exist", client.opts.log_level) return end - echo.info("Switching to workspace '" .. data.args .. "' (" .. client.opts.workspaces[data.args] .. ")") - client.dir = client.opts.workspaces[data.args] + echo.info("Switching to workspace '" .. data.args .. "' (" .. client.opts.workspaces[data.args] .. ")", client.opts.log_level) + client.dir = Path:new(client.opts.workspaces[data.args]) end local commands = { @@ -675,7 +680,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 = 1 } }, + ObsidianWorkspace = { func = command.switch_workspace, opts = { nargs = "?" } }, } ---Register all commands. From 9c190462785dfc1c4f9b3fcbd2fc2235a950d985 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 17:32:52 -0400 Subject: [PATCH 11/31] chore: commit stylua changes --- lua/obsidian/command.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index 01487ac12..16db9b637 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -653,7 +653,7 @@ end command.switch_workspace = function(client, data) if not data.args or #data.args == 0 then - echo.info("Current workspace: " .. client.current_workspace .. ' @ ' .. tostring(client.dir), client.opts.log_level) + echo.info("Current workspace: " .. client.current_workspace .. " @ " .. tostring(client.dir), client.opts.log_level) return end @@ -662,7 +662,10 @@ command.switch_workspace = function(client, data) return end - echo.info("Switching to workspace '" .. data.args .. "' (" .. client.opts.workspaces[data.args] .. ")", client.opts.log_level) + echo.info( + "Switching to workspace '" .. data.args .. "' (" .. client.opts.workspaces[data.args] .. ")", + client.opts.log_level + ) client.dir = Path:new(client.opts.workspaces[data.args]) end From 591a116f0408b8a3e03d043e424ddf4fc897fe0e Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 18:11:02 -0400 Subject: [PATCH 12/31] chore: update readme --- README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6e18b9cc..5da4ed6d5 100644 --- a/README.md +++ b/README.md @@ -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 @@ -86,7 +87,11 @@ return { -- see below for full list of optional dependencies 👇 }, opts = { - dir = "~/my-vault", -- no need to call 'vim.fn.expand' here + workspaces = { + personal = "~/vaults/personal", -- no need to call 'vim.fn.expand' here + work = "~/vaults/work", + }, + default_workspace = personal, -- see below for full list of options 👇 }, @@ -106,7 +111,11 @@ use({ }, config = function() require("obsidian").setup({ - dir = "~/my-vault", + workspaces = { + personal = "~/vaults/personal", -- no need to call 'vim.fn.expand' here + work = "~/vaults/work", + }, + default_workspace = personal, -- see below for full list of options 👇 }) @@ -132,8 +141,14 @@ 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 = { + personal = "~/vaults/personal", + work = "~/vaults/work", + }, + + -- Optional, default vault to open when the plugin loads + default_workspace = personal, -- Optional, if you keep notes in a specific subdirectory of your vault. notes_subdir = "notes", From d780e10af5b9b0dd0a4666a3305b983a28c3a36f Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Fri, 4 Aug 2023 18:40:36 -0400 Subject: [PATCH 13/31] fix: default_workspace type in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5da4ed6d5..ebddc88c3 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ return { personal = "~/vaults/personal", -- no need to call 'vim.fn.expand' here work = "~/vaults/work", }, - default_workspace = personal, + default_workspace = 'personal', -- see below for full list of options 👇 }, @@ -115,7 +115,7 @@ use({ personal = "~/vaults/personal", -- no need to call 'vim.fn.expand' here work = "~/vaults/work", }, - default_workspace = personal, + default_workspace = 'personal', -- see below for full list of options 👇 }) @@ -147,8 +147,8 @@ This is a complete list of all of the options that can be passed to `require("ob work = "~/vaults/work", }, - -- Optional, default vault to open when the plugin loads - default_workspace = personal, + -- Optional, name of the default vault to open when the plugin loads + default_workspace = 'personal', -- Optional, if you keep notes in a specific subdirectory of your vault. notes_subdir = "notes", From acd5f2304c646089049d405768c2765df0b705f9 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 19:36:00 -0400 Subject: [PATCH 14/31] chore: workspace class --- lua/obsidian/workspace.lua | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 lua/obsidian/workspace.lua diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua new file mode 100644 index 000000000..e45a157a1 --- /dev/null +++ b/lua/obsidian/workspace.lua @@ -0,0 +1,71 @@ +---@class obsidian.Workspace +---@field name string +---@field path string +---@return obsidian.Workspace +local workspace = {} + +---Create a new workspace +--- +---@param name string +---@param path string +--- +---@return obsidian.Workspace +workspace.new = function(name, path) + local self = setmetatable({}, { __index = workspace }) + + self.name = name + self.path = vim.fs.normalize(path) + + return self +end + +workspace.new_from_cwd = function() + return workspace.new(".", vim.fn.getcwd()) +end + +---Determines if cwd is a workspace +--- +---@param workspaces table +---@return obsidian.Workspace|nil +workspace.get_workspace_from_cwd = function(workspaces) + local cwd = vim.fn.getcwd() + local _, value = next(vim.tbl_filter(function(w) + if w.path == cwd then + return true + end + return false + end, workspaces)) + + return value +end + +---Returns the default workspace +--- +---@param workspaces table +---@return obsidian.Workspace|nil +workspace.get_default_workspace = function(workspaces) + local _, value = next(workspaces) + return value +end + +---Resolves current workspace from client config +--- +---@param opts obsidian.config.ClientOpts +---@return obsidian.Workspace +workspace.get_from_opts = function(opts) + local current_workspace = nil + + if opts.detect_cwd then + current_workspace = workspace.get_workspace_from_cwd(opts.workspaces) + else + current_workspace = workspace.get_default_workspace(opts.workspaces) + end + + if not current_workspace then + current_workspace = workspace.new_from_cwd() + end + + return current_workspace +end + +return workspace From a5931ba7ea496dbf77d961ef9f312a6a5caafb31 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 19:36:25 -0400 Subject: [PATCH 15/31] refactor: use workspace class --- lua/obsidian/command.lua | 17 +++++++++++++---- lua/obsidian/config.lua | 6 +++--- lua/obsidian/init.lua | 28 ++++++---------------------- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index 16db9b637..f9457aac7 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -653,20 +653,29 @@ end command.switch_workspace = function(client, data) if not data.args or #data.args == 0 then - echo.info("Current workspace: " .. client.current_workspace .. " @ " .. tostring(client.dir), client.opts.log_level) + echo.info("Current workspace: " .. client.current_workspace.name .. " @ " .. tostring(client.dir), client.opts.log_level) return end - if not util.contains_key(client.opts.workspaces, data.args) then + 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 '" .. data.args .. "' (" .. client.opts.workspaces[data.args] .. ")", + "Switching to workspace '" .. workspace.name .. "' (" .. workspace.path .. ")", client.opts.log_level ) - client.dir = Path:new(client.opts.workspaces[data.args]) + client.dir = Path:new(workspace.path) end local commands = { diff --git a/lua/obsidian/config.lua b/lua/obsidian/config.lua index eecf45c9a..6897e281d 100644 --- a/lua/obsidian/config.lua +++ b/lua/obsidian/config.lua @@ -6,7 +6,7 @@ local config = {} ---@class obsidian.config.ClientOpts ---@field workspaces table ----@field default_workspace string +---@field detect_cwd boolean ---@field log_level integer|? ---@field notes_subdir string|? ---@field templates obsidian.config.TemplateOpts @@ -32,7 +32,7 @@ config.ClientOpts = {} config.ClientOpts.default = function() return { workspaces = {}, - default_workspace = nil, + detect_cwd = false, log_level = nil, notes_subdir = nil, templates = config.TemplateOpts.default(), @@ -73,7 +73,7 @@ config.ClientOpts.normalize = function(opts) end for key, value in pairs(opts.workspaces) do - opts.workspaces[key] = vim.fs.normalize(tostring(value)) + opts.workspaces[key].path = vim.fs.normalize(tostring(value.path)) end return opts diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 6affbf639..1e70f55fc 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -10,9 +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 string +---@field current_workspace obsidian.Workspace ---@field dir Path ---@field templates_dir Path|? ---@field opts obsidian.config.ClientOpts @@ -25,26 +26,9 @@ local client = {} ---@return obsidian.Client obsidian.new = function(opts) local self = setmetatable({}, { __index = client }) - local cwd = vim.fn.getcwd() - local current_workspace = nil - if not opts.default_workspace then - for key, value in pairs(opts.workspaces) do - if value == cwd then - current_workspace = key - break - end - end - else - current_workspace = opts.default_workspace - end - - if not current_workspace then - current_workspace = "." - opts.workspaces[current_workspace] = cwd - end - self.current_workspace = current_workspace - self.dir = Path:new(opts.workspaces[current_workspace]) + self.current_workspace = obsidian.workspace.get_from_opts(opts) + self.dir = Path:new(self.current_workspace.path) self.opts = opts self.backlinks_namespace = vim.api.nvim_create_namespace "ObsidianBacklinks" @@ -57,8 +41,8 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - opts.workspaces = vim.tbl_extend("force", opts.workspaces, { test_vault = vim.fs.normalize(dir) }) - opts.default_workspace = "test_vault" + opts.workspaces = vim.tbl_extend("force", obsidian.workspace.new('test_vault', dir), opts.workspaces) + opts.detect_cwd = false return obsidian.new(opts) end From c31a65445e9b772c6e79a36a2658e28ff49d1bff Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 19:38:05 -0400 Subject: [PATCH 16/31] fix: run luacheck and stylua --- lua/obsidian/command.lua | 10 +++++----- lua/obsidian/init.lua | 2 +- lua/obsidian/workspace.lua | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index f9457aac7..999e5d854 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -653,7 +653,10 @@ 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) + echo.info( + "Current workspace: " .. client.current_workspace.name .. " @ " .. tostring(client.dir), + client.opts.log_level + ) return end @@ -671,10 +674,7 @@ command.switch_workspace = function(client, data) client.current_workspace = workspace - echo.info( - "Switching to workspace '" .. workspace.name .. "' (" .. workspace.path .. ")", - client.opts.log_level - ) + echo.info("Switching to workspace '" .. workspace.name .. "' (" .. workspace.path .. ")", client.opts.log_level) client.dir = Path:new(workspace.path) end diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 1e70f55fc..85db662ca 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -41,7 +41,7 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - opts.workspaces = vim.tbl_extend("force", obsidian.workspace.new('test_vault', dir), opts.workspaces) + opts.workspaces = vim.tbl_extend("force", obsidian.workspace.new("test_vault", dir), opts.workspaces) opts.detect_cwd = false return obsidian.new(opts) end diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index e45a157a1..aca42bd02 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -53,7 +53,7 @@ end ---@param opts obsidian.config.ClientOpts ---@return obsidian.Workspace workspace.get_from_opts = function(opts) - local current_workspace = nil + local current_workspace if opts.detect_cwd then current_workspace = workspace.get_workspace_from_cwd(opts.workspaces) From d3a93c5238b03093434103bab2c527ecaeffe534 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 19:42:29 -0400 Subject: [PATCH 17/31] chore: update readme --- README.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ebddc88c3..55643d87c 100644 --- a/README.md +++ b/README.md @@ -88,10 +88,15 @@ return { }, opts = { workspaces = { - personal = "~/vaults/personal", -- no need to call 'vim.fn.expand' here - work = "~/vaults/work", + { + name = "personal", + path = "~/vaults/personal", + }, + { + name = "work", + path = "~/vaults/work", + }, }, - default_workspace = 'personal', -- see below for full list of options 👇 }, @@ -112,10 +117,15 @@ use({ config = function() require("obsidian").setup({ workspaces = { - personal = "~/vaults/personal", -- no need to call 'vim.fn.expand' here - work = "~/vaults/work", + { + name = "personal", + path = "~/vaults/personal", + }, + { + name = "work", + path = "~/vaults/work", + }, }, - default_workspace = 'personal', -- see below for full list of options 👇 }) @@ -143,12 +153,19 @@ This is a complete list of all of the options that can be passed to `require("ob { -- Optional, list of vault names and paths. workspaces = { - personal = "~/vaults/personal", - work = "~/vaults/work", + { + name = "personal", + path = "~/vaults/personal", + }, + { + name = "work", + path = "~/vaults/work", + }, }, - -- Optional, name of the default vault to open when the plugin loads - default_workspace = 'personal', + -- 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", From 417eb91d0b45243f6152ec316da5984cbe2cedc1 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 19:48:48 -0400 Subject: [PATCH 18/31] fix: passing wrong table to vim.tbl_extend --- lua/obsidian/init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 85db662ca..d8c32248f 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -41,8 +41,7 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - opts.workspaces = vim.tbl_extend("force", obsidian.workspace.new("test_vault", dir), opts.workspaces) - opts.detect_cwd = false + opts.workspaces = vim.tbl_extend("force", { obsidian.workspace.new("test_vault", dir) }, opts.workspaces) return obsidian.new(opts) end From c741398edaf631196faca3e5bb69801a2cb07544 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 20:24:09 -0400 Subject: [PATCH 19/31] chore: clarify table type in workspace class --- lua/obsidian/workspace.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index aca42bd02..c87a09704 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -25,7 +25,7 @@ end ---Determines if cwd is a workspace --- ----@param workspaces table +---@param workspaces table ---@return obsidian.Workspace|nil workspace.get_workspace_from_cwd = function(workspaces) local cwd = vim.fn.getcwd() @@ -41,7 +41,7 @@ end ---Returns the default workspace --- ----@param workspaces table +---@param workspaces table ---@return obsidian.Workspace|nil workspace.get_default_workspace = function(workspaces) local _, value = next(workspaces) From 46b4a83a1ff965f4fe479f3d3884889a4ea82a4f Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 8 Aug 2023 20:24:40 -0400 Subject: [PATCH 20/31] chore: add workspace tests --- test/obsidian/workspace_spec.lua | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 test/obsidian/workspace_spec.lua diff --git a/test/obsidian/workspace_spec.lua b/test/obsidian/workspace_spec.lua new file mode 100644 index 000000000..9da7f4abb --- /dev/null +++ b/test/obsidian/workspace_spec.lua @@ -0,0 +1,64 @@ +local workspace = require "obsidian.workspace" + +local opts = { + workspaces = { + { + name = "work", + path = "~/notes/work", + }, + { + name = "personal", + path = "~/notes/personal", + }, + { + name = "cwd_workspace", + path = os.getenv "PWD", + }, + }, + detect_cwd = false, +} + +describe("Workspace", function() + it("should be able to initialize a workspace", function() + local ws = workspace.new("test_workspace", "/tmp/obsidian_test_workspace") + assert.equals("test_workspace", ws.name) + assert.equals("/tmp/obsidian_test_workspace", ws.path) + end) + + it("should be able to initialize from cwd", function() + local ws = workspace.new_from_cwd() + local cwd = os.getenv "PWD" + assert.equals(".", ws.name) + assert.equals(cwd, ws.path) + end) + + it("should be able to retrieve the default workspace", function() + local ws = workspace.get_default_workspace(opts.workspaces) + assert.is_not(ws, nil) + assert.equals(opts.workspaces[1].name, ws.name) + assert.equals(opts.workspaces[1].path, ws.path) + end) + + it("should initialize workspace from cwd", function() + local ws = workspace.get_workspace_from_cwd(opts.workspaces) + assert.equals(opts.workspaces[3].name, ws.name) + assert.equals(opts.workspaces[3].path, ws.path) + end) + + it("should return cwd workspace when detect_cwd is true", function() + local old_cwd = opts.detect_cwd + opts.detect_cwd = true + local ws = workspace.get_from_opts(opts) + assert.equals(opts.workspaces[3].name, ws.name) + assert.equals(opts.workspaces[3].path, ws.path) + opts.detect_cwd = old_cwd + end) + it("should return default workspace when detect_cwd is false", function() + local old_cwd = opts.detect_cwd + opts.detect_cwd = false + local ws = workspace.get_from_opts(opts) + assert.equals(opts.workspaces[1].name, ws.name) + assert.equals(opts.workspaces[1].path, ws.path) + opts.detect_cwd = old_cwd + end) +end) From 598bc0a92a79020845fb0b445366eae0a15dd6f8 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:10:10 -0400 Subject: [PATCH 21/31] chore: add comments clarifying workspace path normalization --- lua/obsidian/command.lua | 1 + lua/obsidian/init.lua | 1 + lua/obsidian/workspace.lua | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/obsidian/command.lua b/lua/obsidian/command.lua index 999e5d854..5fcb46385 100644 --- a/lua/obsidian/command.lua +++ b/lua/obsidian/command.lua @@ -675,6 +675,7 @@ command.switch_workspace = function(client, data) client.current_workspace = workspace echo.info("Switching to workspace '" .. workspace.name .. "' (" .. workspace.path .. ")", client.opts.log_level) + -- NOTE: workspace.path has already been normalized client.dir = Path:new(workspace.path) end diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index d8c32248f..7d885b457 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -28,6 +28,7 @@ obsidian.new = function(opts) local self = setmetatable({}, { __index = client }) self.current_workspace = obsidian.workspace.get_from_opts(opts) + -- NOTE: workspace.path has already been normalized self.dir = Path:new(self.current_workspace.path) self.opts = opts self.backlinks_namespace = vim.api.nvim_create_namespace "ObsidianBacklinks" diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index c87a09704..07ecc8674 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -6,8 +6,8 @@ local workspace = {} ---Create a new workspace --- ----@param name string ----@param path string +---@param name string Workspace name +---@param path string Workspace path (will be normalized) --- ---@return obsidian.Workspace workspace.new = function(name, path) From 48ae1f1e32ed49d4e898ce8ad8a418a507568de9 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:14:00 -0400 Subject: [PATCH 22/31] chore: revert changes to doc/obsidian.txt to upstream/main --- doc/obsidian.txt | 77 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/doc/obsidian.txt b/doc/obsidian.txt index 964a9f6b9..d30ab8dae 100644 --- a/doc/obsidian.txt +++ b/doc/obsidian.txt @@ -75,6 +75,7 @@ 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 to be installed and on your `$PATH`. See ripgrep#installation for install options. +- If you using WSL, you’ll also need wsl-open Search functionality (e.g. via the `:ObsidianSearch` and `:ObsidianQuickSwitch` commands) also requires telescope.nvim @@ -96,9 +97,12 @@ USING LAZY.NVIM ~ return { "epwalsh/obsidian.nvim", lazy = true, - 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" }, + 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", + }, dependencies = { -- Required. "nvim-lua/plenary.nvim", @@ -172,8 +176,12 @@ 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 daily notes. - date_format = "%Y-%m-%d" + -- 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, completion. @@ -195,9 +203,19 @@ 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"] = require("obsidian.mapping").gf_passthrough(), + ["gf"] = { + action = function() + return require("obsidian").util.gf_passthrough() + end, + opts = { noremap = false, expr = true, buffer = true }, + }, }, + -- 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. @@ -236,8 +254,18 @@ 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-%a", + date_format = "%Y-%m-%d", 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 @@ -263,6 +291,12 @@ 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" @@ -332,7 +366,7 @@ Then make sure to comment out the `gf` keybinding in your obsidian.nvim config: >lua mappings = { - -- ["gf"] = require("obsidian.mapping").gf_passthrough(), + -- ["gf"] = ... }, < @@ -341,7 +375,12 @@ different key: >lua mappings = { - ["fo"] = require("obsidian.mapping").gf_passthrough(), + ["fo"] = { + action = function() + return require("obsidian").util.gf_passthrough() + end, + opts = { noremap = false, expr = true, buffer = true }, + }, }, < @@ -363,7 +402,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", }, } < @@ -372,6 +411,7 @@ and the file `~/my-vault/my-templates-folder/note template.md`: >markdown # {{title}} + Date created: {{date}} < @@ -386,6 +426,23 @@ 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* From e19030468c584644d2cfe9ec913d70593295979b Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:30:09 -0400 Subject: [PATCH 23/31] chore: add function to create new workspace from dir - names the workspace the name of the dir --- lua/obsidian/workspace.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index 07ecc8674..b4322a324 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -23,6 +23,10 @@ workspace.new_from_cwd = function() return workspace.new(".", vim.fn.getcwd()) end +workspace.new_from_dir = function(dir) + return workspace.new(vim.fn.fnamemodify(dir, ':t'), dir) +end + ---Determines if cwd is a workspace --- ---@param workspaces table From 1e1ba52e737f2f243fc3ea8b0cb6aca2df9afdd1 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:31:04 -0400 Subject: [PATCH 24/31] chore: default workspace name to the name of the directory - previously using obfuscatory "test_vault" --- lua/obsidian/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 7d885b457..45ab3a9f5 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -42,7 +42,8 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - opts.workspaces = vim.tbl_extend("force", { obsidian.workspace.new("test_vault", dir) }, opts.workspaces) + opts.workspaces = + vim.tbl_extend("force", { obsidian.workspace.new_from_dir(dir) }, opts.workspaces) return obsidian.new(opts) end From 04dd83857becadf7f638d9bd4a3bf3b83d206f63 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:41:23 -0400 Subject: [PATCH 25/31] fix: backward incompatibility - if `dir` is set, convert it to a workspace object --- lua/obsidian/config.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lua/obsidian/config.lua b/lua/obsidian/config.lua index 6897e281d..2a1189936 100644 --- a/lua/obsidian/config.lua +++ b/lua/obsidian/config.lua @@ -1,10 +1,12 @@ local echo = require "obsidian.echo" +local workspace = require "obsidian.workspace" local config = {} ---[[ Options specs ]]--- ---@class obsidian.config.ClientOpts +---@field dir string|? ---@field workspaces table ---@field detect_cwd boolean ---@field log_level integer|? @@ -31,6 +33,7 @@ config.ClientOpts = {} ---@return obsidian.config.ClientOpts config.ClientOpts.default = function() return { + dir = nil, workspaces = {}, detect_cwd = false, log_level = nil, @@ -76,6 +79,11 @@ config.ClientOpts.normalize = function(opts) opts.workspaces[key].path = vim.fs.normalize(tostring(value.path)) end + if opts.dir ~= nil then + -- NOTE: path will be normalized in workspace.new() fn + vim.tbl_extend("force", opts.workspaces, workspace.new("dir", opts.dir)) + end + return opts end From 852895f7ddb7471a93b23a5e90f94fbe62e87fe8 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:42:45 -0400 Subject: [PATCH 26/31] chore: run stylua and luacheck --- lua/obsidian/init.lua | 3 +-- lua/obsidian/workspace.lua | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/obsidian/init.lua b/lua/obsidian/init.lua index 45ab3a9f5..9690dcd0c 100644 --- a/lua/obsidian/init.lua +++ b/lua/obsidian/init.lua @@ -42,8 +42,7 @@ end ---@return obsidian.Client obsidian.new_from_dir = function(dir) local opts = config.ClientOpts.default() - opts.workspaces = - vim.tbl_extend("force", { obsidian.workspace.new_from_dir(dir) }, opts.workspaces) + opts.workspaces = vim.tbl_extend("force", { obsidian.workspace.new_from_dir(dir) }, opts.workspaces) return obsidian.new(opts) end diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index b4322a324..35e156966 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -24,7 +24,7 @@ workspace.new_from_cwd = function() end workspace.new_from_dir = function(dir) - return workspace.new(vim.fn.fnamemodify(dir, ':t'), dir) + return workspace.new(vim.fn.fnamemodify(dir, ":t"), dir) end ---Determines if cwd is a workspace From 79118e17e08d934a1de087a816ae608661a71a19 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 14:45:51 -0400 Subject: [PATCH 27/31] refactor: use dir name for default vault instead of "." --- lua/obsidian/workspace.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index 35e156966..eba9b6040 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -20,7 +20,7 @@ workspace.new = function(name, path) end workspace.new_from_cwd = function() - return workspace.new(".", vim.fn.getcwd()) + return workspace.new_from_dir(vim.fn.getcwd()) end workspace.new_from_dir = function(dir) From f422f98adb493a7037d85d09ae36e63bdc6674c4 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 15:29:49 -0400 Subject: [PATCH 28/31] fix: backward incompatibility - if `dir` is set, use that as the default workspace - even if workspaces are defined, `dir` is used as the default --- lua/obsidian/config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/obsidian/config.lua b/lua/obsidian/config.lua index 2a1189936..a2bfe6408 100644 --- a/lua/obsidian/config.lua +++ b/lua/obsidian/config.lua @@ -81,7 +81,7 @@ config.ClientOpts.normalize = function(opts) if opts.dir ~= nil then -- NOTE: path will be normalized in workspace.new() fn - vim.tbl_extend("force", opts.workspaces, workspace.new("dir", opts.dir)) + table.insert(opts.workspaces, 1, workspace.new("dir", opts.dir)) end return opts From 9402d5971bfd2a1416b9a57c842411c4cd85fe1a Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 16:09:22 -0400 Subject: [PATCH 29/31] chore: update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 55643d87c..bc2cd6c32 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ This is a complete list of all of the options that can be passed to `require("ob ```lua { + -- Optional, and for backward compatibility. Setting this will use it as the default workspace + -- dir = "~/vaults/other", -- Optional, list of vault names and paths. workspaces = { { From bc228a0c6b2a4c732b366420006e3ffb6df02ed3 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Tue, 17 Oct 2023 16:12:47 -0400 Subject: [PATCH 30/31] fix: failed test using wrong workspace name for current directory --- test/obsidian/workspace_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/obsidian/workspace_spec.lua b/test/obsidian/workspace_spec.lua index 9da7f4abb..953645ff3 100644 --- a/test/obsidian/workspace_spec.lua +++ b/test/obsidian/workspace_spec.lua @@ -28,7 +28,7 @@ describe("Workspace", function() it("should be able to initialize from cwd", function() local ws = workspace.new_from_cwd() local cwd = os.getenv "PWD" - assert.equals(".", ws.name) + assert.equals(vim.fn.fnamemodify(vim.fn.getcwd(), ":t"), ws.name) assert.equals(cwd, ws.path) end) From 242fdbee759d39c7f503e7b38debc5ec467829cf Mon Sep 17 00:00:00 2001 From: epwalsh Date: Wed, 18 Oct 2023 12:01:57 -0700 Subject: [PATCH 31/31] update type hints --- lua/obsidian/config.lua | 2 +- lua/obsidian/workspace.lua | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/obsidian/config.lua b/lua/obsidian/config.lua index a2bfe6408..5ee94f2b8 100644 --- a/lua/obsidian/config.lua +++ b/lua/obsidian/config.lua @@ -7,7 +7,7 @@ local config = {} ---@class obsidian.config.ClientOpts ---@field dir string|? ----@field workspaces table +---@field workspaces obsidian.Workspace[]|? ---@field detect_cwd boolean ---@field log_level integer|? ---@field notes_subdir string|? diff --git a/lua/obsidian/workspace.lua b/lua/obsidian/workspace.lua index eba9b6040..f6a0d2c1d 100644 --- a/lua/obsidian/workspace.lua +++ b/lua/obsidian/workspace.lua @@ -1,7 +1,6 @@ ---@class obsidian.Workspace ---@field name string ---@field path string ----@return obsidian.Workspace local workspace = {} ---Create a new workspace