mirror of
https://github.com/iofq/nvim.nix.git
synced 2026-01-23 08:55:16 -06:00
flake.lock: Update
Flake lock file updates:
• Updated input 'neovim-nightly-overlay':
'github:nix-community/neovim-nightly-overlay/b7b3632fab34541543373254c265da03c1d0f240?narHash=sha256-SDLZMha2miaChOBGBkQJlTtk2kvcB7WIVVZ2l0/9byc%3D' (2025-10-01)
→ 'github:nix-community/neovim-nightly-overlay/7ff73a295b0065bde2f8b43b31558136ca77bd98?narHash=sha256-39JFRb006AOsIcOq%2B03H3y6YcGyaphk1CW5DRi14cOE%3D' (2025-10-13)
• Updated input 'neovim-nightly-overlay/flake-parts':
'github:hercules-ci/flake-parts/4524271976b625a4a605beefd893f270620fd751?narHash=sha256-%2BuWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw%3D' (2025-09-01)
→ 'github:hercules-ci/flake-parts/758cf7296bee11f1706a574c77d072b8a7baa881?narHash=sha256-wfG0S7pltlYyZTM%2BqqlhJ7GMw2fTF4mLKCIVhLii/4M%3D' (2025-10-01)
• Updated input 'neovim-nightly-overlay/git-hooks':
'github:cachix/git-hooks.nix/54df955a695a84cd47d4a43e08e1feaf90b1fd9b?narHash=sha256-ytw7ROXaWZ7OfwHrQ9xvjpUWeGVm86pwnEd1QhzawIo%3D' (2025-09-17)
→ 'github:cachix/git-hooks.nix/cfc9f7bb163ad8542029d303e599c0f7eee09835?narHash=sha256-PTod9NG%2Bi3XbbnBKMl/e5uHDBYpwIWivQ3gOWSEuIEM%3D' (2025-10-03)
• Updated input 'neovim-nightly-overlay/neovim-src':
'github:neovim/neovim/198c9e9bca7549cf7110be8c6f1df1c38d4e747f?narHash=sha256-oc1pPeoe8458X0r/Wum8CucVcJLlIp4mg%2B1HiOSVRgA%3D' (2025-09-30)
→ 'github:neovim/neovim/72b0bfa1fb7e897e5126aabae718a5480f466b9e?narHash=sha256-RnB%2BoJcP37YT9pjr1osUQtJc%2B1qcVyaaeSbiSyXwm1Y%3D' (2025-10-12)
• Updated input 'neovim-nightly-overlay/nixpkgs':
'github:NixOS/nixpkgs/647e5c14cbd5067f44ac86b74f014962df460840?narHash=sha256-JVZl8NaVRYb0%2B381nl7LvPE%2BA774/dRpif01FKLrYFQ%3D' (2025-09-28)
→ 'github:NixOS/nixpkgs/362791944032cb532aabbeed7887a441496d5e6e?narHash=sha256-gKl2Gtro/LNf8P%2B4L3S2RsZ0G390ccd5MyXYrTdMCFE%3D' (2025-10-11)
• Updated input 'neovim-nightly-overlay/treefmt-nix':
'github:numtide/treefmt-nix/5eda4ee8121f97b218f7cc73f5172098d458f1d1?narHash=sha256-ySNJ008muQAds2JemiyrWYbwbG%2BV7S5wg3ZVKGHSFu8%3D' (2025-09-24)
→ 'github:numtide/treefmt-nix/761ae7aff00907b607125b2f57338b74177697ed?narHash=sha256-gq9rdocpmRZCwLS5vsHozwB6b5nrOBDNc2kkEaTXHfg%3D' (2025-10-10)
• Updated input 'nixpkgs':
'github:NixOS/nixpkgs/d48b56f4899ee1d5377291576284969c9e37acc2?narHash=sha256-7G5fSMPNVwWNlFpZLwvKB8PUOaqPzFQbrtZ7RAzg2nw%3D' (2025-10-01)
→ 'github:NixOS/nixpkgs/09c05674757a82f32b67f70f702e56c4901a3cdd?narHash=sha256-IzUhHp/jU06jBW6S%2Bd12AO3sgq5yehYVTCBH7J6re5c%3D' (2025-10-13)
flake.lock: Update
Flake lock file updates:
• Updated input 'neovim-nightly-overlay/nixpkgs':
'github:NixOS/nixpkgs/362791944032cb532aabbeed7887a441496d5e6e?narHash=sha256-gKl2Gtro/LNf8P%2B4L3S2RsZ0G390ccd5MyXYrTdMCFE%3D' (2025-10-11)
→ follows 'nixpkgs'
This commit is contained in:
parent
8922491578
commit
49e373fe9a
18 changed files with 431 additions and 643 deletions
139
nvim/after/plugin/autocmd.lua
Normal file
139
nvim/after/plugin/autocmd.lua
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
local cmd = vim.api.nvim_create_autocmd
|
||||
-- open :h in buffers
|
||||
cmd('FileType', {
|
||||
group = vim.api.nvim_create_augroup('help', { clear = true }),
|
||||
pattern = 'help',
|
||||
callback = function(_)
|
||||
vim.cmd.only()
|
||||
vim.keymap.set('n', 'q', vim.cmd.bdelete, { noremap = true })
|
||||
vim.bo.buflisted = false
|
||||
end,
|
||||
})
|
||||
|
||||
-- resize splits if window got resized
|
||||
cmd({ 'VimResized' }, {
|
||||
group = vim.api.nvim_create_augroup('resize_splits', { clear = true }),
|
||||
callback = function()
|
||||
vim.cmd('tabdo wincmd =')
|
||||
vim.cmd('tabnext ' .. vim.fn.tabpagenr())
|
||||
end,
|
||||
})
|
||||
|
||||
-- Check if we need to reload the file when it changed
|
||||
cmd({ 'FocusGained', 'TermClose', 'TermLeave' }, {
|
||||
group = vim.api.nvim_create_augroup('check_reload', { clear = true }),
|
||||
callback = function()
|
||||
if vim.o.buftype ~= 'nofile' then
|
||||
vim.cmd('checktime')
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Configure difftool buffers
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
pattern = 'qf',
|
||||
group = vim.api.nvim_create_augroup('difftool', { clear = true }),
|
||||
callback = function(event)
|
||||
local function exec(fmt, str)
|
||||
os.execute(string.format(fmt, str))
|
||||
end
|
||||
local function refresh()
|
||||
local qf = vim.fn.getqflist()
|
||||
|
||||
local entry = qf[1]
|
||||
if not entry or not entry.user_data.diff then
|
||||
return nil
|
||||
end
|
||||
|
||||
local ns = vim.api.nvim_create_namespace('nvim.difftool.hl')
|
||||
vim.api.nvim_buf_clear_namespace(event.buf, ns, 0, -1)
|
||||
for i, item in ipairs(qf) do
|
||||
local path = vim.fn.fnamemodify(item.user_data.right, ':t')
|
||||
local hl = 'Added'
|
||||
if
|
||||
exec('git diff --quiet -- %s', path) ~= 0
|
||||
or exec('git ls-files --error-unmatch -- %s > /dev/null 2>&1', path) ~= 0
|
||||
then
|
||||
hl = 'Removed'
|
||||
end
|
||||
vim.hl.range(event.buf, ns, hl, { i - 1, 0 }, { i - 1, -1 })
|
||||
end
|
||||
end
|
||||
vim.keymap.set('n', 'gh', function()
|
||||
local idx = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local qf = vim.fn.getqflist()
|
||||
local filename = qf[idx].user_data.rel
|
||||
|
||||
if exec('git diff --quiet --cached -- %s', filename) ~= 0 then
|
||||
exec('git restore --quiet --staged -- %s', filename)
|
||||
else
|
||||
exec('git add -- %s', filename)
|
||||
end
|
||||
refresh()
|
||||
end)
|
||||
vim.schedule(refresh)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Init treesitter
|
||||
cmd('FileType', {
|
||||
group = vim.api.nvim_create_augroup('treesitter', { clear = true }),
|
||||
callback = function(event)
|
||||
local bufnr = event.buf
|
||||
|
||||
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
||||
pcall(vim.treesitter.start, bufnr)
|
||||
|
||||
vim.keymap.set({ 'v', 'n' }, ']]', function()
|
||||
require('nvim-treesitter-textobjects.move').goto_next_start('@function.outer', 'textobjects')
|
||||
end, { buffer = bufnr })
|
||||
vim.keymap.set({ 'v', 'n' }, '[[', function()
|
||||
require('nvim-treesitter-textobjects.move').goto_previous_start('@function.outer', 'textobjects')
|
||||
end, { buffer = bufnr })
|
||||
vim.keymap.set({ 'v', 'n' }, ']a', function()
|
||||
require('nvim-treesitter-textobjects.move').goto_next_start('@parameter.inner', 'textobjects')
|
||||
end, { buffer = bufnr })
|
||||
vim.keymap.set({ 'v', 'n' }, '[a', function()
|
||||
require('nvim-treesitter-textobjects.move').goto_previous_start('@parameter.inner', 'textobjects')
|
||||
end, { buffer = bufnr })
|
||||
vim.keymap.set({ 'v', 'n' }, ']A', function()
|
||||
require('nvim-treesitter-textobjects.swap').swap_next('@parameter.inner')
|
||||
end, { buffer = bufnr })
|
||||
vim.keymap.set({ 'v', 'n' }, '[A', function()
|
||||
require('nvim-treesitter-textobjects.swap').swap_previous('@parameter.inner')
|
||||
end, { buffer = bufnr })
|
||||
end,
|
||||
})
|
||||
|
||||
-- Init LSP
|
||||
cmd('LspAttach', {
|
||||
group = vim.api.nvim_create_augroup('UserLspConfig', {}),
|
||||
callback = function(ev)
|
||||
local client = vim.lsp.get_client_by_id(ev.data.client_id)
|
||||
if not client then
|
||||
return
|
||||
end
|
||||
vim.keymap.set('n', 'gO', function()
|
||||
Snacks.picker.lsp_symbols { focus = 'list' }
|
||||
end, { buffer = ev.buf })
|
||||
|
||||
vim.keymap.set('n', 'grh', function()
|
||||
vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled())
|
||||
end, { buffer = ev.buf })
|
||||
vim.keymap.set('n', 'grl', vim.lsp.codelens.run, { buffer = ev.buf })
|
||||
|
||||
vim.keymap.set('n', 'gre', vim.diagnostic.setloclist, { buffer = ev.buf })
|
||||
vim.keymap.set('n', 'grE', vim.diagnostic.setqflist, { buffer = ev.buf })
|
||||
|
||||
-- Auto-refresh code lenses
|
||||
if client:supports_method('textDocument/codeLens') or client.server_capabilities.codeLensProvider then
|
||||
vim.lsp.codelens.refresh { bufnr = ev.buf }
|
||||
cmd({ 'InsertLeave', 'TextChanged' }, {
|
||||
callback = function()
|
||||
vim.lsp.codelens.refresh { bufnr = ev.buf }
|
||||
end,
|
||||
buffer = ev.buf,
|
||||
})
|
||||
end
|
||||
end,
|
||||
})
|
||||
64
nvim/after/plugin/init.lua
Normal file
64
nvim/after/plugin/init.lua
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
vim.cmd('colorscheme iofq')
|
||||
|
||||
vim.g.mapleader = ' '
|
||||
vim.opt.autowrite = true
|
||||
vim.opt.backspace = 'indent,eol,start'
|
||||
vim.opt.confirm = true
|
||||
vim.opt.completeopt = 'menuone,popup,noselect,fuzzy'
|
||||
vim.opt.diffopt = 'internal,filler,closeoff,inline:char'
|
||||
vim.opt.expandtab = true -- insert tabs as spaces
|
||||
vim.opt.inccommand = 'split' -- incremental live completion
|
||||
vim.opt.laststatus = 1 -- statusline only if split
|
||||
vim.opt.nrformats:append('alpha') -- let Ctrl-a do letters as well
|
||||
vim.opt.path:append('**') -- enable fuzzy :find ing
|
||||
vim.opt.relativenumber = true
|
||||
vim.opt.shadafile = 'NONE' -- disable shada (unless session)
|
||||
vim.opt.shiftwidth = 0 -- >> shifts by tabstop
|
||||
vim.opt.showmatch = true -- highlight matching brackets
|
||||
vim.opt.showmode = true
|
||||
vim.opt.signcolumn = 'no'
|
||||
vim.opt.softtabstop = -1 -- backspace removes tabstop
|
||||
vim.opt.swapfile = false
|
||||
vim.opt.tabstop = 2 -- 2 space tabs are based
|
||||
vim.opt.updatetime = 250 -- decrease update time
|
||||
vim.opt.virtualedit = 'onemore'
|
||||
vim.opt.winborder = 'rounded'
|
||||
|
||||
-- Configure Neovim diagnostic messages
|
||||
vim.diagnostic.config {
|
||||
virtual_text = true,
|
||||
underline = true,
|
||||
severity_sort = true,
|
||||
float = {
|
||||
focusable = false,
|
||||
style = 'minimal',
|
||||
source = 'if_many',
|
||||
},
|
||||
}
|
||||
|
||||
vim.lsp.enable {
|
||||
'nixd',
|
||||
'phpactor',
|
||||
'gopls',
|
||||
'lua_ls',
|
||||
'basedpyright',
|
||||
}
|
||||
|
||||
local map = vim.keymap.set
|
||||
map('n', '\\t', function() -- Switch tab length on the fly
|
||||
vim.o.tabstop = vim.o.tabstop == 8 and 2 or 2 * vim.o.tabstop
|
||||
vim.notify('tabstop: ' .. vim.o.tabstop)
|
||||
end)
|
||||
map({ 'v', 'i' }, 'wq', '<esc>l')
|
||||
map('v', '<', '<gv')
|
||||
map('v', '>', '>gv')
|
||||
map('n', 'n', 'nzz', { noremap = true })
|
||||
map('n', 'N', 'Nzz', { noremap = true })
|
||||
map('n', '<C-u>', '<C-u>zz', { noremap = true })
|
||||
map('n', '<C-d>', '<C-d>zz', { noremap = true })
|
||||
map('n', 'gq', vim.cmd.bdelete, { noremap = true })
|
||||
map('n', 'gQ', function()
|
||||
vim.cmd('bufdo bdelete')
|
||||
end, { noremap = true })
|
||||
|
||||
vim.cmd.packadd('nvim.difftool')
|
||||
78
nvim/after/plugin/mini.lua
Normal file
78
nvim/after/plugin/mini.lua
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
local map = vim.keymap.set
|
||||
|
||||
vim.schedule(function()
|
||||
require('mini.align').setup()
|
||||
require('mini.surround').setup()
|
||||
require('mini.splitjoin').setup { detect = { separator = '[,;\n]' } }
|
||||
|
||||
local ai = require('mini.ai')
|
||||
ai.setup {
|
||||
n_lines = 300,
|
||||
custom_textobjects = {
|
||||
i = require('mini.extra').gen_ai_spec.indent(),
|
||||
b = require('mini.extra').gen_ai_spec.buffer(),
|
||||
a = ai.gen_spec.treesitter { a = '@parameter.outer', i = '@parameter.inner' },
|
||||
f = ai.gen_spec.treesitter { a = '@function.outer', i = '@function.inner' },
|
||||
},
|
||||
}
|
||||
|
||||
require('mini.git').setup()
|
||||
map('n', '<leader>gb', '<Cmd>Git blame -- %<CR>')
|
||||
map('n', '<leader>go', function()
|
||||
return MiniGit.show_at_cursor()
|
||||
end)
|
||||
|
||||
local jump = require('mini.jump2d')
|
||||
jump.setup {
|
||||
mappings = {
|
||||
start_jumping = '<leader>s',
|
||||
},
|
||||
view = { n_steps_ahead = 1, dim = true },
|
||||
spotter = jump.gen_spotter.vimpattern(),
|
||||
}
|
||||
|
||||
local diff = require('mini.diff')
|
||||
diff.setup {
|
||||
source = {
|
||||
require('iofq.minidiff_jj').gen_source(),
|
||||
diff.gen_source.git(),
|
||||
},
|
||||
}
|
||||
map('n', '<leader>gp', MiniDiff.toggle_overlay)
|
||||
|
||||
require('mini.files').setup {
|
||||
mappings = { go_in_plus = '<CR>' },
|
||||
windows = {
|
||||
preview = true,
|
||||
width_preview = 50,
|
||||
},
|
||||
}
|
||||
map('n', '<leader>nc', function()
|
||||
MiniFiles.open(vim.api.nvim_buf_get_name(0), false) -- open current buffer's dir
|
||||
end)
|
||||
vim.api.nvim_create_autocmd('User', {
|
||||
pattern = 'MiniFilesBufferCreate',
|
||||
callback = function(args)
|
||||
map('n', '<leader>nc', function()
|
||||
MiniFiles.synchronize()
|
||||
MiniFiles.close()
|
||||
end, { buffer = args.data.buf_id })
|
||||
map('n', '`', function()
|
||||
local _, cur_entry_path = pcall(MiniFiles.get_fs_entry().path)
|
||||
local cur_directory = vim.fs.dirname(cur_entry_path)
|
||||
if cur_directory ~= '' then
|
||||
vim.fn.chdir(cur_directory)
|
||||
end
|
||||
end, { buffer = args.data.buf_id })
|
||||
end,
|
||||
})
|
||||
|
||||
-- pass file rename events to LSP
|
||||
vim.api.nvim_create_autocmd('User', {
|
||||
group = vim.api.nvim_create_augroup('snacks_rename', { clear = true }),
|
||||
pattern = 'MiniFilesActionRename',
|
||||
callback = function(event)
|
||||
Snacks.rename.on_rename_file(event.data.from, event.data.to)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
153
nvim/after/plugin/plugins.lua
Normal file
153
nvim/after/plugin/plugins.lua
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
local map = vim.keymap.set
|
||||
|
||||
require('mini.basics').setup { mappings = { windows = true } }
|
||||
require('mini.icons').setup()
|
||||
|
||||
require('dart').setup {
|
||||
tabline = {
|
||||
icons = false,
|
||||
label_marked_fg = 'cyan',
|
||||
},
|
||||
}
|
||||
|
||||
require('snacks').setup {
|
||||
bigfile = { enabled = true },
|
||||
terminal = { enabled = true },
|
||||
indent = { enabled = true },
|
||||
input = { enabled = true },
|
||||
notifier = { enabled = true },
|
||||
styles = { notification = { wo = { wrap = true } } },
|
||||
picker = {
|
||||
enabled = true,
|
||||
matcher = {
|
||||
frecency = true,
|
||||
cwd_bonus = true,
|
||||
},
|
||||
layout = 'ivy_split',
|
||||
sources = {
|
||||
grep = { hidden = true },
|
||||
lsp_symbols = {
|
||||
filter = { default = true },
|
||||
layout = {
|
||||
preset = 'left',
|
||||
layout = { width = 90, min_width = 90 },
|
||||
},
|
||||
},
|
||||
smart = {
|
||||
multi = {
|
||||
'buffers',
|
||||
{ source = 'files', hidden = true },
|
||||
{ source = 'git_files', untracked = true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
map({ 'n', 't' }, '<C-\\>', Snacks.terminal.toggle)
|
||||
map('n', '<leader>ff', Snacks.picker.smart)
|
||||
map('n', '<leader><leader>', Snacks.picker.smart)
|
||||
map('n', '<leader>fa', Snacks.picker.grep)
|
||||
map('n', '<leader>f8', Snacks.picker.grep_word)
|
||||
map('n', '<leader>f?', Snacks.picker.pickers)
|
||||
map('n', '<leader>fu', Snacks.picker.undo)
|
||||
map('n', '<leader>fj', Snacks.picker.jumps)
|
||||
map('n', '<leader>f.', Snacks.picker.resume)
|
||||
map('n', '<leader>fb', Snacks.picker.buffers)
|
||||
map('n', '<leader>fq', Snacks.picker.qflist)
|
||||
map('n', '<leader>jf', require('iofq.snacks_jj').status)
|
||||
|
||||
vim.schedule(function()
|
||||
require('nvim-treesitter').setup()
|
||||
require('nvim-treesitter-textobjects').setup()
|
||||
require('render-markdown').setup()
|
||||
require('nvim-autopairs').setup()
|
||||
|
||||
require('refactoring').setup()
|
||||
map('n', '<leader>rr', require('refactoring').select_refactor)
|
||||
map('n', '<leader>rv', function()
|
||||
require('refactoring').refactor('Inline Variable')
|
||||
end)
|
||||
|
||||
require('quicker').setup()
|
||||
map('n', '<leader>qf', function()
|
||||
require('quicker').toggle { max_height = 20 }
|
||||
end)
|
||||
|
||||
require('conform').setup {
|
||||
notify_no_formatters = false,
|
||||
formatters_by_ft = {
|
||||
json = { 'jq' },
|
||||
lua = { 'stylua' },
|
||||
python = { 'ruff' },
|
||||
nix = { 'nixfmt' },
|
||||
fish = { 'fish_indent' },
|
||||
['*'] = { 'trim_whitespace' },
|
||||
},
|
||||
format_on_save = function(bufnr)
|
||||
-- Disable with a global or buffer-local variable
|
||||
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
|
||||
return
|
||||
end
|
||||
return { timeout_ms = 1500, lsp_format = 'fallback' }
|
||||
end,
|
||||
}
|
||||
map('n', '\\f', function()
|
||||
vim.b.disable_autoformat = not vim.b.disable_autoformat
|
||||
vim.notify(string.format('Buffer formatting disabled: %s', vim.b.disable_autoformat))
|
||||
end)
|
||||
map('n', '\\F', function()
|
||||
vim.g.disable_autoformat = not vim.g.disable_autoformat
|
||||
vim.notify(string.format('Global formatting disabled: %s', vim.g.disable_autoformat))
|
||||
end)
|
||||
|
||||
require('lint').linters_by_ft = {
|
||||
docker = { 'hadolint' },
|
||||
yaml = { 'yamllint' },
|
||||
sh = { 'shellcheck' },
|
||||
go = { 'golangcilint' },
|
||||
ruby = { 'rubocop' },
|
||||
fish = { 'fish' },
|
||||
bash = { 'bash' },
|
||||
nix = { 'nix' },
|
||||
php = { 'php' },
|
||||
}
|
||||
vim.api.nvim_create_autocmd({ 'BufWritePost' }, {
|
||||
callback = function()
|
||||
require('lint').try_lint(nil, { ignore_errors = true })
|
||||
end,
|
||||
})
|
||||
|
||||
vim.treesitter.language.register('markdown', 'blink-cmp-documentation')
|
||||
require('blink.cmp').setup {
|
||||
enabled = function()
|
||||
return not vim.tbl_contains({ 'snacks_picker_input' }, vim.bo.filetype)
|
||||
end,
|
||||
sources = {
|
||||
default = { 'lsp', 'path', 'snippets', 'ripgrep', 'buffer' },
|
||||
providers = {
|
||||
lsp = { fallbacks = {} }, -- include buffer even when LSP is active
|
||||
path = { opts = { get_cwd = vim.fn.getcwd } }, -- use nvim pwd instead of current file pwd
|
||||
ripgrep = {
|
||||
module = 'blink-ripgrep',
|
||||
name = 'rg',
|
||||
score_offset = -10,
|
||||
async = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
cmdline = { completion = { menu = { auto_show = true } } },
|
||||
completion = {
|
||||
documentation = { auto_show = true },
|
||||
menu = {
|
||||
draw = {
|
||||
columns = {
|
||||
{ 'label', 'label_description', gap = 1 },
|
||||
{ 'source_name', 'kind', gap = 1 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
signature = { enabled = true, trigger = { show_on_insert = true } },
|
||||
}
|
||||
end)
|
||||
Loading…
Add table
Add a link
Reference in a new issue