plugins/obsidian: switch to mkNeovimPlugin

This commit is contained in:
Gaetan Lepage 2024-02-19 00:24:16 +01:00 committed by Gaétan Lepage
parent becfe75a67
commit 8226d825de
5 changed files with 1002 additions and 806 deletions

View file

@ -159,7 +159,7 @@
./utils/nvim-colorizer.nix
./utils/nvim-osc52.nix
./utils/nvim-ufo.nix
./utils/obsidian.nix
./utils/obsidian
./utils/oil.nix
./utils/ollama.nix
./utils/persistence.nix

View file

@ -1,617 +0,0 @@
{
lib,
helpers,
config,
pkgs,
...
}:
with lib; let
cfg = config.plugins.obsidian;
configOptions = {
logLevel = helpers.defaultNullOpts.mkLogLevel "info" ''
Set the log level for obsidian.nvim.
'';
notesSubdir = helpers.mkNullOrStr ''
If you keep notes in a specific subdirectory of your vault.
'';
templates = {
subdir = helpers.mkNullOrStr ''
The name of the directory where templates are stored.
Example: "templates"
'';
dateFormat = helpers.mkNullOrStr ''
Which date format to use.
Example: "%Y-%m-%d"
'';
timeFormat = helpers.mkNullOrStr ''
Which time format to use.
Example: "%H:%M"
'';
substitutions =
helpers.defaultNullOpts.mkNullable
(with helpers.nixvimTypes; attrsOf (either str rawLua))
"{}"
"A map for custom variables, the key should be the variable and the value a function.";
};
noteIdFunc = helpers.mkNullOrLuaFn ''
Customize how names/IDs for new notes are created.
Example:
```lua
function(title)
-- Create note IDs in a Zettelkasten format with a timestamp and a suffix.
-- In this case a note with the title 'My new note' will be given an ID that looks
-- like '1657296016-my-new-note', and therefore the file name '1657296016-my-new-note.md'
local suffix = ""
if title ~= nil then
-- If title is given, transform it into valid file name.
suffix = title:gsub(" ", "-"):gsub("[^A-Za-z0-9-]", ""):lower()
else
-- If title is nil, just add 4 random uppercase letters to the suffix.
for _ = 1, 4 do
suffix = suffix .. string.char(math.random(65, 90))
end
end
return tostring(os.time()) .. "-" .. suffix
end
```
'';
followUrlFunc = helpers.mkNullOrLuaFn ''
By default when you use `:ObsidianFollowLink` on a link to an external URL it will be
ignored but you can customize this behavior here.
Example:
```lua
function(url)
-- Open the URL in the default web browser.
vim.fn.jobstart({"open", url}) -- Mac OS
-- vim.fn.jobstart({"xdg-open", url}) -- linux
end
```
'';
noteFrontmatterFunc = helpers.mkNullOrLuaFn ''
You can customize the frontmatter data.
Example:
```lua
function(note)
-- This is equivalent to the default frontmatter function.
local out = { id = note.id, aliases = note.aliases, tags = note.tags }
-- `note.metadata` contains any manually added fields in the frontmatter.
-- So here we just make sure those fields are kept in the frontmatter.
if note.metadata ~= nil and not vim.tbl_isempty(note.metadata) then
for k, v in pairs(note.metadata) do
out[k] = v
end
end
return out
end
```
'';
disableFrontmatter = helpers.mkNullOrStrLuaFnOr types.bool ''
Boolean or a function that takes a filename and returns a boolean.
`true` indicates that you don't want obsidian.nvim to manage frontmatter.
Default: `false`
'';
backlinks = {
height = helpers.defaultNullOpts.mkUnsignedInt 10 ''
The default height of the backlinks pane.
'';
wrap = helpers.defaultNullOpts.mkBool true ''
Whether or not to wrap lines.
'';
};
completion = {
nvimCmp = helpers.mkNullOrOption types.bool ''
Set to false to disable completion.
Default: `true` if `cmp` is enabled.
'';
minChars = helpers.defaultNullOpts.mkUnsignedInt 2 ''
Trigger completion at this many chars.
'';
newNotesLocation = helpers.defaultNullOpts.mkEnumFirstDefault ["current_dir" "notes_subdir"] ''
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.
'';
prependNoteId = helpers.defaultNullOpts.mkBool true ''
Whether to add the note ID during completion.
E.g. "[[Foo" completes to "[[foo|Foo]]" assuming "foo" is the ID of the note.
Mutually exclusive with 'prependNotePath' and 'usePathOnly'.
'';
prependNotePath = helpers.defaultNullOpts.mkBool false ''
Whether to add the note path during completion.
E.g. "[[Foo" completes to "[[notes/foo|Foo]]" assuming "notes/foo.md" is the path of the note.
Mutually exclusive with 'prependNoteId' and 'usePathOnly'.
'';
usePathOnly = helpers.defaultNullOpts.mkBool false ''
Whether to only use paths during completion.
E.g. "[[Foo" completes to "[[notes/foo]]" assuming "notes/foo.md" is the path of the note.
Mutually exclusive with 'prependNoteId' and 'prependNotePath'.
'';
};
mappings =
helpers.defaultNullOpts.mkNullable
(
with types;
attrsOf (submodule {
options = {
action = mkOption {
type = helpers.nixvimTypes.strLua;
description = "The lua code for this keymap action.";
apply = helpers.mkRaw;
};
opts =
helpers.keymaps.mapConfigOptions
// {
buffer = helpers.defaultNullOpts.mkBool false ''
If true, the mapping will be effective in the current buffer only.
'';
};
};
})
)
''
{
gf = {
action = "require('obsidian').util.gf_passthrough";
opts = {
noremap = false;
expr = true;
buffer = true;
};
};
"<leader>ch" = {
action = "require('obsidian').util.toggle_checkbox";
opts.buffer = true;
};
}
''
''
Configure key mappings.
'';
dailyNotes = {
folder = helpers.mkNullOrStr ''
Optional, if you keep daily notes in a separate directory.
'';
dateFormat = helpers.mkNullOrStr ''
Optional, if you want to change the date format for the ID of daily notes.
Example: "%Y-%m-%d"
'';
aliasFormat = helpers.mkNullOrStr ''
Optional, if you want to change the date format of the default alias of daily notes.
Example: "%B %-d, %Y"
'';
template = helpers.mkNullOrStr ''
Optional, if you want to automatically insert a template from your template directory like
'daily.md'.
'';
};
useAdvancedUri = helpers.mkNullOrOption types.bool ''
Set to true if you use the Obsidian Advanced URI plugin.
https://github.com/Vinzent03/obsidian-advanced-uri
'';
openAppForeground = helpers.defaultNullOpts.mkBool false ''
Set to true to force `:ObsidianOpen` to bring the app to the foreground.
'';
finder = helpers.mkNullOrStr ''
By default commands like `:ObsidianSearch` will attempt to use `telescope.nvim`, `fzf-lua`,
`fzf.vim`, or `mini.pick` (in that order), and use the first one they find.
You can set this option to tell `obsidian.nvim` to always use this finder.
'';
sortBy = helpers.defaultNullOpts.mkEnum ["path" "modified" "accessed" "created"] "modified" ''
Sort search results by "path", "modified", "accessed", or "created".
The recommend value is "modified" and `true` for `sortReversed`, which means, for example,
that `:ObsidianQuickSwitch` will show the notes sorted by latest modified time.
'';
sortReversed = helpers.defaultNullOpts.mkBool true ''
Whether search results should be reversed.
'';
openNotesIn = helpers.defaultNullOpts.mkEnumFirstDefault ["current" "vsplit" "hsplit"] ''
Determines how certain commands open notes.
The valid options are:
- "current" (the default) - to always open in the current window
- "vsplit" - to open in a vertical split if there's not already a vertical split
- "hsplit" - to open in a horizontal split if there's not already a horizontal split
'';
ui = {
enable = helpers.defaultNullOpts.mkBool true ''
Set to false to disable all additional syntax features.
'';
updateDebounce = helpers.defaultNullOpts.mkUnsignedInt 200 ''
Update delay after a text change (in milliseconds).
'';
checkboxes =
helpers.defaultNullOpts.mkNullable
(
with types;
attrsOf (
submodule {
options = {
char = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The character to use for this checkbox.";
};
hlGroup = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The name of the highlight group to use for this checkbox.";
};
};
}
)
)
''
{
" " = {
char = "󰄱";
hlGroup = "ObsidianTodo";
};
"x" = {
char = "";
hlGroup = "ObsidianDone";
};
">" = {
char = "";
hlGroup = "ObsidianRightArrow";
};
"~" = {
char = "󰰱";
hlGroup = "ObsidianTilde";
};
}
''
''
Define how various check-boxes are displayed.
You can also add more custom ones...
NOTE: the 'char' value has to be a single character, and the highlight groups are defined
in the `ui.hlGroups` option.
'';
externalLinkIcon = {
char = helpers.defaultNullOpts.mkStr "" ''
Which character to use for the external link icon.
'';
hlGroup = helpers.defaultNullOpts.mkStr "ObsidianExtLinkIcon" ''
The name of the highlight group to use for the external link icon.
'';
};
referenceText = {
hlGroup = helpers.defaultNullOpts.mkStr "ObsidianRefText" ''
The name of the highlight group to use for reference text.
'';
};
highlightText = {
hlGroup = helpers.defaultNullOpts.mkStr "ObsidianHighlightText" ''
The name of the highlight group to use for highlight text.
'';
};
tags = {
hlGroup = helpers.defaultNullOpts.mkStr "ObsidianTag" ''
The name of the highlight group to use for tags.
'';
};
hlGroups =
helpers.defaultNullOpts.mkNullable
(with helpers.nixvimTypes; attrsOf highlight)
''
{
ObsidianTodo = {
bold = true;
fg = "#f78c6c";
};
ObsidianDone = {
bold = true;
fg = "#89ddff";
};
ObsidianRightArrow = {
bold = true;
fg = "#f78c6c";
};
ObsidianTilde = {
bold = true;
fg = "#ff5370";
};
ObsidianRefText = {
underline = true;
fg = "#c792ea";
};
ObsidianExtLinkIcon = {
fg = "#c792ea";
};
ObsidianTag = {
italic = true;
fg = "#89ddff";
};
ObsidianHighlightText = {
bg = "#75662e";
};
}
''
"Highlight group definitions.";
};
attachments = {
imgFolder = helpers.defaultNullOpts.mkStr "assets/imgs" ''
The default folder to place images in via `:ObsidianPasteImg`.
If this is a relative path it will be interpreted as relative to the vault root.
You can always override this per image by passing a full path to the command instead of just
a filename.
'';
imgTextFunc =
helpers.defaultNullOpts.mkLuaFn
''
function(client, path)
---@type string
local link_path
local vault_relative_path = client:vault_relative_path(path)
if vault_relative_path ~= nil then
-- Use relative path if the image is saved in the vault dir.
link_path = vault_relative_path
else
-- Otherwise use the absolute path.
link_path = tostring(path)
end
local display_name = vim.fs.basename(link_path)
return string.format("![%s](%s)", display_name, link_path)
end
''
''
A function that determines the text to insert in the note when pasting an image.
It takes two arguments, the `obsidian.Client` and a plenary `Path` to the image file.
```lua
@param client obsidian.Client
@param path Path the absolute path to the image file
@return string
```
'';
};
yamlParser = helpers.defaultNullOpts.mkEnumFirstDefault ["native" "yq"] ''
Set the YAML parser to use.
The valid options are:
- "native" - uses a pure Lua parser that's fast but potentially misses some edge cases.
- "yq" - uses the command-line tool yq (https://github.com/mikefarah/yq), which is more robust
but much slower and needs to be installed separately.
In general you should be using the native parser unless you run into a bug with it, in which
case you can temporarily switch to the "yq" parser until the bug is fixed.
'';
};
in {
meta.maintainers = [maintainers.GaetanLepage];
options.plugins.obsidian =
helpers.neovim-plugin.extraOptionsOptions
// {
enable = mkEnableOption "obsidian.nvim";
package = helpers.mkPackageOption "obsidian.nvim" pkgs.vimPlugins.obsidian-nvim;
dir = helpers.mkNullOrOption types.str ''
Alternatively to `workspaces` - and for backwards compatibility - you can set `dir` to a
single path instead of `workspaces`.
For example:
```nix
dir = "~/vaults/work";
```
'';
workspaces =
helpers.defaultNullOpts.mkNullable
(
with types;
listOf
(types.submodule {
options = {
name = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The name for this workspace";
};
path = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The of the workspace.";
};
overrides = configOptions;
};
})
)
"[]"
''
A list of vault names and paths.
Each path should be the path to the vault root.
If you use the Obsidian app, the vault root is the parent directory of the `.obsidian`
folder.
You can also provide configuration overrides for each workspace through the `overrides`
field.
'';
detectCwd = helpers.defaultNullOpts.mkBool false ''
Set to true to use the current directory as a vault; otherwise the first workspace
is opened by default.
'';
}
// configOptions;
config = mkIf cfg.enable {
extraPlugins = [cfg.package];
assertions = [
{
assertion = let
nvimCmpEnabled = isBool cfg.completion.nvimCmp && cfg.completion.nvimCmp;
in
nvimCmpEnabled -> config.plugins.cmp.enable;
message = ''
Nixvim (plugins.obsidian): You have enabled `completion.nvimCmp` but `plugins.cmp.enable` is `false`.
You need to enable `cmp` to use this setting.
'';
}
];
extraConfigLua = let
processConfigOptions = configOptions:
with configOptions; {
log_level = logLevel;
notes_subdir = notesSubdir;
templates = with templates; {
inherit subdir;
date_format = dateFormat;
time_format = timeFormat;
inherit substitutions;
};
note_id_func = noteIdFunc;
follow_url_func = followUrlFunc;
note_formatter_func = noteFrontmatterFunc;
disable_frontmatter = disableFrontmatter;
backlinks = with backlinks; {
inherit
height
wrap
;
};
completion = with completion; {
nvim_cmp = nvimCmp;
min_chars = minChars;
new_notes_location = newNotesLocation;
prepend_note_id = prependNoteId;
prepend_note_path = prependNotePath;
use_path_only = usePathOnly;
};
inherit mappings;
daily_notes = with dailyNotes; {
inherit folder;
date_format = dateFormat;
alias_format = aliasFormat;
inherit template;
};
use_advanced_uri = useAdvancedUri;
open_app_foreground = openAppForeground;
inherit finder;
sort_by = sortBy;
sort_reversed = sortReversed;
open_notes_in = openNotesIn;
ui = with ui; {
inherit enable;
update_debounce = updateDebounce;
checkboxes =
helpers.ifNonNull' checkboxes
(
mapAttrs
(
_: checkbox: {
inherit (checkbox) char;
hl_group = checkbox.hlGroup;
}
)
checkboxes
);
external_link_icon = with externalLinkIcon; {
inherit char;
hl_group = hlGroup;
};
reference_text = with referenceText; {
hl_group = hlGroup;
};
highlight_text = with highlightText; {
hl_group = hlGroup;
};
tags = with tags; {
hl_group = hlGroup;
};
hl_groups = hlGroups;
};
attachments = with attachments; {
img_folder = imgFolder;
img_text_func = imgTextFunc;
};
yaml_parser = yamlParser;
};
setupOptions = with cfg;
{
inherit dir;
workspaces =
helpers.ifNonNull' workspaces
(
map
(
workspaceConfig: {
inherit
(workspaceConfig)
name
path
;
overrides = processConfigOptions workspaceConfig.overrides;
}
)
workspaces
);
detect_cwd = detectCwd;
}
// (processConfigOptions cfg)
// cfg.extraOptions;
in ''
require('obsidian').setup(${helpers.toLuaObject setupOptions})
'';
};
}

