Skip to content

Commit d9be302

Browse files
committed
Allow viz selection or cursor location with :ObsidianTags command
1 parent 6b2e0f5 commit d9be302

File tree

5 files changed

+82
-6
lines changed

5 files changed

+82
-6
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Added note method `Note.get_field(key: string)`.
1717
- Added note method ` Note.save_to_buffer(bufnr: integer|?, frontmatter: table|?)` for saving the frontmatter to a buffer.
1818

19+
### Changed
20+
21+
- `:ObsidianTags` command can take a visual selection or look for a tag under the cursor instead of explicitly provided arguments.
22+
1923
## [v2.8.0](https://github.com/epwalsh/obsidian.nvim/releases/tag/v2.8.0) - 2024-01-26
2024

2125
### Added

lua/obsidian/commands/init.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ M.register("ObsidianOpen", { opts = { nargs = "?" }, complete = M.complete_args_
159159
M.register("ObsidianBacklinks", { opts = { nargs = 0 } })
160160

161161
---Find all instances of any number of tags.
162-
M.register("ObsidianTags", { opts = { nargs = "+" } })
162+
M.register("ObsidianTags", { opts = { nargs = "*", range = true } })
163163

164164
---Search notes.
165165
M.register("ObsidianSearch", { opts = { nargs = "?" } })

lua/obsidian/commands/tags.lua

+53-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
local log = require "obsidian.log"
22
local util = require "obsidian.util"
3+
local search = require "obsidian.search"
34
local LocationList = require "obsidian.location_list"
45

56
---@param client obsidian.Client
6-
return function(client, data)
7+
---@param tags string[]
8+
local function gather_tag_location_list(client, tags)
79
-- Gather tag locations.
810
local tag_locations = {}
9-
local tags = util.tbl_unique(data.fargs)
1011
local tag_locs = client:find_tags(tags, { sort = true })
1112
for _, tag_loc in ipairs(tag_locs) do
1213
for _, tag in ipairs(tags) do
@@ -92,9 +93,57 @@ return function(client, data)
9293
loclist:render(view_lines, folds, highlights)
9394

9495
log.info(
95-
"Showing tag locations.\n\n"
96+
"Showing tag locations for '%s'.\n\n"
9697
.. "TIPS:\n\n"
9798
.. "- Hit ENTER on a match to go to the tag location\n"
98-
.. "- Hit ENTER on a group header to toggle the fold, or use normal fold mappings"
99+
.. "- Hit ENTER on a group header to toggle the fold, or use normal fold mappings",
100+
table.concat(tags, "', '")
99101
)
100102
end
103+
104+
---@param client obsidian.Client
105+
return function(client, data)
106+
local tags = data.fargs
107+
108+
if vim.tbl_isempty(tags) then
109+
-- Check for visual selection.
110+
local _, csrow, cscol, _ = unpack(assert(vim.fn.getpos "'<"))
111+
local _, cerow, cecol, _ = unpack(assert(vim.fn.getpos "'>"))
112+
if data.line1 == csrow and data.line2 == cerow then
113+
local lines = vim.fn.getline(csrow, cerow)
114+
if #lines ~= 1 then
115+
log.err "Only in-line visual selections allowed"
116+
return
117+
end
118+
119+
local line = assert(lines[1])
120+
local tag = string.sub(line, cscol, cecol)
121+
122+
if not string.match(tag, "^#?" .. search.Patterns.TagCharsRequired .. "$") then
123+
log.err("Visual selection '%s' is not a valid tag", tag)
124+
return
125+
end
126+
127+
if vim.startswith(tag, "#") then
128+
tag = string.sub(tag, 2)
129+
end
130+
131+
tags = { tag }
132+
else
133+
-- Otherwise check for a tag under the cursor.
134+
local tag = util.cursor_tag()
135+
if tag then
136+
tags = { tag }
137+
end
138+
end
139+
end
140+
141+
if vim.tbl_isempty(tags) then
142+
log.warn "Please provide a tag argument"
143+
return
144+
end
145+
146+
tags = util.tbl_unique(tags)
147+
148+
return gather_tag_location_list(client, tags)
149+
end

lua/obsidian/search.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ M.Patterns = {
2626
-- Tags
2727
TagCharsOptional = "[A-Za-z0-9_/-]*",
2828
TagCharsRequired = "[A-Za-z]+[A-Za-z0-9_/-]*[A-Za-z0-9]+", -- assumes tag is at least 2 chars
29+
Tag = "#[A-Za-z]+[A-Za-z0-9_/-]*[A-Za-z0-9]+",
2930

3031
-- Miscellaneous
3132
Highlight = "==[^=]+==", -- ==text==
@@ -35,7 +36,6 @@ M.Patterns = {
3536
Wiki = "%[%[[^][%|]+%]%]", -- [[xxx]]
3637
Markdown = "%[[^][]+%]%([^%)]+%)", -- [yyy](xxx)
3738
NakedUrl = "https?://[a-zA-Z0-9._-]+[a-zA-Z0-9._#/=&?:%%-]+[a-zA-Z0-9]", -- https://xyz.com
38-
Tag = "#[a-zA-Z0-9_/-]+", -- #tag
3939
}
4040

4141
--- Find all matches of a pattern

lua/obsidian/util.lua

+23
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,29 @@ util.cursor_link = function(line, col, include_naked_urls)
605605
return link_location, link_name, link_type
606606
end
607607

608+
--- Get the tag under the cursor, if there is one.
609+
---
610+
---@param line string|?
611+
---@param col integer|?
612+
---
613+
---@return string|?
614+
util.cursor_tag = function(line, col)
615+
local search = require "obsidian.search"
616+
617+
local current_line = line and line or vim.api.nvim_get_current_line()
618+
local _, cur_col = unpack(vim.api.nvim_win_get_cursor(0))
619+
cur_col = col or cur_col + 1 -- nvim_win_get_cursor returns 0-indexed column
620+
621+
for match in iter(search.find_tags(current_line)) do
622+
local open, close, _ = unpack(match)
623+
if open <= cur_col and cur_col <= close then
624+
return string.sub(current_line, open + 1, close)
625+
end
626+
end
627+
628+
return nil
629+
end
630+
608631
util.gf_passthrough = function()
609632
if util.cursor_on_markdown_link(nil, nil, true) then
610633
return "<cmd>ObsidianFollowLink<CR>"

0 commit comments

Comments
 (0)