Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dotfiles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
- role: zellij
tags:
- "zellij"
- role: wezterm
tags:
- "wezterm"
- role: nvim
tags:
- "nvim"
Expand Down
6 changes: 4 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ test:
# Run the code linters: YAML (yamllint) + Ansible (ansible-lint) + Lua
# (lua-language-server + selene). Orthogonal to `check` (prek format/syntax
# hooks); CI runs both.
lint: lint-yaml lint-ansible
just nvim lint
lint: lint-yaml lint-ansible lint-vim

# Lint YAML with yamllint (config: .yamllint).
lint-yaml DIR=".":
Expand All @@ -33,6 +32,9 @@ lint-yaml DIR=".":
lint-ansible:
uvx ansible-lint

lint-vim:
just nvim lint

# Run a dry run of the Ansible playbook to see what changes would be made
dry-run *ARGS:
ansible-playbook -i hosts dotfiles.yml -v --check --diff {{ ARGS }}
Expand Down
89 changes: 89 additions & 0 deletions roles/nvim/files/ftplugin/markdown.lua
Original file line number Diff line number Diff line change
@@ -1 +1,90 @@
vim.wo.conceallevel = (vim.bo.readonly or vim.bo.buftype == "nofile") and 2 or 0

local diagram_backend = require("config.diagram_backend")

if vim.env.ZELLIJ == nil or diagram_backend.get() ~= nil or vim.b.mermaid_external_viewer_configured then
return
end

vim.b.mermaid_external_viewer_configured = true

local function mermaid_source_at_cursor()
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local cursor_row = vim.api.nvim_win_get_cursor(0)[1]
local opening_row

for row = cursor_row, 1, -1 do
local line = lines[row]
if line:match("^%s*```") then
if line:match("^%s*```%s*mermaid%s*$") then
opening_row = row
break
end
return nil
end
end

if not opening_row then
return nil
end

local source = {}
for row = opening_row + 1, #lines do
local line = lines[row]
if line:match("^%s*```%s*$") then
return table.concat(source, "\n")
end
table.insert(source, line)
end
end

local function open_mermaid_at_cursor()
local source = mermaid_source_at_cursor()
if not source then
vim.notify("Place the cursor inside a Mermaid code fence", vim.log.levels.WARN, { title = "Mermaid" })
return
end

if vim.fn.executable("mmdc") == 0 then
vim.notify("mmdc is required to render Mermaid diagrams", vim.log.levels.ERROR, { title = "Mermaid" })
return
end

local cache_dir = vim.fn.stdpath("cache") .. "/mermaid"
vim.fn.mkdir(cache_dir, "p")

local input_path = vim.fn.tempname()
local output_path = cache_dir .. "/" .. vim.fn.sha256(source) .. ".png"
vim.fn.writefile(vim.split(source, "\n"), input_path)

local stderr = {}
vim.fn.jobstart({ "mmdc", "-i", input_path, "-o", output_path, "-t", "dark" }, {
on_stderr = function(_, data)
for _, line in ipairs(data) do
if line ~= "" then
table.insert(stderr, line)
end
end
end,
on_exit = function(_, exit_code)
vim.schedule(function()
vim.fn.delete(input_path)
if exit_code ~= 0 or vim.fn.filereadable(output_path) == 0 then
local message = #stderr > 0 and table.concat(stderr, "\n") or "mmdc exited with code " .. exit_code
vim.notify(message, vim.log.levels.ERROR, { title = "Mermaid render failed" })
return
end
vim.ui.open(output_path)
end)
end,
})
end

vim.api.nvim_buf_create_user_command(0, "MermaidOpen", open_mermaid_at_cursor, {
desc = "Render Mermaid fence at cursor",
})

