feat(ansible): install tmux/mc/git/nvim with laptop dotfiles

This commit is contained in:
u1
2026-02-06 23:23:44 +01:00
parent 415af7efcd
commit 758f996e6b
12 changed files with 825 additions and 1 deletions

View File

@@ -0,0 +1,154 @@
-- hazard3_dap.lua — Hazard3 helpers for nvim-dap (GDB DAP)
local M = {}
local disasm_buf ---@type integer|nil
local function ensure_disasm_buf()
if disasm_buf and vim.api.nvim_buf_is_valid(disasm_buf) then
return disasm_buf
end
disasm_buf = vim.api.nvim_create_buf(false, true)
pcall(vim.api.nvim_buf_set_name, disasm_buf, "[DAP Disassembly]")
vim.bo[disasm_buf].buftype = "nofile"
vim.bo[disasm_buf].bufhidden = "hide"
vim.bo[disasm_buf].swapfile = false
vim.bo[disasm_buf].filetype = "asm"
vim.bo[disasm_buf].modifiable = true
vim.api.nvim_buf_set_lines(disasm_buf, 0, -1, false, { "(DAP) Disassembly" })
vim.bo[disasm_buf].modifiable = false
return disasm_buf
end
local function show_buf(buf)
local wins = vim.fn.win_findbuf(buf)
if wins and #wins > 0 then
vim.api.nvim_set_current_win(wins[1])
vim.wo.cursorline = true
return
end
vim.cmd("vsplit")
vim.api.nvim_set_current_buf(buf)
vim.wo.cursorline = true
end
local function update_disassembly(buf, opts)
opts = opts or {}
local count = opts.count or 80
local before = opts.before or 20
local ok, dap = pcall(require, "dap")
if not ok then
return
end
local session = dap.session()
if not session then
return
end
session:request("stackTrace", { threadId = 1, startFrame = 0, levels = 1 }, function(err, resp)
if err then
vim.notify("DAP disasm: stackTrace failed: " .. tostring(err), vim.log.levels.ERROR)
return
end
local frames = resp and resp.stackFrames or {}
if #frames == 0 then
vim.notify("DAP disasm: no stack frames", vim.log.levels.WARN)
return
end
local frame = frames[1]
local memref = frame.instructionPointerReference
if memref == nil or memref == "" then
vim.notify("DAP disasm: no instructionPointerReference", vim.log.levels.WARN)
return
end
session:request("disassemble", {
memoryReference = memref,
instructionOffset = -before,
instructionCount = count,
resolveSymbols = true,
}, function(err2, resp2)
if err2 then
vim.notify("DAP disasm: disassemble failed: " .. tostring(err2), vim.log.levels.ERROR)
return
end
local insts = resp2 and resp2.instructions or {}
local lines = {}
lines[#lines + 1] = "memref: " .. memref
for _, i in ipairs(insts) do
local addr = i.address or "?"
local ins = i.instruction or ""
local mark = (addr == memref) and "=> " or " "
lines[#lines + 1] = mark .. addr .. " " .. ins
end
if not vim.api.nvim_buf_is_valid(buf) then
return
end
vim.bo[buf].modifiable = true
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].modifiable = false
local pc_line
for idx, line in ipairs(lines) do
if line:sub(1, 3) == "=> " then
pc_line = idx
break
end
end
if pc_line then
local wins = vim.fn.win_findbuf(buf)
if wins and #wins > 0 then
for _, win in ipairs(wins) do
pcall(vim.api.nvim_win_set_cursor, win, { pc_line, 0 })
vim.wo[win].cursorline = true
end
end
end
end)
end)
end
function M.open_disassembly(opts)
local buf = ensure_disasm_buf()
local cur_win = vim.api.nvim_get_current_win()
show_buf(buf)
update_disassembly(buf, opts)
if opts and opts.keep_focus then
pcall(vim.api.nvim_set_current_win, cur_win)
end
end
function M.setup()
local ok, dap = pcall(require, "dap")
if not ok then
return
end
if vim.api.nvim_create_user_command then
pcall(vim.api.nvim_create_user_command, "DapDisassembly", function()
M.open_disassembly()
end, {})
end
dap.listeners.after.event_stopped["hazard3_disasm"] = function()
if disasm_buf and vim.api.nvim_buf_is_valid(disasm_buf) then
update_disassembly(disasm_buf, { keep_focus = true })
end
end
end
return M

View File

@@ -0,0 +1,107 @@
-- ======================================================================
-- keymaps.lua — Key mappings
-- ======================================================================
local map = vim.keymap.set
-- NERDTree
map("n", "<leader>n", ":NERDTreeFocus<CR>", { silent = true, desc = "NERDTree: Focus" })
map("n", "<C-n>", ":NERDTree<CR>", { silent = true, desc = "NERDTree: Open" })
map("n", "<C-t>", ":NERDTreeToggle<CR>", { silent = true, desc = "NERDTree: Toggle" })
map("n", "<C-f>", ":NERDTreeFind<CR>", { silent = true, desc = "NERDTree: Find current file" })
-- OSC52 (vim-oscyank)
map("n", "<leader>c", "<Plug>OSCYankOperator", { desc = "OSC Yank: Operator" })
map("n", "<leader>cc", "<leader>c_", { desc = "OSC Yank: Current line" })
map("v", "<leader>c", "<Plug>OSCYankVisual", { desc = "OSC Yank: Visual selection" })
-- vim-slime
vim.g.slime_no_mappings = 1
map("x", "<C-c><C-c>", "<Plug>SlimeRegionSend", { desc = "Slime: Send region" })
map("n", "<C-c><C-c>", function()
require("utils").send_cell("^#%%")
end, { silent = true, desc = "Slime: Send cell" })
map("n", "<C-c>v", "<Plug>SlimeConfig", { desc = "Slime: Configure" })
-- Claude Code
map("n", "<leader>ac", ":ClaudeCode<CR>", { silent = true, desc = "ClaudeCode: Toggle" })
map("n", "<leader>af", ":ClaudeCodeFocus<CR>", { silent = true, desc = "ClaudeCode: Focus" })
map("v", "<leader>as", ":ClaudeCodeSend<CR>", { silent = true, desc = "ClaudeCode: Send selection" })
map("n", "<leader>ad", ":ClaudeCodeAdd ", { desc = "ClaudeCode: Add file to context" })
map("n", "<leader>aa", ":ClaudeCodeDiffAccept<CR>", { silent = true, desc = "ClaudeCode: Accept diff" })
map("n", "<leader>ar", ":ClaudeCodeDiffDeny<CR>", { silent = true, desc = "ClaudeCode: Reject diff" })
-- DAP (GDB)
local function dap_attach_or(fn)
return function(...)
local dap = require("dap")
if dap.session() then
return fn(dap, ...)
end
if vim.fn.exists(":Hazard3Attach") == 2 then
vim.cmd("Hazard3Attach")
return
end
return fn(dap, ...)
end
end
map("n", "<F5>", dap_attach_or(function(dap)
dap.continue()
end), { desc = "DAP: Continue (or Hazard3Attach)" })
map("n", "<F10>", dap_attach_or(function(dap)
dap.step_over()
end), { desc = "DAP: Step over" })
map("n", "<F11>", dap_attach_or(function(dap)
dap.step_into()
end), { desc = "DAP: Step into" })
map("n", "<F12>", dap_attach_or(function(dap)
dap.step_out()
end), { desc = "DAP: Step out" })
map("n", "<leader>db", function()
require("dap").toggle_breakpoint()
end, { desc = "DAP: Toggle breakpoint" })
map("n", "<leader>dB", function()
require("dap").set_breakpoint(vim.fn.input("Condition: "))
end, { desc = "DAP: Conditional breakpoint" })
map("n", "<leader>dr", function()
require("dap").repl.open()
end, { desc = "DAP: REPL" })
map("n", "<leader>du", function()
require("dapui").toggle()
end, { desc = "DAP: Toggle UI" })
map("n", "<leader>dR", function()
local dapui = require("dapui")
dapui.close()
dapui.open({ reset = true })
end, { desc = "DAP UI: Reset layout" })
map("n", "<leader>da", function()
require("hazard3_dap").open_disassembly()
end, { desc = "DAP: Disassembly" })
map("n", "<leader>di", dap_attach_or(function(dap)
dap.step_into({ granularity = "instruction" })
end), { desc = "DAP: Step instruction into" })
map("n", "<leader>dI", dap_attach_or(function(dap)
dap.step_over({ granularity = "instruction" })
end), { desc = "DAP: Step instruction over" })
map("n", "<leader>dx", function()
require("dap").terminate()
end, { desc = "DAP: Terminate" })
map("n", "<leader>dp", function()
require("dap").pause()
end, { desc = "DAP: Pause" })

View File

@@ -0,0 +1,51 @@
-- ======================================================================
-- options.lua — Vim options and settings
-- ======================================================================
local opt = vim.opt
-- Clipboard
opt.clipboard = "unnamedplus"
-- Editing behavior
opt.backspace = { "indent", "eol", "start" }
opt.mouse = "r"
opt.compatible = false
-- UI
opt.number = true
opt.cursorline = true
opt.showcmd = true
opt.showmode = true
opt.showmatch = true
opt.scrolloff = 10
opt.wrap = false
-- Indentation
opt.shiftwidth = 4
opt.tabstop = 4
opt.expandtab = true
-- Search
opt.incsearch = true
opt.ignorecase = true
opt.smartcase = true
opt.hlsearch = true
-- Files
opt.backup = false
opt.history = 1000
opt.tags = { "./tags;,tags;" }
-- Completion
opt.wildmenu = true
opt.wildmode = { "list", "longest" }
opt.wildignore = {
"*.docx","*.jpg","*.png","*.gif","*.pdf","*.pyc","*.exe","*.flv","*.img","*.xlsx"
}
-- Filetype detection
vim.cmd("filetype on")
vim.cmd("filetype plugin on")
vim.cmd("filetype indent on")
vim.cmd("syntax on")

View File

@@ -0,0 +1,191 @@
-- ======================================================================
-- plugins.lua — Plugin specifications for lazy.nvim
-- ======================================================================
return {
-- File explorer
{
"preservim/nerdtree",
cmd = { "NERDTree", "NERDTreeToggle", "NERDTreeFind", "NERDTreeFocus" },
},
-- Git integration
{
"tpope/vim-fugitive",
event = "VeryLazy",
},
-- Commenting
{
"tpope/vim-commentary",
event = "VeryLazy",
},
-- LSP and completion
{
"neoclide/coc.nvim",
branch = "release",
build = "npm ci",
event = "VeryLazy",
},
-- REPL integration (tmux)
{
"jpalardy/vim-slime",
init = function()
vim.g.slime_target = "tmux"
if vim.fn.exists("g:slime_python_ipython") == 0 then
vim.g.slime_python_ipython = 1
end
end,
},
-- OSC52 clipboard (remote SSH/tmux)
{
"ojroques/vim-oscyank",
event = "VimEnter",
init = function()
vim.g.oscyank_term = "tmux"
vim.g.oscyank_silent = true
end,
config = function()
local grp = vim.api.nvim_create_augroup("osc52_yank", { clear = true })
vim.api.nvim_create_autocmd("TextYankPost", {
group = grp,
callback = function()
if vim.v.event.operator == "y" and vim.v.event.regname == "" then
vim.cmd([[OSCYankRegister "]])
end
end,
})
end,
},
-- Debugging (DAP)
{
"mfussenegger/nvim-dap",
config = function()
local dap = require("dap")
dap.adapters.gdb = {
type = "executable",
command = "gdb-multiarch",
args = { "-i", "dap" },
}
local function pick_elf()
return vim.fn.input("ELF: ", vim.fn.getcwd() .. "/", "file")
end
local function normalize_target(t)
t = t or ""
if vim.trim then
t = vim.trim(t)
else
t = t:gsub("^%s+", ""):gsub("%s+$", "")
end
t = t:gsub("^target%s+extended%-remote%s+", "")
t = t:gsub("^target%s+remote%s+", "")
t = t:gsub("^extended%-remote%s+", "")
t = t:gsub("^remote%s+", "")
return t
end
local function pick_target()
local default = normalize_target(vim.env.HAZARD3_GDB_TARGET)
if default == "" then
default = "localhost:3333"
end
return normalize_target(vim.fn.input("GDB target (host:port): ", default))
end
local hazard3_attach = {
name = "Hazard3 (attach gdb-remote)",
type = "gdb",
request = "attach",
program = pick_elf,
target = pick_target,
cwd = "${workspaceFolder}",
}
local function add_cfg(ft, cfg)
dap.configurations[ft] = dap.configurations[ft] or {}
for _, existing in ipairs(dap.configurations[ft]) do
if existing.name == cfg.name then
return
end
end
table.insert(dap.configurations[ft], cfg)
end
for _, ft in ipairs({ "c", "cpp", "asm", "" }) do
add_cfg(ft, hazard3_attach)
end
pcall(function()
vim.api.nvim_create_user_command("Hazard3Attach", function()
dap.run(hazard3_attach)
end, { desc = "DAP: Hazard3 attach" })
end)
vim.fn.sign_define("DapBreakpoint", { text = "", texthl = "DiagnosticError" })
vim.fn.sign_define("DapStopped", { text = "", texthl = "DiagnosticInfo", linehl = "Visual" })
pcall(function()
require("hazard3_dap").setup()
end)
end,
},
{
"rcarriga/nvim-dap-ui",
dependencies = {
"mfussenegger/nvim-dap",
"nvim-neotest/nvim-nio",
},
config = function()
local dap = require("dap")
local dapui = require("dapui")
dapui.setup({
-- ASCII/unicode-friendly icons (no codicons required)
icons = { expanded = "v", collapsed = ">", current_frame = ">" },
controls = {
enabled = true,
element = "repl",
icons = {
pause = "||",
play = ">",
step_into = "",
step_over = "",
step_out = "",
step_back = "",
run_last = "",
terminate = "X",
disconnect = "D",
},
},
})
dap.listeners.after.event_initialized["dapui"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui"] = function()
dapui.close()
end
end,
},
{
"theHamsta/nvim-dap-virtual-text",
dependencies = {
"mfussenegger/nvim-dap",
},
opts = {},
},
}

View File

@@ -0,0 +1,25 @@
-- ======================================================================
-- utils.lua — Helper functions
-- ======================================================================
local M = {}
-- Show filename aligned to the right
function M.show_filename_right()
local filename = vim.fn.expand("%:p")
local columns = vim.o.columns
local space = columns - #filename - 2
if space < 0 then space = 0 end
vim.api.nvim_echo({{string.rep(" ", space) .. filename, "None"}}, false, {})
end
-- Send cell to slime (delimited by pattern, e.g., "^#%%")
function M.send_cell(pattern)
local start_line = vim.fn.search(pattern, "bnW")
if start_line ~= 0 then start_line = start_line + 1 else start_line = 1 end
local stop_line = vim.fn.search(pattern, "nW")
if stop_line ~= 0 then stop_line = stop_line - 1 else stop_line = vim.fn.line("$") end
vim.fn["slime#send_range"](start_line, stop_line)
end
return M