View file

@ -0,0 +1,175 @@
{
lib,
helpers,
config,
pkgs,
...
}:
with lib;
helpers.neovim-plugin.mkNeovimPlugin config {
name = "obsidian";
originalName = "obsidian.nvim";
defaultPackage = pkgs.vimPlugins.obsidian-nvim;
maintainers = [maintainers.GaetanLepage];
## DEPRECATIONS
# Introduced 2024-03-12
# TODO: remove 2024-05-12
deprecateExtraOptions = true;
optionsRenamedToSettings = [
"dir"
"logLevel"
"notesSubdir"
["templates" "subdir"]
["templates" "dateFormat"]
["templates" "timeFormat"]
["templates" "substitutions"]
"noteIdFunc"
"followUrlFunc"
"noteFrontmatterFunc"
"disableFrontmatter"
["completion" "nvimCmp"]
["completion" "minChars"]
"mappings"
["dailyNotes" "folder"]
["dailyNotes" "dateFormat"]
["dailyNotes" "aliasFormat"]
["dailyNotes" "template"]
"useAdvancedUri"
"openAppForeground"
"sortBy"
"sortReversed"
"openNotesIn"
["ui" "enable"]
["ui" "updateDebounce"]
["ui" "externalLinkIcon" "char"]
["ui" "externalLinkIcon" "hlGroup"]
["ui" "referenceText" "hlGroup"]
["ui" "highlightText" "hlGroup"]
["ui" "tags" "hlGroup"]
["ui" "hlGroups"]
["attachments" "imgFolder"]
["attachments" "imgTextFunc"]
"yamlParser"
];
imports = let
basePluginPath = ["plugins" "obsidian"];
in
[
( # We have to remove the option here because the user could set old-style camelCase options in each workspaces element.
mkRemovedOptionModule
(basePluginPath ++ ["workspaces"])
"Please use `plugins.obsidian.settings.workspaces` instead."
)
(
mkRenamedOptionModule
(basePluginPath ++ ["finder"])
(basePluginPath ++ ["settings" "picker" "name"])
)
(
# https://github.com/epwalsh/obsidian.nvim/blob/656d9c2c64528839db8b2d9a091843b3c90155a2/CHANGELOG.md?plain=1#L184
mkRenamedOptionModule
(basePluginPath ++ ["completion" "newNotesLocation"])
(basePluginPath ++ ["settings" "new_notes_location"])
)
( # We have to remove the option here because the user could set old-style camelCase options in each checkbox element.
mkRemovedOptionModule
(basePluginPath ++ ["ui" "checkboxes"])
"Please use `plugins.obsidian.settings.ui.checkboxes` instead."
)
]
++ (map (
optionPath:
mkRemovedOptionModule
(basePluginPath ++ optionPath)
"This option was deprecated by upstream."
)
[
["detectCwd"]
["backlinks"]
["completion" "prependNoteId"]
["completion" "prependNotePath"]
["completion" "usePathOnly"]
]);
settingsOptions = let
opts = import ./options.nix {inherit lib helpers;};
in
{
dir = helpers.mkNullOrOption types.str ''
Alternatively to `workspaces` - and for backwards compatibility - you can set `dir` to a
single path instead of `workspaces`.
For example:
```nix
dir = "~/vaults/work";
```
'';
workspaces =
helpers.defaultNullOpts.mkNullable
(
with types;
listOf
(types.submodule {
options = {
name = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The name for this workspace";
};
path = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The of the workspace.";
};
overrides = opts;
};
})
)
"[]"
''
A list of vault names and paths.
Each path should be the path to the vault root.
If you use the Obsidian app, the vault root is the parent directory of the `.obsidian`
folder.
You can also provide configuration overrides for each workspace through the `overrides`
field.
'';
}
// opts;
settingsExample = {
workspaces = [
{
name = "work";
path = "~/obsidian/work";
}
{
name = "startup";
path = "~/obsidian/startup";
}
];
new_notes_location = "current_dir";
completion = {
nvim_cmp = true;
min_chars = 2;
};
};
extraConfig = cfg: {
assertions = [
{
assertion = let
nvimCmpEnabled = isBool cfg.settings.completion.nvim_cmp && cfg.settings.completion.nvim_cmp;
in
nvimCmpEnabled -> config.plugins.nvim-cmp.enable;
message = ''
Nixvim (plugins.obsidian): You have enabled `completion.nvim_cmp` but `plugins.cmp.enable` is `false`.
You need to enable `nvim-cmp` to use this setting.
'';
}
];
};
}

