init wrapper-module config

This commit is contained in:
Daniel Winkler 2026-01-30 14:22:39 +11:00
commit 91755583fd
46 changed files with 4277 additions and 0 deletions

166
lua/config/nix.lua Normal file
View file

@ -0,0 +1,166 @@
local M = {}
local function detect_nix()
if vim.g.nix_info_plugin_name ~= nil then
return true
end
if vim.g[ [[nixCats-special-rtp-entry-nixCats]] ] ~= nil then
return true
end
return false
end
M.is_nix = detect_nix()
M.non_nix_default = true
local function setup_nixcats(non_nix_value)
if M.is_nix then
return
end
local ok, utils = pcall(require, "nixCatsUtils")
if ok and utils and utils.setup then
utils.setup { non_nix_value = non_nix_value }
end
end
local function init_from_nix_info()
if vim.g.nix_info_plugin_name == nil then
return nil
end
local ok, nix_info = pcall(require, vim.g.nix_info_plugin_name)
if not ok or not nix_info then
return nil
end
local function cat_lookup(cat)
if type(cat) == "table" then
for _, name in ipairs(cat) do
if nix_info(false, "settings", "cats", name) or nix_info(false, "info", "cats", name) then
return true
end
end
return false
end
return nix_info(false, "settings", "cats", cat) or nix_info(false, "info", "cats", cat)
end
local nc = setmetatable({
cats = nix_info.settings and nix_info.settings.cats or {},
settings = nix_info.settings or {},
get = function(_, default, ...) return nix_info(default, ...) end,
}, {
__call = function(_, default, ...)
if select("#", ...) == 0 then
return cat_lookup(default)
end
return nix_info(default, ...)
end,
__index = nix_info,
})
_G.nixCats = nc
package.preload['nixCats.cats'] = function()
return setmetatable(_G.nixCats.cats or {}, getmetatable(_G.nixCats))
end
return nc
end
local function get_nixcats()
if _G.nixCats then
return _G.nixCats
end
local nc = init_from_nix_info()
if nc then
return nc
end
local ok, nc_module = pcall(require, "nixCats")
if ok and nc_module then
return nc_module
end
return nil
end
function M.init(opts)
local non_nix_value = true
if type(opts) == "table" and type(opts.non_nix_value) == "boolean" then
non_nix_value = opts.non_nix_value
end
M.non_nix_default = non_nix_value
setup_nixcats(non_nix_value)
get_nixcats()
M.is_nix = detect_nix()
return M
end
local function cat_enabled(nc, cat, default)
if not nc then
return default
end
if type(cat) == "table" then
for _, name in ipairs(cat) do
if nc(name) then
return true
end
end
return default
end
local val = nc(cat)
if val == nil then
return default
end
return val
end
function M.get_cat(cat, default)
if default == nil then
default = M.non_nix_default
end
if not M.is_nix then
return default
end
local nc = get_nixcats()
return cat_enabled(nc, cat, default)
end
function M.get_setting(default, ...)
if not M.is_nix then
return default
end
local nc = get_nixcats()
if not nc then
return default
end
local value = nc(default, "settings", ...)
if value == nil then
return default
end
return value
end
function M.get_info(default, ...)
if not M.is_nix then
return default
end
local nc = get_nixcats()
if not nc then
return default
end
local value = nc(default, "info", ...)
if value == nil then
return default
end
return value
end
return M

129
lua/nixCatsUtils/init.lua Normal file
View file

