From 6e1547ae48c615f9894b6f6a30d3621491300bdc Mon Sep 17 00:00:00 2001 From: Christoph Schmatzler Date: Thu, 12 Mar 2026 19:21:42 +0000 Subject: [PATCH] more --- flake.lock | 62 ++++- flake.nix | 4 + modules/_neovim/autocmd.nix | 10 + modules/_neovim/default.nix | 11 +- modules/_neovim/plugins/conform.nix | 5 +- modules/_neovim/plugins/jj-nvim.nix | 1 - modules/_neovim/plugins/mini.nix | 192 +------------- modules/_neovim/plugins/opencode.nix | 272 +------------------- modules/_neovim/plugins/render-markdown.nix | 9 + modules/_neovim/plugins/treesitter.nix | 47 ---- modules/dendritic.nix | 4 + 11 files changed, 105 insertions(+), 512 deletions(-) create mode 100644 modules/_neovim/plugins/render-markdown.nix diff --git a/flake.lock b/flake.lock index d77d44e..f67d9af 100644 --- a/flake.lock +++ b/flake.lock @@ -323,6 +323,27 @@ } }, "flake-parts_3": { + "inputs": { + "nixpkgs-lib": [ + "neovim-nightly-overlay", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_4": { "inputs": { "nixpkgs-lib": [ "nixvim", @@ -584,6 +605,44 @@ "type": "github" } }, + "neovim-nightly-overlay": { + "inputs": { + "flake-parts": "flake-parts_3", + "neovim-src": "neovim-src", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773273897, + "narHash": "sha256-S6r4raWL96DNO8nPP2whq8STG8SI8JDlyEuy1N6SvV8=", + "owner": "nix-community", + "repo": "neovim-nightly-overlay", + "rev": "f165d4768ca760f4c98b63286562b6fa79a8b114", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "neovim-nightly-overlay", + "type": "github" + } + }, + "neovim-src": { + "flake": false, + "locked": { + "lastModified": 1773273300, + "narHash": "sha256-PzRI/GS7+4wz+dgqWq4X2biKtJc8WgUqS6jWk0fXmlM=", + "owner": "neovim", + "repo": "neovim", + "rev": "99a0b2f7b86d447af752ee7436dd5fd69fc6a101", + "type": "github" + }, + "original": { + "owner": "neovim", + "repo": "neovim", + "type": "github" + } + }, "nix-homebrew": { "inputs": { "brew-src": "brew-src" @@ -732,7 +791,7 @@ }, "nixvim": { "inputs": { - "flake-parts": "flake-parts_3", + "flake-parts": "flake-parts_4", "nixpkgs": "nixpkgs_6", "systems": "systems_4" }, @@ -800,6 +859,7 @@ "jj-starship": "jj-starship", "llm-agents": "llm-agents", "naersk": "naersk", + "neovim-nightly-overlay": "neovim-nightly-overlay", "nix-homebrew": "nix-homebrew", "nixpkgs": "nixpkgs_5", "nixpkgs-lib": [ diff --git a/flake.nix b/flake.nix index cc46a68..bd5851f 100644 --- a/flake.nix +++ b/flake.nix @@ -44,6 +44,10 @@ url = "github:nix-community/naersk/master"; inputs.nixpkgs.follows = "nixpkgs"; }; + neovim-nightly-overlay = { + url = "github:nix-community/neovim-nightly-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; nix-homebrew.url = "github:zhaofengli-wip/nix-homebrew"; nixpkgs.url = "github:nixos/nixpkgs/master"; nixpkgs-lib.follows = "nixpkgs"; diff --git a/modules/_neovim/autocmd.nix b/modules/_neovim/autocmd.nix index 96d5fe3..50f6058 100644 --- a/modules/_neovim/autocmd.nix +++ b/modules/_neovim/autocmd.nix @@ -29,6 +29,16 @@ pattern = "elixir,eelixir,heex"; command = "setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2"; } + { + event = "FileType"; + group = "Christoph"; + pattern = "opencode,opencode_output"; + callback.__raw = '' + function() + vim.b.ministatusline_disable = true + end + ''; + } ]; }; } diff --git a/modules/_neovim/default.nix b/modules/_neovim/default.nix index d7eaa18..a3dad34 100644 --- a/modules/_neovim/default.nix +++ b/modules/_neovim/default.nix @@ -1,21 +1,22 @@ -{ +{inputs', ...}: { imports = [ ./autocmd.nix ./mappings.nix ./options.nix ./plugins/blink-cmp.nix + ./plugins/code-review.nix ./plugins/conform.nix + ./plugins/diffview.nix ./plugins/grug-far.nix ./plugins/harpoon.nix ./plugins/hunk.nix ./plugins/jj-diffconflicts.nix ./plugins/jj-nvim.nix - ./plugins/code-review.nix - ./plugins/diffview.nix ./plugins/lsp.nix ./plugins/mini.nix ./plugins/oil.nix ./plugins/opencode.nix + ./plugins/render-markdown.nix ./plugins/toggleterm.nix ./plugins/treesitter.nix ./plugins/zk.nix @@ -24,6 +25,7 @@ programs.nixvim = { enable = true; defaultEditor = true; + package = inputs'.neovim-nightly-overlay.packages.default; luaLoader.enable = true; colorschemes.rose-pine = { enable = true; @@ -31,9 +33,6 @@ variant = "dawn"; }; }; - extraConfigLua = '' - vim.ui.select = MiniPick.ui_select - ''; }; home.shellAliases = { diff --git a/modules/_neovim/plugins/conform.nix b/modules/_neovim/plugins/conform.nix index 24c8ef7..84f5245 100644 --- a/modules/_neovim/plugins/conform.nix +++ b/modules/_neovim/plugins/conform.nix @@ -5,9 +5,8 @@ format_on_save = {}; formatters_by_ft = { nix = ["alejandra"]; - javascript = ["prettier"]; - typescript = ["prettier"]; - vue = ["prettier"]; + javascript = ["oxfmt"]; + typescript = ["oxfmt"]; }; }; }; diff --git a/modules/_neovim/plugins/jj-nvim.nix b/modules/_neovim/plugins/jj-nvim.nix index 2ef69af..ef0f68f 100644 --- a/modules/_neovim/plugins/jj-nvim.nix +++ b/modules/_neovim/plugins/jj-nvim.nix @@ -30,7 +30,6 @@ in { close_on_edit = false, }, }, - -- Disable default keymaps — we set our own in mappings.nix ui = { log = { keymaps = true, diff --git a/modules/_neovim/plugins/mini.nix b/modules/_neovim/plugins/mini.nix index d8c5fa2..41a9cb7 100644 --- a/modules/_neovim/plugins/mini.nix +++ b/modules/_neovim/plugins/mini.nix @@ -29,12 +29,12 @@ { mode = 'n', keys = 'e', desc = '+Explore/+Edit' }, { mode = 'n', keys = 'f', desc = '+Find' }, { mode = 'n', keys = 'v', desc = '+VCS' }, - { mode = 'n', keys = 'l', desc = '+LSP' }, - { mode = 'x', keys = 'l', desc = '+LSP' }, - { mode = 'n', keys = 'o', desc = '+OpenCode' }, - { mode = 'x', keys = 'o', desc = '+OpenCode' }, - { mode = 'n', keys = 'r', desc = '+Review' }, - { mode = 'v', keys = 'r', desc = '+Review' }, + { mode = 'n', keys = 'l', desc = '+LSP' }, + { mode = 'x', keys = 'l', desc = '+LSP' }, + { mode = 'n', keys = 'o', desc = '+OpenCode' }, + { mode = 'x', keys = 'o', desc = '+OpenCode' }, + { mode = 'n', keys = 'r', desc = '+Review' }, + { mode = 'v', keys = 'r', desc = '+Review' }, require("mini.clue").gen_clues.builtin_completion(), require("mini.clue").gen_clues.g(), require("mini.clue").gen_clues.marks(), @@ -159,193 +159,17 @@ }; }; move = {}; - notify = { - content.format.__raw = '' - function(notif) - local formatted = MiniNotify.default_format(notif) - return '\n ' .. formatted:gsub('\n', ' \n ') .. ' \n' - end - ''; - window.config = { - border = "none"; - title = ""; - }; - }; + notify = {}; pairs = {}; pick = {}; splitjoin = {}; starter = {}; - statusline = { - content.active.__raw = '' - function() - local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 }) - local diff = MiniStatusline.section_diff({ trunc_width = 75 }) - local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 }) - local lsp = MiniStatusline.section_lsp({ trunc_width = 75 }) - local filename = MiniStatusline.section_filename({ trunc_width = 140 }) - local search = MiniStatusline.section_searchcount({ trunc_width = 75 }) - - return _G.CschStatusline.active({ - mode = mode, - mode_hl = mode_hl, - diff = diff, - diagnostics = diagnostics, - lsp = lsp, - filename = filename, - search = search, - }) - end - ''; - }; + statusline = {}; surround = {}; trailspace = {}; visits = {}; }; mockDevIcons = true; }; - - extraConfigLua = '' - local mini_notify_group = vim.api.nvim_create_augroup('MiniNotifyDesign', { clear = true }) - _G.CschStatusline = _G.CschStatusline or {} - - local function to_hex(value) - return value and string.format('#%06x', value) or nil - end - - local function get_hl(name) - return vim.api.nvim_get_hl(0, { name = name, link = false }) - end - - local function get_fg(name, fallback) - return to_hex(get_hl(name).fg) or fallback - end - - local function get_bg(name, fallback) - return to_hex(get_hl(name).bg) or fallback - end - - local function set_statusline_highlights() - local block_bg = get_bg('CursorLine', get_bg('Visual', '#373b41')) - local block_fg = get_fg('StatusLine', get_fg('Normal', '#c5c8c6')) - - vim.api.nvim_set_hl(0, 'CschStatuslineBlock', { fg = block_fg, bg = block_bg }) - end - - local function statusline_group(hl, strings) - local parts = vim.tbl_filter(function(x) - return type(x) == 'string' and x ~= "" - end, strings or {}) - - if #parts == 0 then - return "" - end - - return string.format('%%#%s# %s ', hl, table.concat(parts, ' ')) - end - - local function statusline_block(text, hl) - if text == nil or text == "" then - return "" - end - - return string.format('%%#%s# %s ', hl, text) - end - - local function statusline_filesize() - local size = math.max(vim.fn.line2byte(vim.fn.line('$') + 1) - 1, 0) - - if size < 1024 then - return string.format('%dB', size) - elseif size < 1048576 then - return string.format('%.2fKiB', size / 1024) - end - - return string.format('%.2fMiB', size / 1048576) - end - - local function statusline_filetype() - local filetype = vim.bo.filetype - - if filetype == "" then - return vim.bo.buftype ~= "" and vim.bo.buftype or 'text' - end - - local icon = "" - if _G.MiniIcons ~= nil then - icon = _G.MiniIcons.get('filetype', filetype) or "" - end - - return (icon ~= "" and (icon .. ' ') or "") .. filetype - end - - local function statusline_fileinfo() - local label = statusline_filetype() - - if MiniStatusline.is_truncated(120) or vim.bo.buftype ~= "" then - return label - end - - return string.format('%s · %s', label, statusline_filesize()) - end - - local function statusline_location() - local line = vim.fn.line('.') - local total_lines = vim.fn.line('$') - local column = vim.fn.virtcol('.') - - if MiniStatusline.is_truncated(90) then - return string.format('Ln %d Col %d', line, column) - end - - return string.format('Ln %d/%d · Col %d', line, total_lines, column) - end - - function _G.CschStatusline.active(parts) - local left = vim.tbl_filter(function(x) - return x ~= "" - end, { - statusline_block(parts.mode, parts.mode_hl), - statusline_group('MiniStatuslineDevinfo', { parts.diff, parts.diagnostics, parts.lsp }), - '%<', - statusline_group('MiniStatuslineFilename', { parts.filename }), - }) - local right = vim.tbl_filter(function(x) - return x ~= "" - end, { - statusline_block(statusline_fileinfo(), 'CschStatuslineBlock'), - statusline_block(parts.search, 'CschStatuslineBlock'), - statusline_block(statusline_location(), parts.mode_hl), - }) - - return table.concat(left, "") .. '%=%#StatusLine#' .. table.concat(right, "") - end - - local function set_mini_notify_highlights() - local border = vim.api.nvim_get_hl(0, { name = 'FloatBorder' }) - local normal = vim.api.nvim_get_hl(0, { name = 'NormalFloat' }) - local popup_bg = get_bg('Pmenu', get_bg('CursorLine', get_bg('NormalFloat', '#303446'))) - local title = vim.api.nvim_get_hl(0, { name = 'FloatTitle' }) - - border.bg = 'NONE' - normal.bg = popup_bg - normal.bold = true - title.bg = 'NONE' - - vim.api.nvim_set_hl(0, 'MiniNotifyBorder', border) - vim.api.nvim_set_hl(0, 'MiniNotifyNormal', normal) - vim.api.nvim_set_hl(0, 'MiniNotifyTitle', title) - end - - vim.api.nvim_create_autocmd('ColorScheme', { - group = mini_notify_group, - callback = function() - set_mini_notify_highlights() - set_statusline_highlights() - end, - }) - - set_mini_notify_highlights() - set_statusline_highlights() - ''; }; } diff --git a/modules/_neovim/plugins/opencode.nix b/modules/_neovim/plugins/opencode.nix index bc4124c..f292e3c 100644 --- a/modules/_neovim/plugins/opencode.nix +++ b/modules/_neovim/plugins/opencode.nix @@ -14,283 +14,15 @@ }; in { programs.nixvim = { - extraPlugins = with pkgs.vimPlugins; [ + extraPlugins = [ opencode-nvim - plenary-nvim - render-markdown-nvim ]; extraConfigLua = '' - local api = vim.api - local opencode_output_filetype = 'opencode_output' - local opencode_window_filetypes = { - opencode = true, - opencode_output = true, - } - - local palette = { - base = '#faf4ed', - surface = '#fffaf3', - overlay = '#f2e9e1', - highlight_med = '#dfdad9', - text = '#575279', - subtle = '#797593', - muted = '#9893a5', - pine = '#286983', - iris = '#907aa9', - foam = '#56949f', - leaf = '#6d8f89', - gold = '#ea9d34', - rose = '#d7827e', - love = '#b4637a', - } - - local function set_highlights(highlights) - for group, spec in pairs(highlights) do - api.nvim_set_hl(0, group, spec) - end - end - - local opencode_markdown_conceal_query = vim.treesitter.query.parse('markdown_inline', [[ - [ - (emphasis_delimiter) - (code_span_delimiter) - (latex_span_delimiter) - ] @conceal - - (inline_link - [ - "[" - "]" - "(" - (link_destination) - ")" - ] @conceal) - - (full_reference_link - [ - "[" - "]" - (link_label) - ] @conceal) - - (collapsed_reference_link - [ - "[" - "]" - ] @conceal) - - (shortcut_link - [ - "[" - "]" - ] @conceal) - - (image - [ - "!" - "[" - "]" - "(" - (link_destination) - ")" - ] @conceal) - ]]) - - local function collect_conceal_marks(ctx) - local marks = {} - - for _, node in opencode_markdown_conceal_query:iter_captures(ctx.root, ctx.buf) do - local start_row, start_col, end_row, end_col = node:range() - marks[#marks + 1] = { - conceal = true, - start_row = start_row, - start_col = start_col, - opts = { - end_row = end_row, - end_col = end_col, - conceal = "", - }, - } - end - - return marks - end - - local function set_opencode_output_conceal() - if vim.bo.filetype ~= opencode_output_filetype then - return - end - - vim.wo.conceallevel = 3 - vim.wo.concealcursor = 'nvic' - end - - local function hide_opencode_statusline() - if not opencode_window_filetypes[vim.bo.filetype] then - return - end - - vim.wo.statusline = ' ' - end - - vim.treesitter.language.register('markdown', opencode_output_filetype) - vim.treesitter.language.register('markdown_inline', opencode_output_filetype) - - api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter' }, { - callback = set_opencode_output_conceal, - }) - api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter', 'BufEnter' }, { - pattern = '*', - callback = hide_opencode_statusline, - }) - - set_highlights({ - RenderMarkdownCode = { bg = palette.surface }, - RenderMarkdownCodeBorder = { fg = palette.highlight_med, bg = palette.surface }, - RenderMarkdownCodeInline = { bg = palette.surface }, - RenderMarkdownH1 = { fg = palette.pine, bold = true }, - RenderMarkdownH2 = { fg = palette.iris, bold = true }, - RenderMarkdownH3 = { fg = palette.foam, bold = true }, - RenderMarkdownH4 = { fg = palette.gold, bold = true }, - OpencodeInputLegend = { fg = palette.subtle, bold = true }, - OpencodeAgentBuild = { bg = palette.muted, fg = palette.base, bold = true }, - }) - - local render_markdown_config = { - anti_conceal = { enabled = false }, - heading = { - icons = { '◆ ', '◇ ', '○ ', '· ', '· ', '· ' }, - backgrounds = {}, - position = 'inline', - width = 'block', - left_pad = 0, - right_pad = 2, - border = false, - sign = false, - }, - code = { - sign = false, - width = 'full', - left_pad = 2, - right_pad = 0, - border = 'thin', - language_icon = false, - language_name = true, - }, - bullet = { - icons = { '·', '–', '·', '–' }, - }, - custom_handlers = { - markdown_inline = { - extends = true, - parse = collect_conceal_marks, - }, - }, - file_types = { opencode_output_filetype }, - win_options = { - conceallevel = { rendered = 3 }, - concealcursor = { rendered = 'nvic' }, - }, - } - - require('render-markdown').setup(render_markdown_config) - - local opencode_icon_overrides = { - header_user = '│', - header_assistant = '│', - run = '▸', - task = '◦', - read = '◦', - edit = '◦', - write = '◦', - plan = '◦', - search = '◦', - web = '◦', - list = '◦', - tool = '◦', - snapshot = '◦', - restore_point = '◦', - file = '·', - folder = '·', - attached_file = '·', - agent = '·', - reference = '·', - reasoning = '◦', - question = '?', - border = '│', - } - - require('opencode').setup({ - server = { - url = 'http://127.0.0.1', - port = 18822, - auto_kill = false, - }, - input = { - text = { - wrap = true, - }, - }, + require("opencode").setup({ debug = { show_ids = false, }, - ui = { - icons = { - preset = 'nerdfonts', - overrides = opencode_icon_overrides, - }, - }, }) - - do - local config = require('opencode.config') - local formatter = require('opencode.ui.formatter') - local format_utils = require('opencode.ui.formatter.utils') - local icons = require('opencode.ui.icons') - local util = require('opencode.util') - - local function format_reasoning_title(part) - local title = 'Reasoning' - local time = part.time - - if time and type(time) == 'table' and time.start then - local duration_text = util.format_duration_seconds(time.start, time['end']) - if duration_text then - title = string.format('%s %s', title, duration_text) - end - end - - return title - end - - local function highlight_reasoning_block(output, start_line) - local end_line = output:get_line_count() - - if end_line - start_line > 1 then - formatter.add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText') - return - end - - output:add_extmark(start_line - 1, { - line_hl_group = 'OpencodeReasoningText', - }) - end - - formatter._format_reasoning = function(output, part) - local text = vim.trim(part.text or "") - local start_line = output:get_line_count() + 1 - - format_utils.format_action(output, icons.get('reasoning'), format_reasoning_title(part), "") - - if config.ui.output.tools.show_reasoning_output and text ~= "" then - output:add_empty_line() - output:add_lines(vim.split(text, '\n'), ' ') - output:add_empty_line() - end - - highlight_reasoning_block(output, start_line) - end - end ''; }; } diff --git a/modules/_neovim/plugins/render-markdown.nix b/modules/_neovim/plugins/render-markdown.nix new file mode 100644 index 0000000..9f2e6a0 --- /dev/null +++ b/modules/_neovim/plugins/render-markdown.nix @@ -0,0 +1,9 @@ +{ + programs.nixvim.plugins.render-markdown = { + enable = true; + settings = { + anti_conceal = {enabled = false;}; + file_types = ["markdown" "opencode_output"]; + }; + }; +} diff --git a/modules/_neovim/plugins/treesitter.nix b/modules/_neovim/plugins/treesitter.nix index 22b01ea..2de8eec 100644 --- a/modules/_neovim/plugins/treesitter.nix +++ b/modules/_neovim/plugins/treesitter.nix @@ -9,52 +9,5 @@ indent.enable = true; }; }; - - # Register missing treesitter predicates for compatibility with newer grammars - extraConfigLuaPre = '' - do - local query = require("vim.treesitter.query") - local predicates = query.list_predicates() - if not vim.tbl_contains(predicates, "is-not?") then - query.add_predicate("is-not?", function(match, pattern, source, predicate) - local dominated_by = predicate[2] - local dominated = false - for _, node in pairs(match) do - if type(node) == "userdata" then - local current = node:parent() - while current do - if current:type() == dominated_by then - dominated = true - break - end - current = current:parent() - end - end - end - return not dominated - end, { force = true, all = true }) - end - end - - -- Fix grammar-bundled treesitter queries that use #match? with Lua pattern - -- syntax (e.g. %d) instead of Vim regex. Neovim 0.11 picks the first - -- non-extending query file in the rtp as the base, so the grammar-bundled - -- (buggy) queries take precedence over the corrected site-level queries. - -- Override affected languages with the site-level version. - do - local langs = { "sql" } - for _, lang in ipairs(langs) do - local files = vim.api.nvim_get_runtime_file( - "queries/" .. lang .. "/highlights.scm", true) - if #files > 1 then - local f = io.open(files[#files]) - if f then - vim.treesitter.query.set(lang, "highlights", f:read("*all")) - f:close() - end - end - end - end - ''; }; } diff --git a/modules/dendritic.nix b/modules/dendritic.nix index 4cde516..a4e5c7e 100644 --- a/modules/dendritic.nix +++ b/modules/dendritic.nix @@ -49,6 +49,10 @@ flake = false; }; nixvim.url = "github:nix-community/nixvim"; + neovim-nightly-overlay = { + url = "github:nix-community/neovim-nightly-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; llm-agents.url = "github:numtide/llm-agents.nix"; # Overlay inputs himalaya.url = "github:pimalaya/himalaya";