From 0a281046b51857eae22912b5f9b0bca42ba579d0 Mon Sep 17 00:00:00 2001 From: Roel de Cort Date: Tue, 28 Jul 2026 00:11:30 +0200 Subject: [PATCH] feat(nixvim): add VCS and language tooling Signed-off-by: Roel de Cort --- README.md | 9 +- config/default.nix | 7 + config/file_types.nix | 33 ++++- config/plugins/editor/treesitter.nix | 3 + config/plugins/git/jj.nix | 69 ++++++++++ config/plugins/lsp/conform.nix | 10 -- config/plugins/lsp/go.nix | 187 +++++++++++++++++++++++++ config/plugins/lsp/hcl.nix | 29 ++++ config/plugins/lsp/lsp.nix | 52 ------- config/plugins/lsp/nix.nix | 19 +++ config/plugins/lsp/ocaml.nix | 38 ++++++ config/plugins/lsp/rust.nix | 42 ++++++ config/plugins/lsp/yaml.nix | 61 +++++++++ config/plugins/ui/lualine.nix | 13 +- config/plugins/ui/vcs_status.lua | 197 +++++++++++++++++++++++++++ flake.nix | 2 +- 16 files changed, 702 insertions(+), 69 deletions(-) create mode 100644 config/plugins/git/jj.nix create mode 100644 config/plugins/lsp/go.nix create mode 100644 config/plugins/lsp/hcl.nix create mode 100644 config/plugins/lsp/nix.nix create mode 100644 config/plugins/lsp/ocaml.nix create mode 100644 config/plugins/lsp/rust.nix create mode 100644 config/plugins/lsp/yaml.nix create mode 100644 config/plugins/ui/vcs_status.lua diff --git a/README.md b/README.md index 8c933ecf..463171fc 100644 --- a/README.md +++ b/README.md @@ -71,17 +71,24 @@ home-manager.users..home.packages = [ ### UI Plugins - `bufferline.nix`: Configures the Bufferline plugin for enhanced buffer/tab display. -- `lualine.nix`: Configures the Lualine status line plugin. +- `lualine.nix`: Configures Lualine with Git, Jujutsu, and configured-forge context. - `startup.nix`: Configures the startup screen. ### LSP - `lsp.nix`: Configures the Neovim LSP client. +- `go.nix`: Configures Go language support, formatting, tests, and debugging. +- `ocaml.nix`: Configures project-aware OCaml-LSP and OCamlFormat fallback support. +- `nix.nix`: Configures nixd and nixfmt. +- `hcl.nix`: Configures OpenTofu LSP/formatting while keeping generic HCL schema-neutral. +- `yaml.nix`: Configures SchemaStore-backed YAML and Helm language support. +- `rust.nix`: Configures Rustaceanvim, Neotest integration, and Rust-specific actions. - `conform.nix`: Configures the Conform plugin for automatic code formatting. - `fidget.nix`: Configures the Fidget plugin for displaying LSP diagnostics in the status line. ### Git +- `jj.nix`: Configures Jujutsu commands and key mappings. - `lazygit.nix`: Configures the LazyGit plugin for Git integration. - `gitsigns.nix`: Configures the GitSigns plugin for displaying Git diff information. diff --git a/config/default.nix b/config/default.nix index 4134c137..6dcd2b80 100644 --- a/config/default.nix +++ b/config/default.nix @@ -34,10 +34,17 @@ _: { # LSP and formatting ./plugins/lsp/lsp.nix + ./plugins/lsp/go.nix + ./plugins/lsp/ocaml.nix + ./plugins/lsp/nix.nix + ./plugins/lsp/hcl.nix + ./plugins/lsp/yaml.nix + ./plugins/lsp/rust.nix ./plugins/lsp/conform.nix ./plugins/lsp/fidget.nix # Git + ./plugins/git/jj.nix ./plugins/git/lazygit.nix ./plugins/git/gitsigns.nix diff --git a/config/file_types.nix b/config/file_types.nix index 2cf361fe..156f00be 100644 --- a/config/file_types.nix +++ b/config/file_types.nix @@ -12,10 +12,35 @@ ]; pattern = [ "*.tf" - "*.tfvars" - "*.hcl" ]; - command = "set ft=terraform"; + command = "setlocal filetype=terraform"; + } + { + group = "filetypes"; + event = [ + "BufRead" + "BufNewFile" + ]; + pattern = [ "*.tfvars" ]; + command = "setlocal filetype=opentofu-vars"; + } + { + group = "filetypes"; + event = [ + "BufRead" + "BufNewFile" + ]; + pattern = [ "*.tofu" ]; + command = "setlocal filetype=opentofu"; + } + { + group = "filetypes"; + event = [ + "BufRead" + "BufNewFile" + ]; + pattern = [ "*.hcl" ]; + command = "setlocal filetype=hcl"; } ]; @@ -30,7 +55,7 @@ "*.bicep" "*.bicepparam" ]; - command = "set ft=bicep"; + command = "setlocal filetype=bicep"; } ]; } diff --git a/config/plugins/editor/treesitter.nix b/config/plugins/editor/treesitter.nix index c759121d..8e3e7560 100644 --- a/config/plugins/editor/treesitter.nix +++ b/config/plugins/editor/treesitter.nix @@ -34,8 +34,11 @@ markdown markdown_inline nix + ocaml + ocaml_interface python regex + rust sql terraform toml diff --git a/config/plugins/git/jj.nix b/config/plugins/git/jj.nix new file mode 100644 index 00000000..3323a976 --- /dev/null +++ b/config/plugins/git/jj.nix @@ -0,0 +1,69 @@ +{ + plugins.jj = { + enable = true; + settings = { + cmd.describe.editor = { + type = "buffer"; + }; + }; + }; + + keymaps = [ + { + mode = "n"; + key = "jl"; + action = "J log"; + options = { + desc = "Jujutsu log"; + }; + } + { + mode = "n"; + key = "js"; + action = "J status"; + options = { + desc = "Jujutsu status"; + }; + } + { + mode = "n"; + key = "jd"; + action = "J describe"; + options = { + desc = "Jujutsu describe"; + }; + } + { + mode = "n"; + key = "jn"; + action = "J new"; + options = { + desc = "Jujutsu new change"; + }; + } + { + mode = "n"; + key = "ju"; + action = "J undo"; + options = { + desc = "Jujutsu undo"; + }; + } + { + mode = "n"; + key = "jr"; + action = "J redo"; + options = { + desc = "Jujutsu redo"; + }; + } + { + mode = "n"; + key = "jf"; + action = "J fetch"; + options = { + desc = "Jujutsu fetch"; + }; + } + ]; +} diff --git a/config/plugins/lsp/conform.nix b/config/plugins/lsp/conform.nix index c6ab530f..4a3903dc 100644 --- a/config/plugins/lsp/conform.nix +++ b/config/plugins/lsp/conform.nix @@ -103,18 +103,11 @@ "isort" ]; lua = [ "stylua" ]; - nix = [ "nixfmt" ]; markdown = { __unkeyed-1 = "prettierd"; __unkeyed-2 = "prettier"; stop_after_first = true; }; - yaml = { - __unkeyed-1 = "prettierd"; - __unkeyed-2 = "prettier"; - stop_after_first = true; - }; - terraform = [ "terraform_fmt" ]; bicep = [ "bicep" ]; bash = [ "shellcheck" @@ -132,9 +125,6 @@ isort = { command = "${lib.getExe pkgs.isort}"; }; - nixfmt = { - command = "${lib.getExe pkgs.nixfmt}"; - }; alejandra = { command = "${lib.getExe pkgs.alejandra}"; }; diff --git a/config/plugins/lsp/go.nix b/config/plugins/lsp/go.nix new file mode 100644 index 00000000..f8c0410c --- /dev/null +++ b/config/plugins/lsp/go.nix @@ -0,0 +1,187 @@ +{ lib, pkgs, ... }: +{ + plugins = { + lsp.servers.gopls = { + enable = true; + settings.gopls = { + gofumpt = true; + usePlaceholders = true; + analyses = { + nilness = true; + unusedparams = true; + unusedwrite = true; + useany = true; + }; + hints = { + assignVariableTypes = false; + compositeLiteralFields = true; + compositeLiteralTypes = true; + constantValues = true; + functionTypeParameters = true; + parameterNames = true; + rangeVariableTypes = true; + }; + }; + }; + + conform-nvim.settings = { + formatters_by_ft.go = [ + "goimports" + "gofumpt" + ]; + formatters = { + goimports.command = lib.getExe' pkgs.gotools "goimports"; + gofumpt.command = lib.getExe pkgs.gofumpt; + }; + }; + + dap = { + enable = true; + signs = { + dapBreakpoint.text = ""; + dapBreakpointCondition.text = ""; + dapBreakpointRejected.text = ""; + dapLogPoint.text = ""; + dapStopped.text = ""; + }; + }; + dap-go = { + enable = true; + settings.delve.path = lib.getExe' pkgs.delve "dlv"; + }; + dap-ui = { + enable = true; + settings.floating.border = "rounded"; + }; + dap-virtual-text = { + enable = true; + settings = { + commented = true; + virt_text_pos = "eol"; + }; + }; + + neotest = { + enable = true; + adapters.golang = { + enable = true; + settings = { + dap_go_enabled = true; + testify_enabled = true; + warn_test_name_dupes = true; + warn_test_not_executed = true; + args = [ "-count=1" ]; + }; + }; + settings = { + floating.border = "rounded"; + output.open_on_run = false; + }; + }; + }; + + keymaps = [ + { + mode = "n"; + key = "rn"; + action = lib.nixvim.mkRaw "function() require('neotest').run.run() end"; + options.desc = "Run nearest test"; + } + { + mode = "n"; + key = "rf"; + action = lib.nixvim.mkRaw "function() require('neotest').run.run(vim.fn.expand('%')) end"; + options.desc = "Run test file"; + } + { + mode = "n"; + key = "rl"; + action = lib.nixvim.mkRaw "function() require('neotest').run.run_last() end"; + options.desc = "Run last test"; + } + { + mode = "n"; + key = "rd"; + action = lib.nixvim.mkRaw "function() require('neotest').run.run({ strategy = 'dap' }) end"; + options.desc = "Debug nearest test"; + } + { + mode = "n"; + key = "rs"; + action = lib.nixvim.mkRaw "function() require('neotest').summary.toggle() end"; + options.desc = "Toggle test summary"; + } + { + mode = "n"; + key = "ro"; + action = lib.nixvim.mkRaw "function() require('neotest').output.open({ enter = true }) end"; + options.desc = "Show test output"; + } + { + mode = "n"; + key = ""; + action = lib.nixvim.mkRaw "function() require('dap').continue() end"; + options.desc = "Debug continue"; + } + { + mode = "n"; + key = ""; + action = lib.nixvim.mkRaw "function() require('dap').step_over() end"; + options.desc = "Debug step over"; + } + { + mode = "n"; + key = ""; + action = lib.nixvim.mkRaw "function() require('dap').step_into() end"; + options.desc = "Debug step into"; + } + { + mode = "n"; + key = ""; + action = lib.nixvim.mkRaw "function() require('dap').step_out() end"; + options.desc = "Debug step out"; + } + { + mode = "n"; + key = "db"; + action = lib.nixvim.mkRaw "function() require('dap').toggle_breakpoint() end"; + options.desc = "Toggle breakpoint"; + } + { + mode = "n"; + key = "dB"; + action = lib.nixvim.mkRaw "function() require('dap').set_breakpoint(vim.fn.input('Breakpoint condition: ')) end"; + options.desc = "Conditional breakpoint"; + } + { + mode = "n"; + key = "du"; + action = lib.nixvim.mkRaw "function() require('dapui').toggle() end"; + options.desc = "Toggle debug UI"; + } + { + mode = "n"; + key = "dt"; + action = lib.nixvim.mkRaw "function() require('dap').terminate() end"; + options.desc = "Terminate debugger"; + } + ]; + + extraConfigLua = '' + local dap = require("dap") + local dapui = require("dapui") + + dap.listeners.before.attach.dapui_config = function() + dapui.open() + end + dap.listeners.before.launch.dapui_config = function() + dapui.open() + end + dap.listeners.before.event_terminated.dapui_config = function() + dapui.close() + end + dap.listeners.before.event_exited.dapui_config = function() + dapui.close() + end + ''; +} diff --git a/config/plugins/lsp/hcl.nix b/config/plugins/lsp/hcl.nix new file mode 100644 index 00000000..bdf68a4a --- /dev/null +++ b/config/plugins/lsp/hcl.nix @@ -0,0 +1,29 @@ +{ + pkgs, + ... +}: +{ + plugins = { + lsp.servers.tofu_ls = { + enable = true; + packageFallback = true; + }; + + conform-nvim.settings = { + formatters_by_ft = { + terraform = [ "tofu_fmt" ]; + opentofu = [ "tofu_fmt" ]; + "opentofu-vars" = [ "tofu_fmt" ]; + }; + formatters.tofu_fmt.command = "tofu"; + }; + }; + + # tofu-ls and tofu_fmt use the project-managed binary when available. + extraPackagesAfter = [ pkgs.opentofu ]; + + extraConfigLuaPre = '' + vim.treesitter.language.register("terraform", "opentofu") + vim.treesitter.language.register("terraform", "opentofu-vars") + ''; +} diff --git a/config/plugins/lsp/lsp.nix b/config/plugins/lsp/lsp.nix index d7f5ff99..06ca6431 100644 --- a/config/plugins/lsp/lsp.nix +++ b/config/plugins/lsp/lsp.nix @@ -1,12 +1,8 @@ -{ pkgs, ... }: { plugins = { lsp-lines = { enable = true; }; - helm = { - enable = true; - }; lsp = { enable = true; inlayHints = true; @@ -17,9 +13,6 @@ lua_ls = { enable = true; }; - nil_ls = { - enable = true; - }; ts_ls = { enable = true; }; @@ -29,50 +22,9 @@ pyright = { enable = true; }; - gopls = { - enable = true; - }; - terraformls = { - enable = true; - }; jsonls = { enable = true; }; - helm_ls = { - enable = true; - extraOptions = { - settings = { - "helm_ls" = { - yamlls = { - path = "${pkgs.yaml-language-server}/bin/yaml-language-server"; - }; - }; - }; - }; - }; - yamlls = { - enable = true; - extraOptions = { - settings = { - yaml = { - schemas = { - kubernetes = "*.yaml"; - "https://json.schemastore.org/github-workflow" = ".github/workflows/*"; - "https://json.schemastore.org/github-action" = ".github/action.{yml,yaml}"; - "https://json.schemastore.org/ansible-stable-2.9" = "roles/tasks/*.{yml,yaml}"; - "https://json.schemastore.org/kustomization" = "kustomization.{yml,yaml}"; - "https://json.schemastore.org/ansible-playbook" = "*play*.{yml,yaml}"; - "https://json.schemastore.org/chart" = "Chart.{yml,yaml}"; - "https://json.schemastore.org/dependabot-v2" = ".github/dependabot.{yml,yaml}"; - "https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json" = - "*docker-compose*.{yml,yaml}"; - "https://raw.githubusercontent.com/argoproj/argo-workflows/master/api/jsonschema/schema.json" = - "*flow*.{yml,yaml}"; - }; - }; - }; - }; - }; }; keymaps = { @@ -114,10 +66,6 @@ }; }; }; - extraPlugins = with pkgs.vimPlugins; [ - ansible-vim - ]; - extraConfigLua = '' local _border = "rounded" diff --git a/config/plugins/lsp/nix.nix b/config/plugins/lsp/nix.nix new file mode 100644 index 00000000..2d7fae61 --- /dev/null +++ b/config/plugins/lsp/nix.nix @@ -0,0 +1,19 @@ +{ + lib, + pkgs, + ... +}: +{ + plugins = { + lsp.servers.nixd = { + enable = true; + packageFallback = true; + settings.formatting.command = [ (lib.getExe pkgs.nixfmt) ]; + }; + + conform-nvim.settings = { + formatters_by_ft.nix = [ "nixfmt" ]; + formatters.nixfmt.command = lib.getExe pkgs.nixfmt; + }; + }; +} diff --git a/config/plugins/lsp/ocaml.nix b/config/plugins/lsp/ocaml.nix new file mode 100644 index 00000000..645c48e9 --- /dev/null +++ b/config/plugins/lsp/ocaml.nix @@ -0,0 +1,38 @@ +{ + lib, + pkgs, + ... +}: +{ + plugins.lsp.servers.ocamllsp = { + enable = true; + # Prefer the server from the active opam switch or Dune tools environment. + packageFallback = true; + }; + + # OCaml-LSP delegates formatting to ocamlformat. Keep the Nix package as a + # fallback so a project-local/opam version remains authoritative. + extraPackagesAfter = [ pkgs.ocamlPackages.ocamlformat ]; + + autoCmd = [ + { + event = "FileType"; + pattern = [ + "ocaml" + "ocamlinterface" + ]; + desc = "OCaml implementation/interface keymap"; + callback = lib.nixvim.mkRaw '' + function(args) + vim.keymap.set("n", "oi", function() + vim.cmd.LspOcamllspSwitchImplIntf() + end, { + buffer = args.buf, + desc = "OCaml switch implementation/interface", + silent = true, + }) + end + ''; + } + ]; +} diff --git a/config/plugins/lsp/rust.nix b/config/plugins/lsp/rust.nix new file mode 100644 index 00000000..643a316b --- /dev/null +++ b/config/plugins/lsp/rust.nix @@ -0,0 +1,42 @@ +{ lib, ... }: +{ + dependencies.rust-analyzer.packageFallback = true; + + plugins = { + rustaceanvim = { + enable = true; + settings = { + server.load_vscode_settings = true; + tools.enable_clippy = true; + }; + }; + + # Rustaceanvim's adapter asks rust-analyzer for the actual test targets and + # reuses its debugger detection. The shared r mappings then work for + # both Go and Rust. + neotest.settings.adapters = [ "require('rustaceanvim.neotest')" ]; + }; + + autoCmd = [ + { + event = "FileType"; + pattern = "rust"; + desc = "Rustaceanvim keymaps"; + callback = lib.nixvim.mkRaw '' + function(args) + local function rust_lsp(command) + return function() + vim.cmd.RustLsp(command) + end + end + + local opts = { buffer = args.buf, silent = true } + vim.keymap.set("n", "Ra", rust_lsp("codeAction"), vim.tbl_extend("force", opts, { desc = "Rust code action" })) + vim.keymap.set("n", "Rr", rust_lsp("runnables"), vim.tbl_extend("force", opts, { desc = "Rust runnables" })) + vim.keymap.set("n", "Rt", rust_lsp("testables"), vim.tbl_extend("force", opts, { desc = "Rust testables" })) + vim.keymap.set("n", "Rd", rust_lsp("debuggables"), vim.tbl_extend("force", opts, { desc = "Rust debuggables" })) + end + ''; + } + ]; +} diff --git a/config/plugins/lsp/yaml.nix b/config/plugins/lsp/yaml.nix new file mode 100644 index 00000000..9adaf7f3 --- /dev/null +++ b/config/plugins/lsp/yaml.nix @@ -0,0 +1,61 @@ +{ + lib, + pkgs, + ... +}: +{ + plugins = { + lsp.servers = { + yamlls = { + enable = true; + settings = { + validate = true; + completion = true; + hover = true; + format.enable = false; + }; + }; + + helm_ls = { + enable = true; + extraOptions.settings."helm-ls" = { + yamlls = { + path = lib.getExe pkgs.yaml-language-server; + config = { + schemas.kubernetes = "templates/**"; + completion = true; + hover = true; + }; + }; + }; + }; + }; + + conform-nvim.settings.formatters_by_ft = { + yaml = { + __unkeyed-1 = "prettierd"; + __unkeyed-2 = "prettier"; + stop_after_first = true; + }; + "yaml.helm-values" = { + __unkeyed-1 = "prettierd"; + __unkeyed-2 = "prettier"; + stop_after_first = true; + }; + }; + }; + + extraPlugins = with pkgs.vimPlugins; [ + ansible-vim + helm-ls-nvim + ]; + + # helm-ls uses Helm for linting. A dev-shell version takes precedence. + extraPackagesAfter = [ pkgs.kubernetes-helm ]; + + extraConfigLua = '' + require("helm-ls").setup({ + conceal_templates = { enabled = false }, + }) + ''; +} diff --git a/config/plugins/ui/lualine.nix b/config/plugins/ui/lualine.nix index c82239d5..bc54b2a0 100644 --- a/config/plugins/ui/lualine.nix +++ b/config/plugins/ui/lualine.nix @@ -1,4 +1,7 @@ -_: { +{ lib, ... }: +{ + extraFiles."lua/vcs_status.lua".source = ./vcs_status.lua; + plugins.lualine = { enable = true; settings = { @@ -28,6 +31,10 @@ _: { __unkeyed-1 = "branch"; icon = ""; } + { + __unkeyed-1 = lib.nixvim.mkRaw "require('vcs_status').jj"; + icon = "󱗆"; + } { __unkeyed-1 = "diff"; symbols = { @@ -53,6 +60,10 @@ _: { } ]; lualine_x = [ + { + __unkeyed-1 = lib.nixvim.mkRaw "require('vcs_status').forges"; + icon = "󰛳"; + } { __unkeyed-1 = "filetype"; icon_only = true; diff --git a/config/plugins/ui/vcs_status.lua b/config/plugins/ui/vcs_status.lua new file mode 100644 index 00000000..d217c85d --- /dev/null +++ b/config/plugins/ui/vcs_status.lua @@ -0,0 +1,197 @@ +local M = {} + +local cache = {} +local buffer_roots = {} +local cache_ttl_ms = 5000 + +local function now_ms() + return vim.uv.hrtime() / 1000000 +end + +local function buffer_root() + local bufnr = vim.api.nvim_get_current_buf() + if buffer_roots[bufnr] ~= nil then + return buffer_roots[bufnr] or nil + end + + local name = vim.api.nvim_buf_get_name(bufnr) + local start = name ~= "" and vim.fs.dirname(name) or vim.uv.cwd() + local root = start and vim.fs.root(start, { ".jj", ".git" }) or nil + buffer_roots[bufnr] = root or false + return root +end + +local function refresh_lualine() + vim.schedule(function() + local ok, lualine = pcall(require, "lualine") + if ok then + lualine.refresh({ place = { "statusline" } }) + end + end) +end + +local function finish(entry) + entry.pending = entry.pending - 1 + if entry.pending == 0 then + entry.updated_at = now_ms() + refresh_lualine() + end +end + +local function parse_jj(entry, result) + if result.code == 0 then + local line = vim.trim(result.stdout or "") + local fields = vim.split(line, "\t", { plain = true }) + entry.jj = { + change_id = fields[1] or "", + bookmarks = fields[2] or "", + description = fields[3] or "", + conflicted = fields[4] == "!", + } + else + entry.jj = nil + end + finish(entry) +end + +local forge_definitions = { + { label = "GH", patterns = { "github", "github.com" } }, + { label = "GL", patterns = { "gitlab", "gitlab.com" } }, + { label = "TG", patterns = { "tangled", "tangled.org" } }, + { label = "RAD", patterns = { "radicle", "rad://" } }, +} + +local function parse_forges(entry, result) + local found = {} + if result.code == 0 then + local remotes = string.lower(result.stdout or "") + for _, forge in ipairs(forge_definitions) do + for _, pattern in ipairs(forge.patterns) do + if remotes:find(pattern, 1, true) then + table.insert(found, forge.label) + break + end + end + end + end + entry.forges = table.concat(found, " ") + finish(entry) +end + +local function refresh(root, entry) + if entry.pending > 0 then + return + end + + local is_jj = vim.uv.fs_stat(root .. "/.jj") ~= nil + entry.pending = is_jj and 2 or 1 + + if is_jj then + vim.system({ + "jj", + "--ignore-working-copy", + "--repository", + root, + "log", + "--no-graph", + "--revision", + "@", + "--template", + 'change_id.shortest(8) ++ "\\t" ++ bookmarks.join(",") ++ "\\t" ++ description.first_line() ++ "\\t" ++ if(conflict, "!", "") ++ "\\n"', + }, { text = true }, function(result) + parse_jj(entry, result) + end) + + vim.system({ + "jj", + "--ignore-working-copy", + "--repository", + root, + "git", + "remote", + "list", + }, { text = true }, function(result) + parse_forges(entry, result) + end) + else + entry.jj = nil + vim.system({ "git", "-C", root, "remote", "--verbose" }, { text = true }, function(result) + parse_forges(entry, result) + end) + end +end + +local function current_entry() + local root = buffer_root() + if not root then + return nil + end + + local entry = cache[root] + if not entry then + entry = { + pending = 0, + updated_at = 0, + jj = nil, + forges = "", + } + cache[root] = entry + end + + if now_ms() - entry.updated_at >= cache_ttl_ms then + refresh(root, entry) + end + return entry +end + +function M.jj() + local entry = current_entry() + if not entry or not entry.jj then + return "" + end + + local parts = { entry.jj.change_id } + if entry.jj.bookmarks ~= "" then + table.insert(parts, entry.jj.bookmarks) + end + if entry.jj.conflicted then + table.insert(parts, "!") + end + + if vim.o.columns >= 140 and entry.jj.description ~= "" then + local description = entry.jj.description + if #description > 28 then + description = description:sub(1, 27) .. "…" + end + table.insert(parts, description) + end + + return table.concat(parts, " ") +end + +function M.forges() + local entry = current_entry() + return entry and entry.forges or "" +end + +local group = vim.api.nvim_create_augroup("vcs_statusline", { clear = true }) + +vim.api.nvim_create_autocmd({ "BufEnter", "BufFilePost" }, { + group = group, + callback = function(args) + buffer_roots[args.buf] = nil + end, +}) + +vim.api.nvim_create_autocmd({ "DirChanged", "FocusGained" }, { + group = group, + callback = function() + buffer_roots = {} + local root = buffer_root() + if root and cache[root] then + cache[root].updated_at = 0 + end + end, +}) + +return M diff --git a/flake.nix b/flake.nix index bc550fbb..c1940ef5 100644 --- a/flake.nix +++ b/flake.nix @@ -61,7 +61,7 @@ }; }; - formatter = pkgs.nixfmt; + formatter = pkgs.nixfmt-tree; packages = { default = nvim;