vim.keymap.set("n", "<leader>mo", "<cmd>MermaidOpen<cr>", {
buffer = true,
desc = "Open Mermaid diagram",
})
15 changes: 8 additions & 7 deletions roles/nvim/files/lazy-lock.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"FixCursorHold.nvim": { "branch": "master", "commit": "1900f89dc17c603eec29960f57c00bd9ae696495" },
"HowMuch": { "branch": "master", "commit": "787ead49e6311dc77b8e07a3aca79cbd6feab34c" },
"LuaSnip": { "branch": "master", "commit": "458560534a73f7f8d7a11a146c801db00b081df0" },
"LuaSnip": { "branch": "master", "commit": "642b0c595e11608b4c18219e93b88d7637af27bc" },
"actions-preview.nvim": { "branch": "master", "commit": "0ac9c2aa3cfc8c885321c0862b50b6b1c3392405" },
"ansi.nvim": { "branch": "main", "commit": "195b64c3da1c22c2e95648dcbdc6ed075d507064" },
"baleia.nvim": { "branch": "main", "commit": "710537ff5cd669c5a76c5f5b6a9169fd9b913d18" },
Expand All @@ -11,8 +11,8 @@
"blink.lib": { "branch": "main", "commit": "5876dd95deeb70aadbe9f1c0b7117a135061cdac" },
"cmp-digraphs": { "branch": "master", "commit": "5efc1f0078d7c5f3ea1c8e3aad04da3fd6e081a9" },
"codecompanion-history.nvim": { "branch": "main", "commit": "bc1b4fe06eaaf0aa2399be742e843c22f7f1652a" },
"codecompanion.nvim": { "branch": "main", "commit": "c8bd2d0ff58b75d9a76f0a17115581754824b725" },
"copilot.lua": { "branch": "master", "commit": "760912b5992e1d6437378a9c7f2b0cb950966cf5" },
"codecompanion.nvim": { "branch": "main", "commit": "4fb6e67d537d88d1a642c623efcbbee80d7d231c" },
"copilot.lua": { "branch": "master", "commit": "d521d3959f81f4fa58870ed329754f1dd5057511" },
"crates.nvim": { "branch": "main", "commit": "694357861ec9ebf12475ddcdd04ea45a0923c32d" },
"diagram.nvim": { "branch": "master", "commit": "a221810b17cdda2d5fdddba9bab3eba6fab8fabc" },
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
Expand All @@ -23,14 +23,13 @@
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
"git-worktree.nvim": { "branch": "main", "commit": "3ad8c17a3d178ac19be925284389c14114638ebb" },
"gitsigns.nvim": { "branch": "main", "commit": "eb60cc7b94c46005237fd34170d76f3a089a90aa" },
"hererocks": { "branch": "master", "commit": "3db37265c3839cbd4d27fc73f92fa7b58bc3a76f" },
"hunk.nvim": { "branch": "master", "commit": "c8a8e7b6973576f63e5bf39aa8338d7f37f4dc53" },
"hydra.nvim": { "branch": "main", "commit": "8c4a9f621ec7cdc30411a1f3b6d5eebb12b469dc" },
"image.nvim": { "branch": "master", "commit": "88351f1f7d9dbae286e671ce3690a49660dd8a5c" },
"iron.nvim": { "branch": "master", "commit": "88cd340407b959f1168af2a6ae5a3d180e6df32c" },
"jj-diffconflicts": { "branch": "main", "commit": "a2aa9a247b56d2c1a6f6be81bcf41c5450cc82ff" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"lean.nvim": { "branch": "main", "commit": "32fb8aa2edf3f172c307957dc7083e6c7f6caa7e" },
"lean.nvim": { "branch": "main", "commit": "f5771e07ec026aeff2915c338a4cb8e7eb7247a2" },
"lsp-format.nvim": { "branch": "master", "commit": "e2b4ae87545c1aadebcf1dfc1b5dffe28dcf2ffb" },
"lsp_signature.nvim": { "branch": "master", "commit": "b7ace9ddb1640ce266012a45a672dfdaedfa5ec6" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "47059d71b42d74b0a1e9f61c1d99d301039c3b5b" },
Expand All @@ -40,7 +39,7 @@
"mdx.nvim": { "branch": "main", "commit": "6c3dc7c4a4b58b5d86404c21a9b4655282ca08c5" },
"multicursors.nvim": { "branch": "main", "commit": "72225ea9e4443c3f4b9df91d0193e07c4ee8d382" },
"neodev.nvim": { "branch": "main", "commit": "46aa467dca16cf3dfe27098042402066d2ae242d" },
"neogit": { "branch": "master", "commit": "43ea1a0854052e583f647e0b35879f0158a70a78" },
"neogit": { "branch": "master", "commit": "6fc2fa890bd2031ed999c074daab0fb4feff20a5" },
"neotest": { "branch": "master", "commit": "fb84d87285698f7055f618083c351984449119b8" },
"neotest-plenary": { "branch": "master", "commit": "3523adcf9ffaad1911960c5813b0136c1b63a2ec" },
"neotest-python": { "branch": "master", "commit": "51c453d57f8d5156671b42ea57fafa2e1c9fb641" },
Expand All @@ -65,7 +64,7 @@
"pomo.nvim": { "branch": "main", "commit": "7e06e5221d8d1e596a0ab29dd4d7fcee5f3cd05a" },
"popup.nvim": { "branch": "master", "commit": "b7404d35d5d3548a82149238289fa71f7f6de4ac" },
"project.nvim": { "branch": "main", "commit": "8c6bad7d22eef1b71144b401c9f74ed01526a4fb" },
"schemastore.nvim": { "branch": "main", "commit": "6ff1f21b2e2b77ec59f7433ce2d9fbc052d908ac" },
"schemastore.nvim": { "branch": "main", "commit": "e954496f8ef22904e8a84f5078f4a110fdc7a0d3" },
"suda.vim": { "branch": "master", "commit": "c492741b4679b3cdd4d9e34138209784e061d916" },
"tcomment_vim": { "branch": "master", "commit": "48ab639a461d9b8344f7fee06cb69b4374863b13" },
"telescope-fzf-native.nvim": { "branch": "main", "commit": "b25b749b9db64d375d782094e2b9dce53ad53a40" },
Expand All @@ -82,6 +81,8 @@
"unison": { "branch": "trunk", "commit": "e1870038739ffcb27b4e3b483dafd2c21f6541b2" },
"vim-autoclose": { "branch": "master", "commit": "a9a3b7384657bc1f60a963fd6c08c63fc48d61c3" },
"vim-dadbod": { "branch": "master", "commit": "6d1d41da4873a445c5605f2005ad2c68c99d8770" },
"vim-dadbod-completion": { "branch": "master", "commit": "a8dac0b3cf6132c80dc9b18bef36d4cf7a9e1fe6" },
"vim-dadbod-ui": { "branch": "master", "commit": "afd07819d8efcefc3317205b855ad4e3513b0011" },
"vim-dispatch": { "branch": "master", "commit": "a2ff28abdb2d89725192db5b8562977d392a4d3f" },
"vim-endwise": { "branch": "master", "commit": "4994afb0cdf956d9a665a14b9c834869e602c396" },
"vim-matchup": { "branch": "master", "commit": "055881643e1b37038a572c883de7b1969716db77" },
Expand Down
14 changes: 14 additions & 0 deletions roles/nvim/files/lua/config/diagram_backend.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
local M = {}

function M.get()
if vim.env.WEZTERM_PANE ~= nil then
return "sixel"
end

local is_kitty = vim.env.KITTY_PID ~= nil or vim.env.TERM == "xterm-kitty"
if vim.env.ZELLIJ == nil and is_kitty then
return "kitty"
end
end

return M
2 changes: 1 addition & 1 deletion roles/nvim/files/lua/config/options.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
local options = {
-- clipboard = "unnamed,unnamedplus", --- Copy-paste between vim and everything else
cmdheight = 0, --- Give more space for displaying messages
cmdheight = 1, --- Give more space for displaying messages
completeopt = "menu,menuone,noselect", --- Better autocompletion
cursorline = true, --- Highlight of current line
emoji = false, --- Fix emoji display
Expand Down
46 changes: 29 additions & 17 deletions roles/nvim/files/lua/plugins/spec/diagram.lua
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
-- Inline rendering of mermaid (and other) diagrams directly in the buffer via
-- the kitty graphics protocol. Requires a graphics-capable terminal (kitty),
-- the `mmdc` CLI (@mermaid-js/mermaid-cli) to render mermaid, and ImageMagick
-- (`magick`) to process the images -- both installed by the packages role.
local diagram_backend = require("config.diagram_backend")

-- WezTerm supports Sixel, which also survives Zellij. Kitty graphics do not,
-- so use that backend only in direct Kitty sessions.
return {
{
"3rd/image.nvim",
event = "VeryLazy",
opts = {
backend = "kitty",
-- Use the ImageMagick CLI rather than the `magick` luarock, so there is
-- no luarocks build step to maintain.
processor = "magick_cli",
-- diagram.nvim drives the rendering; don't also auto-render markdown images.
integrations = {},
max_width_window_percentage = 80,
max_height_window_percentage = 80,
},
cond = function()
return diagram_backend.get() ~= nil
end,
opts = function()
return {
backend = diagram_backend.get(),
-- Use the ImageMagick CLI rather than the `magick` luarock, so there is
-- no luarocks build step to maintain.
processor = "magick_cli",
-- diagram.nvim drives the rendering; don't also auto-render markdown images.
integrations = {},
max_width_window_percentage = 80,
max_height_window_percentage = 80,
}
end,
},
{
"3rd/diagram.nvim",
dependencies = { "3rd/image.nvim" },
ft = { "markdown" },
cond = function()
return diagram_backend.get() ~= nil
end,
-- opts is a function so the integration module is required only after the
-- plugin is on the runtimepath.
opts = function()
return {
events = {
-- diagram.nvim clears diagrams on BufLeave. A buffer already shown
-- in another split does not emit BufWinEnter when focus returns, so
-- redraw it explicitly for the Sixel backend.
render_buffer = { "InsertLeave", "BufWinEnter", "TextChanged", "WinEnter" },
},
integrations = {
require("diagram.integrations.markdown"),
},
renderer_options = {
mermaid = {
theme = "dark",
-- mmdc drives Chromium via puppeteer; on Ubuntu 23.10+ AppArmor
cli_args = vim.env.DOTFILES_TRUSTED_MERMAID == "1"
and { "--puppeteerConfigFile", vim.fn.stdpath("config") .. "/puppeteer-config.json" }
or {},
cli_args = {},
},
},
}
Expand Down
24 changes: 22 additions & 2 deletions roles/nvim/files/lua/plugins/spec/editor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,28 @@ return {
event = "VeryLazy",
},
{
"tpope/vim-dadbod",
cmd = "DB",
"kristijanhusak/vim-dadbod-ui",
dependencies = {
{
"tpope/vim-dadbod",
lazy = true,
},
{
"kristijanhusak/vim-dadbod-completion",
ft = { "sql", "mysql", "plsql" },
lazy = true,
}, -- Optional
},
cmd = {
"DBUI",
"DBUIToggle",
"DBUIAddConnection",
"DBUIFindBuffer",
},
init = function()
-- Your DBUI configuration
vim.g.db_ui_use_nerd_fonts = 1
end,
},
{
"tpope/vim-dispatch",
Expand Down
5 changes: 2 additions & 3 deletions roles/nvim/files/lua/plugins/telescope/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ end, { desc = "Telescope find files (selected text)" })
vim.keymap.set("n", "<leader>pp", function()
builtin.find_files({ hidden = true, no_ignore = true })
end, { desc = "Telescope find hidden files" })
vim.keymap.set("n", "<leader>pb", builtin.buffers, { desc = "Telescope buffers" })
vim.keymap.set("n", "<leader>pd", function()
vim.keymap.set("n", "<leader>pb", function()
builtin.buffers({
attach_mappings = function(prompt_bufnr, map)
-- <Tab> multi-selects; this deletes the selection (or the entry under the cursor)
Expand All @@ -131,7 +130,7 @@ vim.keymap.set("n", "<leader>pd", function()
return true
end,
})
end, { desc = "Telescope delete buffers" })
end, { desc = "Telescope buffers" })
vim.keymap.set("n", "<leader>po", builtin.oldfiles, { desc = "Telescope oldfiles" })
vim.keymap.set("n", "<leader>pe", require("telescope").extensions.projects.projects, { desc = "Telescope projects" })
vim.keymap.set("n", "<leader>p.", function()
Expand Down
2 changes: 1 addition & 1 deletion roles/nvim/files/puppeteer-config.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"args": ["--no-sandbox"]
"args": []
}
54 changes: 54 additions & 0 deletions roles/nvim/files/tests/spec/diagram_backend_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
describe("Markdown diagram backends", function()
local environment = { "WEZTERM_PANE", "TERM", "KITTY_PID", "ZELLIJ" }
local original_environment = {}

local function set_environment(values)
for _, name in ipairs(environment) do
vim.env[name] = values[name]
end
end

before_each(function()
for _, name in ipairs(environment) do
original_environment[name] = vim.env[name]
end
set_environment({})
end)

after_each(function()
set_environment(original_environment)
end)

it("uses Sixel for WezTerm, including inside Zellij", function()
set_environment({ WEZTERM_PANE = "123", ZELLIJ = "0" })

local plugins = require("plugins.spec.diagram")
assert.is_true(plugins[1].cond())
assert.is_true(plugins[2].cond())
assert.equals("sixel", plugins[1].opts().backend)
end)

it("uses Kitty only outside Zellij", function()
set_environment({ TERM = "xterm-kitty" })

local plugins = require("plugins.spec.diagram")
assert.is_true(plugins[1].cond())
assert.equals("kitty", plugins[1].opts().backend)

set_environment({ TERM = "xterm-kitty", ZELLIJ = "0" })
assert.is_false(plugins[1].cond())
assert.is_false(plugins[2].cond())
end)

it("rerenders a focused Markdown split after its image was cleared", function()
local integration_module = "diagram.integrations.markdown"
local original_integration = package.loaded[integration_module]
package.loaded[integration_module] = {}

local plugins = require("plugins.spec.diagram")
local options = plugins[2].opts()

package.loaded[integration_module] = original_integration
assert.same({ "InsertLeave", "BufWinEnter", "TextChanged", "WinEnter" }, options.events.render_buffer)
end)
end)
Loading
Loading