Skip to content

Commit

Permalink
Added :ObsidianToggleCheckbox cmd and smart action mapping (#329) (#…
Browse files Browse the repository at this point in the history
…518)

* Added smart action to perform depending on the cursor context (#329)

* Fix: change toggle_checkbox default opts & make opts' sorting function more robust

* clean up

---------

Co-authored-by: CaeChao <winston@An0nym0us1q84>
Co-authored-by: epwalsh <petew@allenai.org>
  • Loading branch information
3 people committed Apr 1, 2024
1 parent a7e2ec9 commit 2f9b95a
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 11 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed bug with YAML parser where it would fail to parse field names with spaces in them.
- Fixed bug with YAML parser where it would incorrectly identity comments.

### Added

- Added a smart action mapping on `<CR>`, that depends on the context. It will follow links or toggle checkboxes.

### Changed

- Added `order` field to `ui.checkboxes` config fields that determines the order in which the different checkbox characters are cycled through via `:ObsidianToggleCheckbox` and the smart action.

## [v3.7.5](https://github.com/epwalsh/obsidian.nvim/releases/tag/v3.7.5) - 2024-03-22

### Fixed
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ _Keep in mind this plugin is not meant to replace Obsidian, but to complement it

- `:ObsidianRename [NEWNAME] [--dry-run]` to rename the note of the current buffer or reference under the cursor, updating all backlinks across the vault. Since this command is still relatively new and could potentially write a lot of changes to your vault, I highly recommend committing the current state of your vault (if you're using version control) before running it, or doing a dry-run first by appending "--dry-run" to the command, e.g. `:ObsidianRename new-id --dry-run`.

- `:ObsidianToggleCheckbox` to cycle through checkbox options.

### Demo

[![2024-01-31 14 22 52](https://github.com/epwalsh/obsidian.nvim/assets/8812459/2986e1d2-13e8-40e2-9c9e-75691a3b662e)](https://github.com/epwalsh/obsidian.nvim/assets/8812459/2986e1d2-13e8-40e2-9c9e-75691a3b662e)
Expand Down Expand Up @@ -279,6 +281,13 @@ This is a complete list of all of the options that can be passed to `require("ob
end,
opts = { buffer = true },
},
-- Smart action depending on context, either follow link or toggle checkbox.
["<cr>"] = {
action = function()
return require("obsidian").util.smart_action()
end,
opts = { buffer = true },
}
},

-- Where to put new notes. Valid options are
Expand Down
3 changes: 3 additions & 0 deletions lua/obsidian/commands/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local iter = require("obsidian.itertools").iter

local command_lookups = {
ObsidianCheck = "obsidian.commands.check",
ObsidianToggleCheckbox = "obsidian.commands.toggle_checkbox",
ObsidianToday = "obsidian.commands.today",
ObsidianYesterday = "obsidian.commands.yesterday",
ObsidianTomorrow = "obsidian.commands.tomorrow",
Expand Down Expand Up @@ -179,6 +180,8 @@ M.register("ObsidianLinks", { opts = { nargs = 0, desc = "Collect all links with

M.register("ObsidianFollowLink", { opts = { nargs = "?", desc = "Follow reference or link under cursor" } })

M.register("ObsidianToggleCheckbox", { opts = { nargs = 0, desc = "Toggle checkbox" } })

M.register("ObsidianWorkspace", { opts = { nargs = "?", desc = "Check or switch workspace" } })

M.register(
Expand Down
10 changes: 10 additions & 0 deletions lua/obsidian/commands/toggle_checkbox.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
local toggle_checkbox = require("obsidian.util").toggle_checkbox

---@param client obsidian.Client
return function(client)
local checkboxes = vim.tbl_keys(client.opts.ui.checkboxes)
table.sort(checkboxes, function(a, b)
return (client.opts.ui.checkboxes[a].order or 1000) < (client.opts.ui.checkboxes[b].order or 1000)
end)
toggle_checkbox(checkboxes)
end
26 changes: 21 additions & 5 deletions lua/obsidian/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ config.ClientOpts.normalize = function(opts, defaults)
opts.tags = nil
end

if opts.ui and opts.ui.checkboxes then
-- Add a default 'order' for backwards compat.
for i, char in ipairs { " ", "x" } do
if opts.ui.checkboxes[char] and not opts.ui.checkboxes[char].order then
opts.ui.checkboxes[char].order = i
end
end
end

--------------------------
-- Merge with defaults. --
--------------------------
Expand Down Expand Up @@ -281,6 +290,7 @@ config.MappingOpts.default = function()
return {
["gf"] = mappings.gf_passthrough(),
["<leader>ch"] = mappings.toggle_checkbox(),
["<cr>"] = mappings.smart_action(),
}
end

Expand Down Expand Up @@ -381,7 +391,7 @@ end
---
---@field enable boolean
---@field update_debounce integer
---@field checkboxes table<string, obsidian.config.UICharSpec>
---@field checkboxes table<string, obsidian.config.CheckboxSpec>
---@field bullets obsidian.config.UICharSpec|?
---@field external_link_icon obsidian.config.UICharSpec
---@field reference_text obsidian.config.UIStyleSpec
Expand All @@ -396,6 +406,12 @@ config.UIOpts = {}
---@field char string
---@field hl_group string

---@class obsidian.config.CheckboxSpec : obsidian.config.UICharSpec
---
---@field char string
---@field hl_group string
---@field order integer

---@class obsidian.config.UIStyleSpec
---
---@field hl_group string
Expand All @@ -406,10 +422,10 @@ config.UIOpts.default = function()
enable = true,
update_debounce = 200,
checkboxes = {
[" "] = { char = "󰄱", hl_group = "ObsidianTodo" },
["x"] = { char = "", hl_group = "ObsidianDone" },
[">"] = { char = "", hl_group = "ObsidianRightArrow" },
["~"] = { char = "󰰱", hl_group = "ObsidianTilde" },
[" "] = { order = 1, char = "󰄱", hl_group = "ObsidianTodo" },
["~"] = { order = 2, char = "󰰱", hl_group = "ObsidianTilde" },
[">"] = { order = 3, char = "", hl_group = "ObsidianRightArrow" },
["x"] = { order = 4, char = "", hl_group = "ObsidianDone" },
},
bullets = { char = "", hl_group = "ObsidianBullet" },
external_link_icon = { char = "", hl_group = "ObsidianExtLinkIcon" },
Expand Down
4 changes: 4 additions & 0 deletions lua/obsidian/mappings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ local M = {}
---@field opts table

---@return obsidian.mappings.MappingConfig
M.smart_action = function()
return { action = util.smart_action, opts = { noremap = false, expr = true, buffer = true } }
end

M.gf_passthrough = function()
return { action = util.gf_passthrough, opts = { noremap = false, expr = true, buffer = true } }
end
Expand Down
26 changes: 20 additions & 6 deletions lua/obsidian/util.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local iter = require("obsidian.itertools").iter
local enumerate = require("obsidian.itertools").enumerate
local log = require "obsidian.log"

local util = {}
Expand Down Expand Up @@ -492,26 +493,28 @@ util.zettel_id = function()
end

---Toggle the checkbox on the line that the cursor is on.
util.toggle_checkbox = function()
util.toggle_checkbox = function(opts)
local line_num = unpack(vim.api.nvim_win_get_cursor(0)) -- 1-indexed
local line = vim.api.nvim_get_current_line()

local checkbox_pattern = "^%s*- %[.] "
local checkboxes = opts or { " ", "x" }

if not string.match(line, checkbox_pattern) then
local unordered_list_pattern = "^(%s*)[-*+] (.*)"

if string.match(line, unordered_list_pattern) then
line = string.gsub(line, unordered_list_pattern, "%1- [ ] %2")
else
line = string.gsub(line, "^(%s*)", "%1- [ ] ")
return
end
elseif string.match(line, "^%s*- %[ %].*") then
line = util.string_replace(line, "- [ ]", "- [x]", 1)
else
for check_char in iter { "x", "~", ">", "-" } do
for i, check_char in enumerate(checkboxes) do
if string.match(line, "^%s*- %[" .. check_char .. "%].*") then
line = util.string_replace(line, "- [" .. check_char .. "]", "- [ ]", 1)
if i == #checkboxes then
i = 0
end
line = util.string_replace(line, "- [" .. check_char .. "]", "- [" .. checkboxes[i + 1] .. "]", 1)
break
end
end
Expand Down Expand Up @@ -737,6 +740,17 @@ util.gf_passthrough = function()
end
end

util.smart_action = function()
-- follow link if possible
if util.cursor_on_markdown_link(nil, nil, true) then
return "<cmd>ObsidianFollowLink<CR>"
end

-- toggle task if possible
-- cycles through your custom UI checkboxes, default: [ ] [~] [>] [x]
return "<cmd>ObsidianToggleCheckbox<CR>"
end

---Get the path to where a plugin is installed.
---@param name string|?
---@return string|?
Expand Down

0 comments on commit 2f9b95a

Please sign in to comment.