-
Notifications
You must be signed in to change notification settings - Fork 249
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move each command to its own module (#271)
- Loading branch information
Showing
19 changed files
with
1,374 additions
and
1,311 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
local util = require "obsidian.util" | ||
local log = require "obsidian.log" | ||
local RefTypes = require("obsidian.search").RefTypes | ||
|
||
---@param client obsidian.Client | ||
return function(client, _) | ||
---@type obsidian.Note|? | ||
local note | ||
local cursor_link, _, ref_type = util.cursor_link() | ||
if cursor_link ~= nil and ref_type ~= RefTypes.NakedUrl then | ||
note = client:resolve_note(cursor_link) | ||
if note == nil then | ||
log.err "Could not resolve link under cursor to a note ID, path, or alias" | ||
return | ||
end | ||
end | ||
|
||
local ok, backlinks = pcall(function() | ||
return require("obsidian.backlinks").new(client, nil, nil, note) | ||
end) | ||
|
||
if ok then | ||
backlinks:view(function(matches) | ||
if not vim.tbl_isempty(matches) then | ||
log.info( | ||
"Showing backlinks to '%s'.\n\n" | ||
.. "TIPS:\n\n" | ||
.. "- Hit ENTER on a match to follow the backlink\n" | ||
.. "- Hit ENTER on a group header to toggle the fold, or use normal fold mappings", | ||
backlinks.note.id | ||
) | ||
else | ||
if note ~= nil then | ||
log.warn("No backlinks to '%s'", note.id) | ||
else | ||
log.warn "No backlinks to current note" | ||
end | ||
end | ||
end) | ||
else | ||
log.err "Backlinks command can only be used from a valid note" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
local Path = require "plenary.path" | ||
local Note = require "obsidian.note" | ||
local log = require "obsidian.log" | ||
local iter = require("obsidian.itertools").iter | ||
local AsyncExecutor = require("obsidian.async").AsyncExecutor | ||
local scan = require "plenary.scandir" | ||
|
||
---@param client obsidian.Client | ||
return function(client, _) | ||
local skip_dirs = {} | ||
if client.opts.templates ~= nil and client.opts.templates.subdir ~= nil then | ||
skip_dirs[#skip_dirs + 1] = Path:new(client.opts.templates.subdir) | ||
end | ||
|
||
local executor = AsyncExecutor.new() | ||
local count = 0 | ||
local errors = {} | ||
local warnings = {} | ||
|
||
---@param path Path | ||
local function check_note(path, relative_path) | ||
local ok, res = pcall(Note.from_file_async, path, client.dir) | ||
if not ok then | ||
errors[#errors + 1] = "Failed to parse note '" .. relative_path .. "': " .. tostring(res) | ||
elseif res.has_frontmatter == false then | ||
warnings[#warnings + 1] = "'" .. relative_path .. "' missing frontmatter" | ||
end | ||
count = count + 1 | ||
end | ||
|
||
---@diagnostic disable-next-line: undefined-field | ||
local start_time = vim.loop.hrtime() | ||
|
||
scan.scan_dir(vim.fs.normalize(tostring(client:vault_root())), { | ||
hidden = false, | ||
add_dirs = false, | ||
respect_gitignore = true, | ||
search_pattern = ".*%.md", | ||
on_insert = function(entry) | ||
local relative_path = assert(client:vault_relative_path(entry)) | ||
for skip_dir in iter(skip_dirs) do | ||
if vim.startswith(relative_path, tostring(skip_dir) .. skip_dir._sep) then | ||
return | ||
end | ||
end | ||
executor:submit(check_note, nil, entry, relative_path) | ||
end, | ||
}) | ||
|
||
executor:join_and_then(5000, function() | ||
---@diagnostic disable-next-line: undefined-field | ||
local runtime = math.floor((vim.loop.hrtime() - start_time) / 1000000) | ||
local messages = { "Checked " .. tostring(count) .. " notes in " .. runtime .. "ms" } | ||
local log_level = vim.log.levels.INFO | ||
if #warnings > 0 then | ||
messages[#messages + 1] = "\nThere were " .. tostring(#warnings) .. " warning(s):" | ||
log_level = vim.log.levels.WARN | ||
for warning in iter(warnings) do | ||
messages[#messages + 1] = " " .. warning | ||
end | ||
end | ||
if #errors > 0 then | ||
messages[#messages + 1] = "\nThere were " .. tostring(#errors) .. " error(s):" | ||
for err in iter(errors) do | ||
messages[#messages + 1] = " " .. err | ||
end | ||
log_level = vim.log.levels.ERROR | ||
end | ||
log.log(table.concat(messages, "\n"), log_level) | ||
end) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
local Path = require "plenary.path" | ||
local RefTypes = require("obsidian.search").RefTypes | ||
local util = require "obsidian.util" | ||
local log = require "obsidian.log" | ||
local iter = require("obsidian.itertools").iter | ||
|
||
---@param client obsidian.Client | ||
return function(client, _) | ||
local location, name, link_type = util.cursor_link(nil, nil, true) | ||
if location == nil then | ||
return | ||
end | ||
|
||
-- Check if it's a URL. | ||
if util.is_url(location) then | ||
if client.opts.follow_url_func ~= nil then | ||
client.opts.follow_url_func(location) | ||
else | ||
log.warn "This looks like a URL. You can customize the behavior of URLs with the 'follow_url_func' option." | ||
end | ||
return | ||
end | ||
|
||
-- Remove links from the end if there are any. | ||
local header_link = location:match "#[%a%d%s-_^]+$" | ||
if header_link ~= nil then | ||
location = location:sub(1, -header_link:len() - 1) | ||
end | ||
|
||
local buf_cwd = vim.fs.basename(vim.api.nvim_buf_get_name(0)) | ||
|
||
-- Search for matching notes. | ||
-- TODO: handle case where there are multiple matches by prompting user to choose. | ||
client:resolve_note_async(location, function(note) | ||
if note == nil and (link_type == RefTypes.Wiki or link_type == RefTypes.WikiWithAlias) then | ||
vim.schedule(function() | ||
local confirmation = string.lower(vim.fn.input { | ||
prompt = "Create new note '" .. location .. "'? [Y/n] ", | ||
}) | ||
if confirmation == "y" or confirmation == "yes" then | ||
-- Create a new note. | ||
local aliases = name == location and {} or { name } | ||
note = client:new_note(location, nil, nil, aliases) | ||
vim.api.nvim_command("e " .. tostring(note.path)) | ||
else | ||
log.warn "Aborting" | ||
end | ||
end) | ||
elseif note ~= nil then | ||
-- Go to resolved note. | ||
local path = note.path | ||
assert(path) | ||
vim.schedule(function() | ||
vim.api.nvim_command("e " .. tostring(path)) | ||
end) | ||
else | ||
local paths_to_check = { client:vault_root() / location, Path:new(location) } | ||
if buf_cwd ~= nil then | ||
paths_to_check[#paths_to_check + 1] = Path:new(buf_cwd) / location | ||
end | ||
|
||
for path in iter(paths_to_check) do | ||
if path:is_file() then | ||
return vim.schedule(function() | ||
vim.api.nvim_command("e " .. tostring(path)) | ||
end) | ||
end | ||
end | ||
return log.err("Failed to resolve file '" .. location .. "'") | ||
end | ||
end) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
local Note = require "obsidian.note" | ||
local util = require "obsidian.util" | ||
local iter = require("obsidian.itertools").iter | ||
|
||
local command_lookups = { | ||
ObsidianCheck = "obsidian.commands.check", | ||
ObsidianToday = "obsidian.commands.today", | ||
ObsidianYesterday = "obsidian.commands.yesterday", | ||
ObsidianTomorrow = "obsidian.commands.tomorrow", | ||
ObsidianNew = "obsidian.commands.new", | ||
ObsidianOpen = "obsidian.commands.open", | ||
ObsidianBacklinks = "obsidian.commands.backlinks", | ||
ObsidianSearch = "obsidian.commands.search", | ||
ObsidianTemplate = "obsidian.commands.template", | ||
ObsidianQuickSwitch = "obsidian.commands.quick_switch", | ||
ObsidianLinkNew = "obsidian.commands.link_new", | ||
ObsidianLink = "obsidian.commands.link", | ||
ObsidianFollowLink = "obsidian.commands.follow_link", | ||
ObsidianWorkspace = "obsidian.commands.workspace", | ||
ObsidianRename = "obsidian.commands.rename", | ||
ObsidianPasteImg = "obsidian.commands.paste_img", | ||
} | ||
|
||
local M = setmetatable({ | ||
commands = {}, | ||
}, { | ||
__index = function(t, k) | ||
local require_path = command_lookups[k] | ||
if not require_path then | ||
return | ||
end | ||
|
||
local mod = require(require_path) | ||
t[k] = mod | ||
|
||
return mod | ||
end, | ||
}) | ||
|
||
---@class obsidian.CommandConfig | ||
---@field opts table | ||
---@field complete function|? | ||
---@field func function|? (obsidian.Client, table) -> nil | ||
|
||
---Register a new command. | ||
---@param name string | ||
---@param config obsidian.CommandConfig | ||
M.register = function(name, config) | ||
if not config.func then | ||
config.func = function(client, data) | ||
return M[name](client, data) | ||
end | ||
end | ||
M.commands[name] = config | ||
end | ||
|
||
---Install all commands. | ||
--- | ||
---@param client obsidian.Client | ||
M.install = function(client) | ||
for command_name, command_config in pairs(M.commands) do | ||
local func = function(data) | ||
command_config.func(client, data) | ||
end | ||
|
||
if command_config.complete ~= nil then | ||
command_config.opts.complete = function(arg_lead, cmd_line, cursor_pos) | ||
return command_config.complete(client, arg_lead, cmd_line, cursor_pos) | ||
end | ||
end | ||
|
||
vim.api.nvim_create_user_command(command_name, func, command_config.opts) | ||
end | ||
end | ||
|
||
---@param client obsidian.Client | ||
---@return string[] | ||
M.complete_args_search = function(client, _, cmd_line, _) | ||
local query | ||
local cmd_arg, _ = util.lstrip_whitespace(string.gsub(cmd_line, "^.*Obsidian[A-Za-z0-9]+", "")) | ||
if string.len(cmd_arg) > 0 then | ||
if string.find(cmd_arg, "|", 1, true) then | ||
return {} | ||
else | ||
query = cmd_arg | ||
end | ||
else | ||
local _, csrow, cscol, _ = unpack(vim.fn.getpos "'<") | ||
local _, cerow, cecol, _ = unpack(vim.fn.getpos "'>") | ||
local lines = vim.fn.getline(csrow, cerow) | ||
assert(type(lines) == "table") | ||
|
||
if #lines > 1 then | ||
lines[1] = string.sub(lines[1], cscol) | ||
lines[#lines] = string.sub(lines[#lines], 1, cecol) | ||
elseif #lines == 1 then | ||
lines[1] = string.sub(lines[1], cscol, cecol) | ||
else | ||
return {} | ||
end | ||
|
||
query = table.concat(lines, " ") | ||
end | ||
|
||
local completions = {} | ||
local query_lower = string.lower(query) | ||
for note in iter(client:find_notes(query, { sort = true })) do | ||
local note_path = assert(client:vault_relative_path(note.path)) | ||
if string.find(string.lower(note:display_name()), query_lower, 1, true) then | ||
table.insert(completions, note:display_name() .. " " .. note_path) | ||
else | ||
for _, alias in pairs(note.aliases) do | ||
if string.find(string.lower(alias), query_lower, 1, true) then | ||
table.insert(completions, alias .. " " .. note_path) | ||
break | ||
end | ||
end | ||
end | ||
end | ||
|
||
return completions | ||
end | ||
|
||
M.complete_args_id = function(_, _, cmd_line, _) | ||
local cmd_arg, _ = util.lstrip_whitespace(string.gsub(cmd_line, "^.*Obsidian[A-Za-z0-9]+", "")) | ||
if string.len(cmd_arg) > 0 then | ||
return {} | ||
else | ||
local note_id = util.cursor_link() | ||
if note_id == nil then | ||
local bufpath = vim.api.nvim_buf_get_name(vim.fn.bufnr()) | ||
local note = Note.from_file(bufpath) | ||
note_id = tostring(note.id) | ||
end | ||
return { note_id } | ||
end | ||
end | ||
|
||
---Check the directory for notes with missing/invalid frontmatter. | ||
M.register("ObsidianCheck", { opts = { nargs = 0 } }) | ||
|
||
---Create or open a new daily note. | ||
M.register("ObsidianToday", { opts = { nargs = "?" } }) | ||
|
||
---Create (or open) the daily note from the last weekday. | ||
M.register("ObsidianYesterday", { opts = { nargs = 0 } }) | ||
|
||
---Create (or open) the daily note for the next weekday. | ||
M.register("ObsidianTomorrow", { opts = { nargs = 0 } }) | ||
|
||
---Create a new note. | ||
M.register("ObsidianNew", { opts = { nargs = "?" } }) | ||
|
||
---Open a note in the Obsidian app. | ||
M.register("ObsidianOpen", { opts = { nargs = "?" }, complete = M.complete_args_search }) | ||
|
||
---Get backlinks to a note. | ||
M.register("ObsidianBacklinks", { opts = { nargs = 0 } }) | ||
|
||
---Search notes. | ||
M.register("ObsidianSearch", { opts = { nargs = "?" } }) | ||
|
||
--- Insert a template | ||
M.register("ObsidianTemplate", { opts = { nargs = "?" } }) | ||
|
||
---Quick switch to an obsidian note | ||
M.register("ObsidianQuickSwitch", { opts = { nargs = 0 } }) | ||
|
||
---Create a new note and link to it. | ||
M.register("ObsidianLinkNew", { opts = { nargs = "?", range = true } }) | ||
|
||
---Create a link to an existing note on the current visual selection. | ||
M.register("ObsidianLink", { opts = { nargs = "?", range = true }, complete = M.complete_args_search }) | ||
|
||
---Follow link under cursor. | ||
M.register("ObsidianFollowLink", { opts = { nargs = 0 } }) | ||
|
||
---Switch to a different workspace. | ||
M.register("ObsidianWorkspace", { opts = { nargs = "?" } }) | ||
|
||
---Rename a note and update all backlinks. | ||
M.register("ObsidianRename", { opts = { nargs = 1 }, complete = M.complete_args_id }) | ||
|
||
---Paste an image into a note. | ||
M.register("ObsidianPasteImg", { opts = { nargs = "?", complete = "file" } }) | ||
|
||
return M |
Oops, something went wrong.