Open sotte opened 10 months ago
Sounds like a good feature. I'm happy to accept a PR with this as a default mapping.
My dumb pragmatic implementation of this. Just a mapping via lazyvim really.
return {
{
"epwalsh/obsidian.nvim",
...
keys = {
{
"<cr>",
function()
local util = require("obsidian.util")
if util.cursor_on_markdown_link(nil, nil, true) then
vim.cmd("ObsidianFollowLink")
else
util.toggle_checkbox()
end
end,
desc = "Smart action: follow/toggle",
ft = "markdown",
},
It's slightly annoying that one action is a command and one a lua function that actually does something.
I'll play around with this the next few days and might create a MR. But if someone else comes firs, I'm not offended.
I'm also playing with adding "show notes with tag if cursor is on tag". Not robust yet, but quite nice.
I wanted to add ObsidianLink
to SmartAction
, i.e. trigger ObsidianLink
when I'm in visual mode. This is the code that IMO should do the trick:
ObsidianSmartAction = function(opts)
-- opts comes from the user command
-- https://neovim.io/doc/user/lua-guide.html#lua-guide-commands-create
-- print(vim.inspect(opts))
local mode = vim.api.nvim_get_mode().mode
if mode == "v" then
local client = require("obsidian").get_client()
client:command("ObsidianLink", opts)
return
end
local obs_util = require("obsidian.util")
if obs_util.cursor_on_markdown_link(nil, nil, true) then
vim.cmd("ObsidianFollowLink")
else
vim.cmd("SmartToggleTask")
end
end
I have the problem, that the visual selection is not consistently passed to the function. Sometimes I get the previous selection. Sometimes I get the right one. Sometimes I get no selection at all and the subsequent error message: "ObsidianLink must be called with visual selection". I'm a bit lost how to best call ObsidianLink
.
Do you have any suggestions?
Well, registering the mapping with :SmartAction<cr>
instead of <cmd>SmartAction<cr>
does the trick. But it seems more of a workaround than a proper solution.
Some relevant resources:
Just a quick update on what I use in normal mode now:
ObsidianSmartAction = function()
local obs_util = require("obsidian.util")
-- follow link if possible
if obs_util.cursor_on_markdown_link(nil, nil, true) then
vim.cmd("ObsidianFollowLink")
return
end
-- show tags if possible
local current_wrod = vim.fn.expand("<cWORD>")
if current_wrod:match("^#") then
vim.cmd("ObsidianTags")
return
end
-- toggle task if possible
-- custom implementation that cycles through [ ] [~] [>] [x]
vim.cmd("ToggleTask")
end
Hi @epwalsh I just followed @sotte 's idea and did a custom implementation for smart action here
We could merge this, or a version of this, into obsidian.nvim
or create an entry in the cookbook https://github.com/epwalsh/obsidian.nvim/discussions/categories/cookbook.
I am loving the smart action! Nice work!
One question I have is whether we can add to the "Toggle Checkbox" action to add to the cycle removing the checkbox all together which then leave just a regular list item.
The cycle would be something like this:
-
(regular list item with no checkbox)- [ ]
(open checkbox)- [x]
(completed checkbox)- [n]
(any other custom statuses like ? / - etc.)Thoughts on this?
One question I have is whether we can add to the "Toggle Checkbox" action to add to the cycle removing the checkbox all together which then leave just a regular list item.
Here is my super rough and quick implementation of this on my side overriding the mapping for smart action (included below is just my list toggle override function... I also have the follow link and tag recognition checked for in the actual smart action function keymap):
local my_list_toggle = function()
local line_num = unpack(vim.api.nvim_win_get_cursor(0))
local line = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num, false)[1]
local checkbox_pattern = '^%s*- %[.] '
local checkboxes_cycle = { ' ', 'x' }
if not string.match(line, checkbox_pattern) then
-- not a current checkbox
local unordered_list_pattern = '^(%s*)- (.*)$'
if string.match(line, unordered_list_pattern) then
-- this is a regular list item, so make it a checkbox
line = string.gsub(line, unordered_list_pattern, '%1- [ ] %2')
else
-- not a list item at all so make it a list item (NOT a checkbox)
line = string.gsub(line, '^(%s*)', '%1- ')
end
else
-- already a checkbox so just toggle it to the next item
local cur_cb_pattern = '^%s*- %[(.)%] '
local cur_cb_char = string.match(line, cur_cb_pattern)
if cur_cb_char == nil then
vim.notify('Error: could not find current checkbox state')
return
end
local replace_cb_pattern = '^(%s*)- %[.%] '
-- find the current character and replace it with the next one in the checkboxes list
local found_match = false
for i, check_char in ipairs(checkboxes_cycle) do
if cur_cb_char == check_char then
found_match = true
local next_cb = ''
if i == #checkboxes_cycle then
-- last item in cycle so change to a regular list item
next_cb = ''
else
next_cb = '[' .. checkboxes_cycle[i + 1] .. '] '
end
line = string.gsub(line, replace_cb_pattern, '%1- ' .. next_cb)
break
end
end
if not found_match then
vim.notify('Error: could not find current checkbox state in cycle list')
return
end
end
vim.schedule(function()
vim.api.nvim_buf_set_lines(0, line_num - 1, line_num, true, { line })
end)
end
🚀 The feature, motivation and pitch
Often there is an obvious action to perform depending on the context (textobject the cursor is on). Why not offer a smart mapping, maybe enter, to do the right thing based on the context:
(I'm using a similar mapping in orgmode and I love it.)
Alternatives
Additional context
No response