View file

@ -0,0 +1,556 @@
{
lib,
helpers,
}:
with lib; {
# https://github.com/epwalsh/obsidian.nvim/blob/main/lua/obsidian/config.lua
log_level = helpers.defaultNullOpts.mkLogLevel "info" ''
Set the log level for obsidian.nvim.
'';
notes_subdir = helpers.mkNullOrStr ''
If you keep notes in a specific subdirectory of your vault.
'';
templates = {
subdir = helpers.mkNullOrStr ''
The name of the directory where templates are stored.
Example: "templates"
'';
date_format = helpers.mkNullOrStr ''
Which date format to use.
Example: "%Y-%m-%d"
'';
time_format = helpers.mkNullOrStr ''
Which time format to use.
Example: "%H:%M"
'';
substitutions =
helpers.defaultNullOpts.mkAttrsOf
(with helpers.nixvimTypes; either str rawLua)
"{}"
"A map for custom variables, the key should be the variable and the value a function.";
};
new_notes_location = helpers.defaultNullOpts.mkEnumFirstDefault ["current_dir" "notes_subdir"] ''
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.
'';
note_id_func = helpers.mkNullOrLuaFn ''
Customize how names/IDs for new notes are created.
Example:
```lua
function(title)
-- Create note IDs in a Zettelkasten format with a timestamp and a suffix.
-- In this case a note with the title 'My new note' will be given an ID that looks
-- like '1657296016-my-new-note', and therefore the file name '1657296016-my-new-note.md'
local suffix = ""
if title ~= nil then
-- If title is given, transform it into valid file name.
suffix = title:gsub(" ", "-"):gsub("[^A-Za-z0-9-]", ""):lower()
else
-- If title is nil, just add 4 random uppercase letters to the suffix.
for _ = 1, 4 do
suffix = suffix .. string.char(math.random(65, 90))
end
end
return tostring(os.time()) .. "-" .. suffix
end
```
'';
note_path_func = helpers.mkNullOrLuaFn ''
Customize how note file names are generated given the ID, target directory, and title.
```lua
---@param spec { id: string, dir: obsidian.Path, title: string|? }
---@return string|obsidian.Path The full path to the new note.
```
Example:
```lua
function(spec)
-- This is equivalent to the default behavior.
local path = spec.dir / tostring(spec.id)
return path:with_suffix(".md")
end
```
'';
wiki_link_func =
helpers.mkNullOrLuaFn
''
Customize how wiki links are formatted.
```lua
---@param opts {path: string, label: string, id: string|?}
---@return string
```
Example:
```lua
function(opts)
if opts.id == nil then
return string.format("[[%s]]", opts.label)
elseif opts.label ~= opts.id then
return string.format("[[%s|%s]]", opts.id, opts.label)
else
return string.format("[[%s]]", opts.id)
end
end
```
Default: See source
'';
markdown_link_func = helpers.mkNullOrLuaFn ''
Customize how markdown links are formatted.
```lua
---@param opts {path: string, label: string, id: string|?}
---@return string links are formatted.
```
Example:
```lua
function(opts)
return string.format("[%s](%s)", opts.label, opts.path)
end
```
Default: See source
'';
preferred_link_style = helpers.defaultNullOpts.mkEnumFirstDefault ["wiki" "markdown"] ''
Either 'wiki' or 'markdown'.
'';
follow_url_func = helpers.mkNullOrLuaFn ''
By default when you use `:ObsidianFollowLink` on a link to an external URL it will be
ignored but you can customize this behavior here.
Example:
```lua
function(url)
-- Open the URL in the default web browser.
vim.fn.jobstart({"open", url}) -- Mac OS
-- vim.fn.jobstart({"xdg-open", url}) -- linux
end
```
'';
image_name_func = helpers.mkNullOrLuaFn ''
Customize the default name or prefix when pasting images via `:ObsidianPasteImg`.
Example:
```lua
function()
-- Prefix image names with timestamp.
return string.format("%s-", os.time())
end
```
'';
note_frontmatter_func = helpers.mkNullOrLuaFn ''
You can customize the frontmatter data.
Example:
```lua
function(note)
-- Add the title of the note as an alias.
if note.title then
note:add_alias(note.title)
end
local out = { id = note.id, aliases = note.aliases, tags = note.tags }
-- `note.metadata` contains any manually added fields in the frontmatter.
-- So here we just make sure those fields are kept in the frontmatter.
if note.metadata ~= nil and not vim.tbl_isempty(note.metadata) then
for k, v in pairs(note.metadata) do
out[k] = v
end
end
return out
end
```
'';
disable_frontmatter = helpers.mkNullOrStrLuaFnOr types.bool ''
Boolean or a function that takes a filename and returns a boolean.
`true` indicates that you don't want obsidian.nvim to manage frontmatter.
Default: `false`
'';
completion = {
nvim_cmp = helpers.mkNullOrOption types.bool ''
Set to false to disable completion.
Default: `true` if `nvim-cmp` is enabled (`plugins.cmp.enable`).
'';
min_chars = helpers.defaultNullOpts.mkUnsignedInt 2 ''
Trigger completion at this many chars.
'';
};
mappings =
helpers.defaultNullOpts.mkNullable
(
with types;
attrsOf (submodule {
options = {
action = mkOption {
type = helpers.nixvimTypes.strLua;
description = "The lua code for this keymap action.";
apply = helpers.mkRaw;
};
opts =
helpers.keymaps.mapConfigOptions
// {
buffer = helpers.defaultNullOpts.mkBool false ''
If true, the mapping will be effective in the current buffer only.
'';
};
};
})
)
''
{
gf = {
action = "require('obsidian').util.gf_passthrough";
opts = {
noremap = false;
expr = true;
buffer = true;
};
};
"<leader>ch" = {
action = "require('obsidian').util.toggle_checkbox";
opts.buffer = true;
};
}
''
''
Configure key mappings.
'';
picker = {
name = helpers.mkNullOrOption (types.enum ["telescope.nvim" "fzf-lua" "mini.pick"]) ''
Set your preferred picker.
'';
note_mappings =
helpers.defaultNullOpts.mkAttrsOf types.str
''
{
new = "<C-x>";
insert_link = "<C-l>";
}
''
''
Optional, configure note mappings for the picker. These are the defaults.
Not all pickers support all mappings.
'';
tag_mappings =
helpers.defaultNullOpts.mkAttrsOf types.str
''
{
tag_note = "<C-x>";
insert_tag = "<C-l>";
}
''
''
Optional, configure tag mappings for the picker. These are the defaults.
Not all pickers support all mappings.
'';
};
daily_notes = {
folder = helpers.mkNullOrStr ''
Optional, if you keep daily notes in a separate directory.
'';
date_format = helpers.mkNullOrStr ''
Optional, if you want to change the date format for the ID of daily notes.
Example: "%Y-%m-%d"
'';
alias_format = helpers.mkNullOrStr ''
Optional, if you want to change the date format of the default alias of daily notes.
Example: "%B %-d, %Y"
'';
template = helpers.mkNullOrStr ''
Optional, if you want to automatically insert a template from your template directory like
'daily.md'.
'';
};
use_advanced_uri = helpers.defaultNullOpts.mkBool false ''
Set to true to force ':ObsidianOpen' to bring the app to the foreground.
'';
open_app_foreground = helpers.defaultNullOpts.mkBool false ''
Set to true to force `:ObsidianOpen` to bring the app to the foreground.
'';
sort_by = helpers.defaultNullOpts.mkEnum ["path" "modified" "accessed" "created"] "modified" ''
Sort search results by "path", "modified", "accessed", or "created".
The recommend value is "modified" and `true` for `sortReversed`, which means, for example,
that `:ObsidianQuickSwitch` will show the notes sorted by latest modified time.
'';
sort_reversed = helpers.defaultNullOpts.mkBool true ''
Whether search results should be reversed.
'';
open_notes_in = helpers.defaultNullOpts.mkEnumFirstDefault ["current" "vsplit" "hsplit"] ''
Determines how certain commands open notes.
The valid options are:
- "current" (the default) - to always open in the current window
- "vsplit" - to open in a vertical split if there's not already a vertical split
- "hsplit" - to open in a horizontal split if there's not already a horizontal split
'';
ui = {
enable = helpers.defaultNullOpts.mkBool true ''
Set to false to disable all additional syntax features.
'';
update_debounce = helpers.defaultNullOpts.mkUnsignedInt 200 ''
Update delay after a text change (in milliseconds).
'';
checkboxes =
helpers.defaultNullOpts.mkNullable
(
with types;
attrsOf (
submodule {
options = {
char = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The character to use for this checkbox.";
};
hl_group = mkOption {
type = with helpers.nixvimTypes; maybeRaw str;
description = "The name of the highlight group to use for this checkbox.";
};
};
}
)
)
''
{
" " = {
char = "󰄱";
hl_group = "ObsidianTodo";
};
"x" = {
char = "";
hl_group = "ObsidianDone";
};
">" = {
char = "";
hl_group = "ObsidianRightArrow";
};
"~" = {
char = "󰰱";
hl_group = "ObsidianTilde";
};
}
''
''
Define how various check-boxes are displayed.
You can also add more custom ones...
NOTE: the 'char' value has to be a single character, and the highlight groups are defined
in the `ui.hl_groups` option.
'';
bullets = {
char = helpers.defaultNullOpts.mkStr "" ''
Which character to use for the bullets.
'';
hl_group = helpers.defaultNullOpts.mkStr "ObsidianBullet" ''
The name of the highlight group to use for the bullets.
'';
};
external_link_icon = {
char = helpers.defaultNullOpts.mkStr "" ''
Which character to use for the external link icon.
'';
hl_group = helpers.defaultNullOpts.mkStr "ObsidianExtLinkIcon" ''
The name of the highlight group to use for the external link icon.
'';
};
reference_text = {
hl_group = helpers.defaultNullOpts.mkStr "ObsidianRefText" ''
The name of the highlight group to use for reference text.
'';
};
highlight_text = {
hl_group = helpers.defaultNullOpts.mkStr "ObsidianHighlightText" ''
The name of the highlight group to use for highlight text.
'';
};
tags = {
hl_group = helpers.defaultNullOpts.mkStr "ObsidianTag" ''
The name of the highlight group to use for tags.
'';
};
hl_groups =
helpers.defaultNullOpts.mkNullable
(with helpers.nixvimTypes; attrsOf highlight)
''
{
ObsidianTodo = {
bold = true;
fg = "#f78c6c";
};
ObsidianDone = {
bold = true;
fg = "#89ddff";
};
ObsidianRightArrow = {
bold = true;
fg = "#f78c6c";
};
ObsidianTilde = {
bold = true;
fg = "#ff5370";
};
ObsidianRefText = {
underline = true;
fg = "#c792ea";
};
ObsidianExtLinkIcon = {
fg = "#c792ea";
};
ObsidianTag = {
italic = true;
fg = "#89ddff";
};
ObsidianHighlightText = {
bg = "#75662e";
};
}
''
"Highlight group definitions.";
};
attachments = {
img_folder = helpers.defaultNullOpts.mkStr "assets/imgs" ''
The default folder to place images in via `:ObsidianPasteImg`.
If this is a relative path it will be interpreted as relative to the vault root.
You can always override this per image by passing a full path to the command instead of just
a filename.
'';
img_text_func =
helpers.defaultNullOpts.mkLuaFn
''
function(client, path)
---@type string
local link_path
local vault_relative_path = client:vault_relative_path(path)
if vault_relative_path ~= nil then
-- Use relative path if the image is saved in the vault dir.
link_path = vault_relative_path
else
-- Otherwise use the absolute path.
link_path = tostring(path)
end
local display_name = vim.fs.basename(link_path)
return string.format("![%s](%s)", display_name, link_path)
end
''
''
A function that determines the text to insert in the note when pasting an image.
It takes two arguments, the `obsidian.Client` and a plenary `Path` to the image file.
```lua
@param client obsidian.Client
@param path Path the absolute path to the image file
@return string
```
'';
confirm_img_paste = helpers.defaultNullOpts.mkBool true ''
Whether to prompt for confirmation when pasting an image.
'';
};
callbacks = {
post_setup = helpers.mkNullOrLuaFn ''
`fun(client: obsidian.Client)`
Runs right after the `obsidian.Client` is initialized.
'';
enter_note = helpers.mkNullOrLuaFn ''
`fun(client: obsidian.Client, note: obsidian.Note)`
Runs when entering a note buffer.
'';
leave_note = helpers.mkNullOrLuaFn ''
`fun(client: obsidian.Client, note: obsidian.Note)`
Runs when leaving a note buffer.
'';
pre_write_note = helpers.mkNullOrLuaFn ''
`fun(client: obsidian.Client, note: obsidian.Note)`
Runs right before writing a note buffer.
'';
post_set_workspace = helpers.mkNullOrLuaFn ''
`fun(client: obsidian.Client, workspace: obsidian.Workspace)`
Runs anytime the workspace is set/changed.
'';
};
yaml_parser = helpers.defaultNullOpts.mkEnumFirstDefault ["native" "yq"] ''
Set the YAML parser to use.
The valid options are:
- "native" - uses a pure Lua parser that's fast but potentially misses some edge cases.
- "yq" - uses the command-line tool yq (https://github.com/mikefarah/yq), which is more robust
but much slower and needs to be installed separately.
In general you should be using the native parser unless you run into a bug with it, in which
case you can temporarily switch to the "yq" parser until the bug is fixed.
'';
}