@ -0,0 +1,129 @@
--[[
This directory is the luaUtils template.
You can choose what things from it that you would like to use.
And then delete the rest.
Everything in this directory is optional.
--]]
local M = {}
--[[
This file is for making your config still work WITHOUT nixCats.
When you don't use nixCats to load your config,
you wont have the nixCats plugin.
The setup function defined here defines a mock nixCats plugin when nixCats wasnt used to load the config.
This will help avoid indexing errors when the nixCats plugin doesnt exist.
NOTE: If you only ever use nixCats to load your config, you don't need this file.
--]]
---@type boolean
M.isNixCats = vim.g.nix_info_plugin_name ~= nil or vim.g[ [[nixCats-special-rtp-entry-nixCats]] ] ~= nil
---@class nixCatsSetupOpts
---@field non_nix_value boolean|nil
---This function will setup a mock nixCats plugin when not using nix
---It will help prevent you from running into indexing errors without a nixCats plugin from nix.
---If you loaded the config via nix, it does nothing
---non_nix_value defaults to true if not provided or is not a boolean.
---@param v nixCatsSetupOpts
function M.setup(v)
if not M.isNixCats then
local nixCats_default_value
if type(v) == "table" and type(v.non_nix_value) == "boolean" then
nixCats_default_value = v.non_nix_value
else
nixCats_default_value = true
end
local mk_with_meta = function (tbl)
return setmetatable(tbl, {
__call = function(_, attrpath)
local strtable = {}
if type(attrpath) == "table" then
strtable = attrpath
elseif type(attrpath) == "string" then
for key in attrpath:gmatch("([^%.]+)") do
table.insert(strtable, key)
end
else
print("function requires a table of strings or a dot separated string")
return
end
return vim.tbl_get(tbl, unpack(strtable));
end
})
end
package.preload['nixCats'] = function ()
local ncsub = {
get = function(_) return nixCats_default_value end,
cats = mk_with_meta({
nixCats_config_location = vim.fn.stdpath('config'),
wrapRc = false,
}),
settings = mk_with_meta({
nixCats_config_location = vim.fn.stdpath('config'),
configDirName = os.getenv("NVIM_APPNAME") or "nvim",
wrapRc = false,
}),
petShop = mk_with_meta({}),
extra = mk_with_meta({}),
pawsible = mk_with_meta({
allPlugins = {
start = {},
opt = {},
},
}),
configDir = vim.fn.stdpath('config'),
packageBinPath = os.getenv('NVIM_WRAPPER_PATH_NIX') or vim.v.progpath
}
return setmetatable(ncsub, {__call = function(_, cat) return ncsub.get(cat) end})
end
_G.nixCats = require('nixCats')
end
end
---allows you to guarantee a boolean is returned, and also declare a different
---default value than specified in setup when not using nix to load the config
---@overload fun(v: string|string[]): boolean
---@overload fun(v: string|string[], default: boolean): boolean
function M.enableForCategory(v, default)
if M.isNixCats or default == nil then
if nixCats(v) then
return true
else
return false
end
else
return default
end
end
---if nix, return value of nixCats(v) else return default
---Exists to specify a different non_nix_value than the one in setup()
---@param v string|string[]
---@param default any
---@return any
function M.getCatOrDefault(v, default)
if M.isNixCats then
return nixCats(v)
else
return default
end
end
---for conditionally disabling build steps on nix, as they are done via nix
---I should probably have named it dontAddIfCats or something.
---@overload fun(v: any): any|nil
---Will return the second value if nix, otherwise the first
---@overload fun(v: any, o: any): any
function M.lazyAdd(v, o)
if M.isNixCats then
return o
else
return v
end
end
return M

View file

@ -0,0 +1,37 @@
--[[
This directory is the luaUtils template.
You can choose what things from it that you would like to use.
And then delete the rest.
Everything in this directory is optional.
--]]
local M = {}
local nix = require('config.nix')
-- A nixCats specific lze handler that you can use to conditionally enable by category easier.
-- at the start of your config, register with
-- require('lze').register_handlers(require('nixCatsUtils.lzUtils').for_cat)
-- before any calls to require('lze').load using the handler have been made.
-- accepts:
-- for_cat = { "your" "cat" };
-- for_cat = { cat = { "your" "cat" }, default = bool }
-- for_cat = "your.cat";
-- for_cat = { cat = "your.cat", default = bool }
-- where default is an alternate value for when nixCats was NOT used to install the config
M.for_cat = {
spec_field = "for_cat",
set_lazy = false,
modify = function(plugin)
if type(plugin.for_cat) == "table" and plugin.for_cat.cat ~= nil then
local default = plugin.for_cat.default
if default == nil then
default = false
end
plugin.enabled = nix.get_cat(plugin.for_cat.cat, default)
else
plugin.enabled = nix.get_cat(plugin.for_cat, false)
end
return plugin
end,
}
return M

196
lua/nix_smart_send.lua Normal file
View file