View file

@ -1,42 +1,73 @@
{
# Note: we can only use `~/` as a workspace as the workspace folder need to exist in the sandbox.
empty = {
# TODO fix the plugin tests
plugins.obsidian.enable = false;
plugins.obsidian = {
enable = true;
# At least one workspaces is needed for the plugin to work
settings.workspaces = [
{
name = "foo";
path = "~/";
}
];
};
};
example = {
simple-example = {
plugins = {
cmp.enable = true;
obsidian = {
enable = true;
settings = {
dir = null;
workspaces = [
{
name = "work";
path = "~";
}
];
new_notes_location = "current_dir";
completion = {
nvim_cmp = true;
min_chars = 2;
};
};
};
};
};
complete-example = {
plugins = {
cmp.enable = true;
obsidian = {
enable = false;
settings = {
dir = null;
workspaces = [
{
name = "personal";
path = "~/vaults/personal";
}
{
name = "work";
path = "~/vaults/work";
path = "~/";
overrides = {
notesSubdir = "notes";
notes_subdir = "notes";
};
}
];
detectCwd = false;
logLevel = "info";
notesSubdir = "notes";
log_level = "info";
notes_subdir = "notes";
templates = {
# We cannot set this as it doesn't exist in the testing environment
# subdir = "templates";
dateFormat = "%Y-%m-%d";
timeFormat = "%H:%M";
date_format = "%Y-%m-%d";
time_format = "%H:%M";
substitutions = {};
};
noteIdFunc = ''
new_notes_location = "current_dir";
note_id_func = ''
function(title)
-- Create note IDs in a Zettelkasten format with a timestamp and a suffix.
-- In this case a note with the title 'My new note' will be given an ID that looks
@ -54,17 +85,52 @@
return tostring(os.time()) .. "-" .. suffix
end
'';
followUrlFunc = ''
note_path_func = ''
function(spec)
-- This is equivalent to the default behavior.
local path = spec.dir / tostring(spec.id)
return path:with_suffix(".md")
end
'';
wiki_link_func = ''
function(opts)
if opts.id == nil then
return string.format("[[%s]]", opts.label)
elseif opts.label ~= opts.id then
return string.format("[[%s|%s]]", opts.id, opts.label)
else
return string.format("[[%s]]", opts.id)
end
end
'';
markdown_link_func = ''
function(opts)
return string.format("[%s](%s)", opts.label, opts.path)
end
'';
preferred_link_style = "wiki";
follow_url_func = ''
function(url)
-- Open the URL in the default web browser.
vim.fn.jobstart({"open", url}) -- Mac OS
-- vim.fn.jobstart({"xdg-open", url}) -- linux
end
'';
noteFrontmatterFunc = ''
image_name_func = ''
function()
-- Prefix image names with timestamp.
return string.format("%s-", os.time())
end
'';
note_frontmatter_func = ''
function(note)
-- This is equivalent to the default frontmatter function.
-- Add the title of the note as an alias.
if note.title then
note:add_alias(note.title)
end
local out = { id = note.id, aliases = note.aliases, tags = note.tags }
-- `note.metadata` contains any manually added fields in the frontmatter.
-- So here we just make sure those fields are kept in the frontmatter.
if note.metadata ~= nil and not vim.tbl_isempty(note.metadata) then
@ -72,21 +138,14 @@
out[k] = v
end
end
return out
end
'';
disableFrontmatter = false;
backlinks = {
height = 10;
wrap = true;
};
disable_frontmatter = false;
completion = {
nvimCmp = true;
minChars = 2;
newNotesLocation = "current_dir";
prependNoteId = true;
prependNotePath = false;
usePathOnly = false;
nvim_cmp = true;
min_chars = 2;
};
mappings = {
gf = {
@ -103,53 +162,67 @@
opts.buffer = true;
};
};
dailyNotes = {
picker = {
name = "telescope.nvim";
note_mappings = {
new = "<C-x>";
insert_link = "<C-l>";
};
tag_mappings = {
tag_note = "<C-x>";
insert_tag = "<C-l>";
};
};
daily_notes = {
folder = "notes";
dateFormat = "%Y-%m-%d";
aliasFormat = "%B %-d, %Y";
date_format = "%Y-%m-%d";
alias_format = "%B %-d, %Y";
template = "daily.md";
};
useAdvancedUri = false;
openAppForeground = false;
finder = "telescope.nvim";
sortBy = "modified";
sortReversed = true;
openNotesIn = "current";
use_advanced_uri = false;
open_app_foreground = false;
sort_by = "modified";
sort_reversed = true;
open_notes_in = "current";
ui = {
enable = true;
updateDebounce = 200;
update_debounce = 200;
checkboxes = {
" " = {
char = "󰄱";
hlGroup = "ObsidianTodo";
hl_group = "ObsidianTodo";
};
"x" = {
char = "";
hlGroup = "ObsidianDone";
hl_group = "ObsidianDone";
};
">" = {
char = "";
hlGroup = "ObsidianRightArrow";
hl_group = "ObsidianRightArrow";
};
"~" = {
char = "󰰱";
hlGroup = "ObsidianTilde";
hl_group = "ObsidianTilde";
};
};
externalLinkIcon = {
bullets = {
char = "";
hl_group = "ObsidianBullet";
};
external_link_icon = {
char = "";
hlGroup = "ObsidianExtLinkIcon";
hl_group = "ObsidianExtLinkIcon";
};
referenceText = {
hlGroup = "ObsidianRefText";
reference_text = {
hl_group = "ObsidianRefText";
};
highlightText = {
hlGroup = "ObsidianHighlightText";
highlight_text = {
hl_group = "ObsidianHighlightText";
};
tags = {
hlGroup = "ObsidianTag";
hl_group = "ObsidianTag";
};
hlGroups = {
hl_groups = {
ObsidianTodo = {
bold = true;
fg = "#f78c6c";
@ -183,8 +256,8 @@
};
};
attachments = {
imgFolder = "assets/imgs";
imgTextFunc = ''
img_folder = "assets/imgs";
img_text_func = ''
function(client, path)
---@type string
local link_path
@ -200,8 +273,17 @@
return string.format("![%s](%s)", display_name, link_path)
end
'';
confirm_img_paste = true;
};
callbacks = {
post_setup = "function(client) end";
enter_note = "function(client, note) end";
leave_note = "function(client, note) end";
pre_write_note = "function(client, note) end";
post_set_workspace = "function(client, workspace) end";
};
yaml_parser = "native";
};
yamlParser = "native";
};
};
};