@ -0,0 +1,196 @@
local M = {}
-- Helper function to check if value exists in list (optimized with early return)
local function is_in_list(list, value)
if not list or not value then
return false
end
for _, v in ipairs(list) do
if v == value then
return true
end
end
return false
end
-- Define comment node types as constants
local COMMENT_TYPES = {
comment = true,
block_comment = true,
line_comment = true,
}
function M.get_current_node()
local ts_utils = require('nvim-treesitter.ts_utils')
local cur_win = vim.api.nvim_get_current_win()
return ts_utils.get_node_at_cursor(cur_win, true)
end
function M.detect_global_node()
local cur_node = M.get_current_node()
local root
if not cur_node then
-- print("No node detected")
local parser = vim.treesitter.get_parser()
if not parser then
return nil
end
root = parser:parse()[1]:root()
else
root = cur_node:root()
end
if not root then
return nil
end
return root:type()
end
function M.move_to_next_non_empty_line()
local ts_utils = require('nvim-treesitter.ts_utils')
-- Search for the next non-empty line
local line_num = vim.fn.search("[^;\\s]", "W")
if line_num <= 0 then
-- print("No non-empty line found below the current position")
return false
end
-- Get the line content and find first non-whitespace character
local line_content = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num, false)[1]
local first_non_ws = line_content:find("%S") or 1
vim.api.nvim_win_set_cursor(0, { line_num, first_non_ws - 1 })
local node = M.get_current_node()
if not node or not node:type() then
-- print("No node found")
return false
end
local global_node_type = M.detect_global_node()
-- Skip comments and global nodes
while node and (COMMENT_TYPES[node:type()] or node:type() == global_node_type) do
line_num = line_num + 1
local max_lines = vim.api.nvim_buf_line_count(0)
if line_num > max_lines then
-- print("Reached end of buffer")
return false
end
-- Get the line content and find first non-whitespace character
line_content = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num, false)[1]
first_non_ws = line_content:find("%S") or 1
vim.api.nvim_win_set_cursor(0, { line_num, first_non_ws - 1 })
node = ts_utils.get_node_at_cursor()
end
return true
end
function M.vselect_node(node)
local ts_utils = require('nvim-treesitter.ts_utils')
if not node then
return false
end
local cur_buf = vim.api.nvim_get_current_buf()
ts_utils.update_selection(cur_buf, node, "V")
return true
end
function M.select_until_global(global_nodes)
local ts_utils = require('nvim-treesitter.ts_utils')
local root_node = M.detect_global_node()
if not root_node and global_nodes then
root_node = global_nodes[1]
end
-- Use empty table if no global nodes provided
global_nodes = global_nodes or {}
local node = ts_utils.get_node_at_cursor()
if not node then
-- print("No syntax node found at cursor position")
return nil
end
local node_type = node:type()
if node_type == root_node then
-- print("Cursor is on the root " .. root_node .. " node or in an empty area.")
return nil
end
-- Check if current node is a global
if is_in_list(global_nodes, node_type) then
if M.vselect_node(node) then
return node
end
end
-- Traverse up the tree until we find a global node or reach the root
local parent = node:parent()
local parent_type = parent:type() or ""
if parent and is_in_list(global_nodes, parent_type) then
if M.vselect_node(node) then
return node
end
end
while parent and not is_in_list(global_nodes, parent:type()) do
node = parent
parent = node:parent()
end
if M.vselect_node(node) then
return node
end
return nil
end
function M.slime_send_region()
-- Check if slime plugin is available
if not vim.fn.exists('*slime#send_op') then
vim.notify("slime plugin not available", vim.log.levels.ERROR)
return
end
local slime_command = ":<C-u>call slime#send_op(visualmode(), 1)<CR>"
local termcodes = vim.api.nvim_replace_termcodes(slime_command, true, true, true)
vim.api.nvim_feedkeys(termcodes, "x", true)
end
function M.send_repl(global_nodes)
local ts_utils = require('nvim-treesitter.ts_utils')
local cur_node = M.get_current_node()
if not cur_node then
M.move_to_next_non_empty_line()
else
local cur_type = cur_node:type()
if COMMENT_TYPES[cur_type] or is_in_list(global_nodes, cur_type) then
M.move_to_next_non_empty_line()
end
end
local sel_node = M.select_until_global(global_nodes)
if not sel_node then
-- print("No node selected for REPL")
return
end
-- Send the selected text to the terminal using vim-slime
M.slime_send_region()
-- Move cursor and continue
ts_utils.goto_node(sel_node, true)
M.move_to_next_non_empty_line()
end
return M