summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--home.nix44
-rw-r--r--neovim/README.md2
-rw-r--r--neovim/autoload/buffers.vim98
-rw-r--r--neovim/autoload/reloadpackage.vim3
-rw-r--r--neovim/init.lua293
-rw-r--r--neovim/lua/vimrc.lua211
-rw-r--r--neovim/lua/vimrc/ansible.lua195
-rw-r--r--neovim/lua/vimrc/azure.lua39
-rw-r--r--neovim/lua/vimrc/buffers.lua35
-rw-r--r--neovim/lua/vimrc/dap.lua266
-rw-r--r--neovim/lua/vimrc/helm.lua129
-rw-r--r--neovim/lua/vimrc/lsp.lua322
-rw-r--r--neovim/lua/vimrc/snippets.lua133
-rw-r--r--neovim/lua/vimrc/term.lua91
-rw-r--r--neovim/lua/vimrc/terragrunt.lua141
-rw-r--r--neovim/lua/vimrc/utils.lua70
16 files changed, 2051 insertions, 21 deletions
diff --git a/home.nix b/home.nix
index 9513602..f7905f7 100644
--- a/home.nix
+++ b/home.nix
@@ -66,40 +66,42 @@
programs.neovim = {
enable = true;
- package = unstable.neovim-unwrapped;
+ package = pkgs.neovim-unwrapped;
viAlias = true;
vimAlias = true;
extraConfig = "
- luafile ~/.config/nvim/user.lua
+ luafile neovim/init.lua
";
plugins = with pkgs.vimPlugins;
let
- fetchPluginFromGit = name: ref: pkgs.vimUtils.buildVimPluginFrom2Nix {
+ fetchPluginFromGit = name: rev: pkgs.vimUtils.buildVimPluginFrom2Nix {
name = name;
src = builtins.fetchGit {
url = "https://github.com/${name}";
submodules = true;
- inherit ref;
+ inherit rev;
};
};
in [
- (fetchPluginFromGit "LnL7/vim-nix" "HEAD")
- (fetchPluginFromGit "tpope/vim-fugitive" "HEAD")
- (fetchPluginFromGit "sainnhe/gruvbox-material" "HEAD")
- (fetchPluginFromGit "nvim-treesitter/nvim-treesitter" "HEAD")
- (fetchPluginFromGit "mvinkio/tnychain" "HEAD")
- (fetchPluginFromGit "L3MON4D3/LuaSnip" "HEAD")
- (fetchPluginFromGit "Furkanzmc/firvish.nvim" "HEAD")
- (fetchPluginFromGit "folke/trouble.nvim" "HEAD")
- (fetchPluginFromGit "klen/nvim-test" "HEAD")
- (fetchPluginFromGit "neovim/nvim-lspconfig" "HEAD")
- (fetchPluginFromGit "mfussenegger/nvim-dap" "HEAD")
- (fetchPluginFromGit "rcarriga/nvim-dap-ui" "HEAD")
- (fetchPluginFromGit "tjdevries/nlua.nvim" "HEAD")
- (fetchPluginFromGit "jose-elias-alvarez/null-ls.nvim" "HEAD")
- (fetchPluginFromGit "nvim-lua/plenary.nvim" "HEAD")
- (fetchPluginFromGit "ray-x/lsp_signature.nvim" "HEAD")
- (fetchPluginFromGit "justinmk/vim-dirvish" "HEAD")
+ vim-nix
+ vim-dirvish
+ nvim-dap
+ nvim-dap-ui
+ vim-fugitive
+ gruvbox-material
+ luasnip
+ nvim-lspconfig
+ trouble-nvim
+ nlua-nvim
+ null-ls-nvim
+ plenary-nvim
+
+ (nvim-treesitter.withPlugins (_: pkgs.tree-sitter.allGrammars))
+
+ (fetchPluginFromGit "klen/nvim-test" "32f162c27045fc712664b9ddbd33d3c550cb2bfc")
+ (fetchPluginFromGit "mvinkio/tnychain" "cef72f688e67f40616db8ecf9d7c63e505c2dd23")
+ (fetchPluginFromGit "Furkanzmc/firvish.nvim" "127f9146175d6bbaff6a8b761081cfd2279f8351")
+ (fetchPluginFromGit "ray-x/lsp_signature.nvim" "137bfaa7c112cb058f8e999a8fb49363fae3a697")
];
};
}
diff --git a/neovim/README.md b/neovim/README.md
new file mode 100644
index 0000000..3a441bd
--- /dev/null
+++ b/neovim/README.md
@@ -0,0 +1,2 @@
+# mike_neovim
+installs my neovim config on mac or linux
diff --git a/neovim/autoload/buffers.vim b/neovim/autoload/buffers.vim
new file mode 100644
index 0000000..3fc7818
--- /dev/null
+++ b/neovim/autoload/buffers.vim
@@ -0,0 +1,98 @@
+" all taken from: https://github.com/Furkanzmc/dotfiles/tree/master/vim/autoload/buffers.vim
+" Buffer related code from https://stackoverflow.com/a/4867969
+function! s:get_buflist()
+ return filter(range(1, bufnr('$')), 'buflisted(v:val)')
+endfunction
+
+function! s:matching_buffers(pattern)
+ return filter(s:get_buflist(), 'bufname(v:val) =~ a:pattern')
+endfunction
+
+function! s:cmd_line(mode, str)
+ call feedkeys(a:mode . a:str)
+endfunction
+
+function! buffers#wipe_matching(pattern, bang)
+ if a:pattern == "*"
+ let l:matchList = s:get_buflist()
+ else
+ let l:matchList = s:matching_buffers(a:pattern)
+ endif
+
+ let l:count = len(l:matchList)
+ if l:count < 1
+ echohl WarningMsg
+ echo '[vimrc] No buffers found matching pattern "' . a:pattern . '"'
+ echohl Normal
+ return
+ endif
+
+ if l:count == 1
+ let l:suffix = ''
+ else
+ let l:suffix = 's'
+ endif
+
+ if a:bang == "!"
+ exec 'bw! ' . join(l:matchList, ' ')
+ else
+ exec 'bw ' . join(l:matchList, ' ')
+ endif
+
+ echohl IncSearch
+ echo '[vimrc] Wiped ' . l:count . ' buffer' . l:suffix . '.'
+ echohl Normal
+endfunction
+
+function! buffers#wipe_nonexisting_files()
+ let l:matchList = filter(s:get_buflist(),
+ \ '!file_readable(bufname(v:val)) && !has_key(getbufinfo(v:val)[0].variables, "terminal_job_id") && getbufvar(v:val, "&buftype") != "nofile" && getbufvar(v:val, "&filetype") != "qf"')
+
+ let l:count = len(l:matchList)
+ if l:count < 1
+ return
+ endif
+
+ if l:count == 1
+ let l:suffix = ''
+ else
+ let l:suffix = 's'
+ endif
+
+ exec 'bw! ' . join(l:matchList, ' ')
+
+ echohl IncSearch
+ echo '[vimrc] Wiped ' . l:count . ' buffer' . l:suffix . '.'
+ echohl Normal
+endfunction
+
+" Code taken from here: https://stackoverflow.com/a/6271254
+function! buffers#get_visual_selection()
+ let [l:line_start, l:column_start] = getpos("'<")[1:2]
+ let [l:line_end, l:column_end] = getpos("'>")[1:2]
+ let l:lines = getline(l:line_start, l:line_end)
+ if len(l:lines) == 0
+ return l:lines
+ endif
+
+ let l:lines[-1] = l:lines[-1][: l:column_end - (&selection == 'inclusive' ? 1 : 2)]
+ let l:lines[0] = l:lines[0][l:column_start - 1:]
+ return l:lines
+endfunction
+
+function! buffers#visual_selection(direction, extra_filter) range
+ let l:saved_reg = @"
+ execute "normal! vgvy"
+
+ let l:pattern = escape(@", "\\/.*'$^~[]")
+ let l:pattern = substitute(l:pattern, "\n$", "", "")
+
+ if a:direction == 'search'
+ call s:cmd_line('/', l:pattern)
+ elseif a:direction == 'replace'
+ call s:cmd_line(':', "%s" . '/'. l:pattern . '/')
+ endif
+
+ let @/ = l:pattern
+ let @" = l:saved_reg
+endfunction
diff --git a/neovim/autoload/reloadpackage.vim b/neovim/autoload/reloadpackage.vim
new file mode 100644
index 0000000..f8a76a2
--- /dev/null
+++ b/neovim/autoload/reloadpackage.vim
@@ -0,0 +1,3 @@
+function reloadpackage#complete(arg_lead, cmd_line, cursor_pos)
+ return luaeval("require('vimrc').reload_package_complete('" . a:arg_lead . "', '" . a:cmd_line . "', '" . a:cursor_pos . "')")
+endfunction
diff --git a/neovim/init.lua b/neovim/init.lua
new file mode 100644
index 0000000..542348c
--- /dev/null
+++ b/neovim/init.lua
@@ -0,0 +1,293 @@
+-- general options {{{
+vim.opt.clipboard = {}
+vim.api.nvim_set_keymap(
+ "n",
+ "s",
+ "<Plug>Ysurround",
+ {silent=true,noremap=true}
+)
+
+-- don't load standard plugins
+
+vim.g.loaded_2html_plugin = false
+vim.g.loaded_fzf = false
+vim.g.loaded_man = false
+vim.g.loaded_gzip = false
+vim.g.loaded_health = false
+vim.g.loaded_matchit = false
+vim.g.loaded_matchparen = false
+vim.g.loaded_netrwPlugin = false
+vim.g.loaded_rplugin = false
+vim.g.loaded_shada = false
+vim.g.loaded_spellfile = false
+vim.g.loaded_tarPlugin = false
+vim.g.loaded_tohtml = false
+vim.g.loaded_tutor = false
+vim.g.loaded_zipPlugin = false
+
+vim.cmd [[filetype plugin on]]
+vim.cmd [[filetype indent on]]
+vim.cmd [[colorscheme gruvbox-material]]
+vim.cmd [[highlight WinSeparator guibg=None]]
+vim.opt.laststatus = 3
+vim.opt.winbar = "%=%m %f"
+
+
+vim.g.dirvish_mode = ':sort | sort ,^.*[^/]$, r'
+
+vim.opt.foldopen = "block,hor,jump,mark,percent,quickfix,search,tag"
+vim.opt.complete = ".,w,k,kspell,b"
+vim.opt.completeopt = "menuone,noselect"
+
+
+vim.opt.secure = true
+vim.opt.exrc = true
+
+-- relativenumbers and absolute number
+vim.opt.relativenumber = true
+vim.opt.number = true
+
+-- don't show previous search pattern
+vim.opt.hlsearch = false
+
+-- don't free buffer memory when abandoned
+vim.opt.hidden = true
+
+-- 1 tab == 4 spaces
+vim.opt.tabstop = 4
+vim.opt.softtabstop = 4
+vim.opt.shiftwidth = 4
+
+-- use spaces instead of tabs
+vim.opt.expandtab = true
+
+vim.opt.smartindent = true
+vim.opt.autoindent = true
+
+-- show special characters as listed
+vim.opt.list = true
+vim.opt.listchars = { tab = ' ', eol = "﬋" }
+vim.opt.showbreak = '﬋'
+
+-- make pasting better but makes insert mappings stop working...
+-- vim.opt.paste = true
+
+-- magic vim patterns
+vim.opt.magic = true
+
+-- make splitting consistent
+vim.opt.splitbelow = true
+
+vim.opt.wrap = false
+vim.opt.swapfile = false
+vim.opt.backup = false
+vim.opt.undodir = os.getenv('HOME') .. "/.local/share/nvim/undo"
+vim.opt.undofile = true
+vim.opt.incsearch = true
+vim.opt.termguicolors = true
+vim.opt.scrolloff = 8
+vim.opt.smd = false
+vim.opt.signcolumn = "yes"
+vim.opt.inccommand = "split"
+vim.opt.wmw = 10
+vim.opt.isfname:append("@-@")
+vim.opt.diffopt:append("vertical")
+vim.opt.shortmess:append("c")
+vim.opt.shell = "/bin/zsh"
+-- }}}
+
+-- load global and utility functions
+
+vim.g.mapleader = " "
+vim.g.maplocalleader = " "
+local vimrc = require('vimrc')
+-- save session file in cwd
+vimrc.cwd_save_session()
+
+-- tree-sitter {{{
+require('vimrc').setup_treesitter()
+-- }}}
+
+-- {{{ git
+vim.cmd([[command! -bang Gap :G commit -am 'fixmeuplater' | G push]])
+-- }}}
+
+-- completion {{{
+vim.api.nvim_set_keymap('n', ']p', ':tabn<cr>', { silent = true, noremap = true})
+vim.api.nvim_set_keymap('n', '[p', ':tabp<cr>', { silent = true, noremap = true})
+
+require'tnychain'.setup(
+ {
+ complete_key = '<c-n>',
+ repeat_chain = '<c-p>'
+ }
+)
+
+-- require'vimrc.snippets'.setup()
+
+-- }}}
+
+-- buffers {{{
+vim.opt.switchbuf = "useopen,usetab"
+vim.opt.stal = 2
+
+vim.api.nvim_set_keymap(
+ "n",
+ "<leader>;",
+ "<C-^>",
+ { silent = true, noremap = true }
+)
+
+-- taken from: https://github.com/Furkanzmc/dotfiles/blob/master/vim/init.lua
+-- searching and replacing in buffers
+vim.api.nvim_set_keymap(
+ "v",
+ "<leader>s",
+ ":call buffers#visual_selection('search', '')<CR>",
+ { silent = true, noremap = true }
+)
+vim.api.nvim_set_keymap(
+ "v",
+ "<leader>r",
+ ":call buffers#visual_selection('replace', '')<CR>",
+ { silent = true, noremap = true }
+)
+vim.cmd([[command -nargs=1 -complete=file E execute('silent! !mkdir -p "$(dirname "<args>")"') <Bar> e <args>]])
+-- wiping buffers and wiping nofile-buffers
+vim.cmd([[command! -nargs=1 -bang Bdeletes :call buffers#wipe_matching('<args>', <q-bang>)]])
+vim.cmd([[command! Bdnonexisting :call buffers#wipe_nonexisting_files()]])
+
+vim.cmd([[command! CleanTrailingWhiteSpace :lua require"vimrc.buffers".clean_trailing_spaces()]])
+
+vim.cmd([[augroup vimrc_plugin_buffers]])
+vim.cmd([[au!]])
+vim.cmd(
+ [[autocmd BufWritePre *.md,*.hcl,*.tf,*.py,*.cpp,*.qml,*.js,*.txt,*.json,*.html,*.lua,*.yaml,*.yml,*.bash,*.sh,*.go :lua require"vimrc.buffers".clean_trailing_spaces()]]
+)
+vim.cmd(
+ [[autocmd BufReadPost * lua require"vimrc.buffers".setup_white_space_highlight(vim.fn.bufnr())]]
+)
+vim.cmd(
+ [[autocmd BufReadPre *.tf,*.hcl packadd vim-terraform]]
+)
+vim.cmd([[augroup END]])
+
+-- }}}
+
+-- quickfix {{{
+vim.api.nvim_set_keymap(
+ "n",
+ "<C-q>o",
+ ":copen<cr>",
+ { silent = true, noremap = true }
+)
+vim.api.nvim_set_keymap(
+ "n",
+ "<C-q>z",
+ ":cclose<cr>",
+ { silent = true, noremap = true }
+)
+
+vim.api.nvim_set_keymap(
+ "n",
+ "<C-q>lo",
+ ":lopen<cr>",
+ { silent = true, noremap = true }
+)
+vim.api.nvim_set_keymap(
+ "n",
+ "<C-q>lz",
+ ":lclose<cr>",
+ { silent = true, noremap = true }
+)
+vim.api.nvim_set_keymap("n", "]q", ":cnext<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "[q", ":cprevious<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "]Q", ":cfirst<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "[Q", ":clast<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "]l", ":lnext<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "[l", ":lprevious<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "]L", ":lfirst<cr>", { silent = true, noremap = true })
+vim.api.nvim_set_keymap("n", "[L", ":llast<cr>", { silent = true, noremap = true })
+-- }}}
+
+-- firvish {{{
+require'firvish'.setup()
+vim.g.firvish_use_default_mappings=1
+require'vimrc'.setup_jq_function()
+require'vimrc'.setup_build_function()
+-- }}}
+
+-- {{{ trouble
+require"trouble".setup { }
+-- }}}
+
+-- {{{ tests
+require"nvim-test".setup{}
+vim.api.nvim_set_keymap(
+ "n",
+ "<leader>t",
+ "<cmd>TestFile<CR>",
+ {silent = true, noremap = true}
+)
+-- }}}
+
+-- lsp {{{
+vim.cmd [[augroup vimrc_nvim_lsp_setup]]
+vim.cmd [[au!]]
+vim.cmd [[au VimEnter * lua require'vimrc.lsp'.setup()]]
+vim.cmd [[augroup END]]
+-- }}}
+
+-- dap {{{
+vim.cmd [[augroup vimrc_nvim_dap_setup]]
+vim.cmd [[au!]]
+vim.cmd [[au VimEnter * lua require('vimrc.dap').setup_dap()]]
+vim.cmd [[augroup END]]
+-- }}}
+
+-- terminal {{{
+-- open close terminal
+vim.cmd [[command! Term :lua require('vimrc.term').toggle()]]
+-- run current file
+vim.cmd [[command! Run :lua require('vimrc.term').run()]]
+-- send selection
+vim.cmd [[command! -range Send :lua require('vimrc.term').sendSelection()]]
+vim.api.nvim_set_keymap(
+ "t",
+ "<c-q><c-w>",
+ "<c-\\><c-n>",
+ { silent = true, noremap = true }
+)
+-- }}}
+
+-- statusline {{{
+-- require('el').setup {}
+-- TODO: move to colortemplates
+vim.cmd [[hi! link Winbar StatusLine]]
+-- }}}
+
+-- init autocommand {{{
+vim.cmd([[augroup vimrc_init]])
+vim.cmd([[autocmd!]])
+vim.cmd(
+ [[autocmd BufReadPre,FileReadPre *.rest :if !exists("g:vimrc_rest_nvim_loaded") | packadd vim-rest-console | let g:vimrc_rest_nvim_loaded = v:true | endif | :e]]
+)
+vim.cmd(
+ [[autocmd TextYankPost * silent! lua vim.highlight.on_yank{on_visual=false, higroup="IncSearch", timeout=100}]]
+)
+vim.cmd(
+ [[ autocmd TextYankPost * if v:event.operator is 'y' && v:event.regname is '' | execute 'OSCYankReg "' | endif ]]
+)
+vim.cmd(
+ [[autocmd VimEnter * if filereadable(".exrc.lua") | call execute("luafile .exrc.lua") | endif]]
+)
+vim.cmd(
+ [[autocmd VimEnter * if filereadable(".env") | echo execute("Dotenv") | call execute("Dotenv .env") | endif]]
+)
+-- temp fix for screen redrawing issues
+-- cmd(
+-- [[autocmd BufEnter * mod]]
+-- )
+vim.cmd([[augroup END]])
+-- }}}
+-- vim: fdm=marker
diff --git a/neovim/lua/vimrc.lua b/neovim/lua/vimrc.lua
new file mode 100644
index 0000000..fc40e80
--- /dev/null
+++ b/neovim/lua/vimrc.lua
@@ -0,0 +1,211 @@
+-- vim: fdm=marker
+local log_warning = require 'vimrc.utils'.log_warning
+local M = {}
+
+M.cwd_save_session = function()
+ vim.cmd([[
+augroup vimrc_save_session
+ au!
+ au VimLeave * mksession! ]] .. os.getenv("PWD") .. [[/Session.vim
+augroup end
+ ]])
+end
+
+function M.setup_treesitter()
+ if vim.o.loadplugins == false then
+ return
+ end
+
+ assert(vim.fn.exists(":TSInstall") == 0, "TreeSitter is already configured.")
+
+ -- vim.cmd([[packadd nvim-treesitter]])
+ require 'nvim-treesitter.configs'.setup {
+ highlight = {
+ enable = true,
+ -- Setting this to true will run `:h syntax` and tree-sitter at the same time.
+ -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
+ -- Using this option may slow down your editor, and you may see some duplicate highlights.
+ -- Instead of true it can also be a list of languages
+ },
+ }
+end
+
+function M.setup_rest_nvim()
+ require("rest-nvim").setup({
+ result_split_horizontal = true,
+ skip_ssl_verification = false,
+ highlight = { enabled = true, timeout = 150 },
+ jump_to_request = false,
+ })
+
+ local map = vim.api.nvim_set_keymap
+ map(
+ "n",
+ "<leader>tt",
+ "<Plug>RestNvim",
+ { silent = true }
+ )
+ map(
+ "n",
+ "<leader>tp",
+ "<Plug>RestNvimPreview",
+ { silent = true }
+ )
+end
+
+local jq_functions = {}
+function M.setup_jq_function()
+ jq_functions.filter_jq = function(path, first, last)
+ P(path)
+ local buf = vim.api.nvim_get_current_buf()
+ first, last = tonumber(first), tonumber(last)
+ first = first - 1
+ local json_string = table.concat(vim.api.nvim_buf_get_lines(buf, first, last, true))
+ local tmp = os.tmpname()
+ local f = io.open(tmp, "w")
+ if f then
+ f:write(json_string)
+ f:close()
+ else
+ return false
+ end
+ local cmd = {
+ "/bin/sh",
+ "-c",
+ [[cat ]] .. tmp .. [[ | jq ']] .. path .. [[']]
+ }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "json",
+ title = "jq",
+ listed = true,
+ output_qf = false,
+ is_background_job = false,
+ })
+ end
+ _G.jq_functions = jq_functions
+ vim.cmd [[command! -nargs=* -range FJq :lua _G.jq_functions.filter_jq('<args>', "<line1>", "<line2>")]]
+end
+
+local build_functions = {}
+function M.setup_build_function()
+ build_functions.rebuild = function()
+ local cmd = { "rebuild" }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "job-output",
+ title = "rebuild",
+ listed = true,
+ output_qf = true,
+ is_background_job = true,
+ })
+ end
+ _G.build_functions = build_functions
+ vim.cmd [[command! Rebuild :lua _G.build_functions.rebuild()]]
+
+ build_functions.azure_yaml = function(file)
+ local cwd = os.getenv('PWD')
+ local auth = os.getenv('AZURE_DEVOPS_AUTH')
+ local ado_org = os.getenv('AZURE_DEVOPS_ORG')
+ local ado_proj = os.getenv('AZURE_DEVOPS_PROJECT')
+ local pipeline = os.getenv('AZURE_YAML_DEFINITION_ID')
+ local branch = os.getenv('AZURE_YAML_BRANCH')
+
+ local url = [[https://dev.azure.com/]] .. ado_org .. [[/]] .. ado_proj .. [[/_apis/pipelines/]] .. pipeline .. [[/preview?api-version=7.1-preview.1]]
+ local auth_header = [[Authorization: Basic ]] .. auth
+ local yaml_file = cwd .. "/" .. file
+
+ local shell_script = string.format(
+[[yaml_string="
+$(cat ${PWD}/${AZURE_YAML_DEBUG_SNIPPET})
+$(cat %s)
+"
+yaml_string="$(echo "$yaml_string" | yq '... style="single"' - | sed -re /\s*\#.*$/d)"
+curl -s -X POST '%s' -H 'Content-Type: application/json' -H '%s' --data-binary @- << THEEND | jq '.finalYaml // .' | yq '... style="double"' -
+{
+ "previewRun":true,
+ "templateParameters": {},
+ "resources": {
+ "repositories": {
+ "self": {
+ "refName": "refs/heads/%s"
+ }
+ }
+ },
+ "yamlOverride":"$yaml_string"
+}
+THEEND]],
+ yaml_file, url, auth_header, branch, branch
+ )
+ local cmd = {
+ "/bin/bash", "-c", shell_script
+ }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "yaml",
+ title = "azureyaml",
+ use_last_buffer = false,
+ listed = true,
+ output_qf = true,
+ is_background_job = false,
+ cwd = cwd
+ })
+ end
+ vim.cmd [[command! -nargs=1 -complete=file AzureYaml :lua _G.build_functions.azure_yaml("<args>")]]
+end
+
+-- {{{ global lua debugging
+_G.P = function(arg)
+ print(vim.inspect(arg))
+end
+
+M.reload_package_complete = function(_, _, _)
+ -- arg_lead, cmd_line, cursor_pos
+ -- TODO: implement completion based on loaded packages
+ return 'hi'
+end
+
+M.reload_package = function(name)
+ P([[name=]] .. name)
+ if package.loaded[name] == nil then
+ log_warning("Package not loaded.", "[vimrc]")
+ return
+ end
+ package.loaded[name] = nil
+ return require(name)
+end
+
+vim.cmd(
+ [[command! -complete=custom,reloadpackage#complete -nargs=1 LuaReload :lua require('vimrc').reload_package(<q-args>)]]
+)
+
+M.activate_reload_on_write = function(name)
+ vim.cmd([[augroup package_reload_]] .. name)
+ vim.cmd([[au!]])
+ vim.cmd([[au BufWritePost <buffer> ]]
+ .. string.format([[:lua require('vimrc').reload_package('%s')]], name))
+ vim.cmd([[augroup END]])
+end
+
+vim.cmd(
+ [[command! -complete=custom,reloadpackage#complete -nargs=1 ActivateReload :lua require('vimrc').activate_reload_on_write(<q-args>)]]
+)
+
+-- }}}
+-- {{{ syntax tools
+M.print_synstack_at = function(line, col)
+ local syn = vim.fn.synstack(line, col)
+ for _, id in pairs(syn) do
+ P(vim.fn.synIDattr(id, 'name'))
+ end
+end
+
+vim.cmd(
+ string.format("command! SyntaxInspect :lua require('vimrc').print_synstack_at(%s, %s)",
+ vim.fn.line("."),
+ vim.fn.col(".")
+ )
+)
+-- }}}
+
+return M
diff --git a/neovim/lua/vimrc/ansible.lua b/neovim/lua/vimrc/ansible.lua
new file mode 100644
index 0000000..4dfeee9
--- /dev/null
+++ b/neovim/lua/vimrc/ansible.lua
@@ -0,0 +1,195 @@
+local vim = vim
+local cmd = vim.cmd
+local fn = vim.fn
+
+local M = {}
+function M.setup(opts)
+ P(opts)
+ local home = os.getenv('HOME')
+ local user = os.getenv('USER')
+ assert(opts.cwd, "cwd is required. ")
+ assert(opts.inventory, "inventory is required. ")
+ -- This needs to be built on the machine you use it due to requiring the user to be present in the /etc/passwd table.
+ local functions = {}
+ local secrets_exist = fn.filereadable(fn.expand(opts.vault_file)) == 1
+ local vars_files = ""
+ if secrets_exist then
+ vars_files = vars_files .. [[
+ vars_files:
+ - ]] .. opts.vault_file .. "\n"
+ end
+ functions.ansible_dump = function(output_qf)
+ local cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--network=host",
+ "--user=" .. user,
+ "-e",
+ "HOME=" .. home,
+ "--volume=" .. home .. ":" .. home,
+ "--workdir=" .. opts.cwd,
+ "mvinkio/ansible",
+ "/bin/bash",
+ "-c",
+[[cat <<EOF > dev-plabook.yaml
+---
+- name: dump all
+ hosts: all
+]]
+..
+vars_files
+..
+[[
+ tasks:
+ - name: Print vars
+ debug:
+ var: vars
+ - name: Print environment
+ debug:
+ var: environment
+ - name: Print group_names
+ debug:
+ var: group_names
+ - name: Print groups
+ debug:
+ var: groups
+ - name: Print hostvars
+/ debug:
+ var: hostvars
+EOF]] ..
+"\n ansible-playbook -vvv -i " .. opts.inventory .. " dev-plabook.yaml "
+ }
+ P(cmd)
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "dump",
+ listed = true,
+ output_qf = false,
+ is_background_job = false,
+ })
+ end
+
+ functions.ansible_run = function (playbook, debug)
+ P(playbook)
+ P(debug)
+ local set_debug = ""
+ if debug then
+ set_debug = "ANSIBLE_ENABLE_TASK_DEBUGGER=True"
+ end
+ local run_cmd = set_debug ..
+ [[ ansible-playbook ]] ..
+ [[-v ]] ..
+ [[-i ]] .. opts.inventory .. [[ ]] ..
+ playbook
+ local job_cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--network=host",
+ "--user=" .. user,
+ "-e", "HOME=" .. home,
+ "-e", "USER=" .. user,
+ "--volume=" .. home .. ":" .. home,
+ "--workdir=" .. opts.cwd,
+ "mvinkio/ansible",
+ "/bin/bash",
+ "-c",
+ run_cmd
+ }
+ local interactive_cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--tty",
+ "--rm",
+ "--network=host",
+ "--user=" .. user,
+ "-e",
+ "HOME=" .. home,
+ "-e",
+ "USER=" .. user,
+ "--volume=" .. home .. ":" .. home,
+ "--workdir=" .. opts.cwd,
+ "mvinkio/ansible",
+ "/bin/bash",
+ "-c"
+ }
+ if not debug then
+ P(job_cmd)
+ require('firvish.job_control').start_job({
+ cmd = job_cmd,
+ filetype = "log",
+ title = "ansiblejob",
+ listed = true,
+ output_qf = false,
+ is_background_job = false,
+ })
+ else
+ local term_cmd = [[sp | term /bin/bash -c ']] .. table.concat(interactive_cmd, ' ') .. [[ "]] .. run_cmd .. [["']]
+ P(term_cmd)
+ vim.cmd(term_cmd)
+ end
+ end
+
+ functions.install_requirements = function (bang)
+ local cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--network=host",
+ "--user=" .. user,
+ "-e", "HOME=" .. home,
+ "--volume=" .. home .. ":" .. home,
+ "--workdir=" .. opts.cwd,
+ "mvinkio/ansible",
+ "/bin/bash",
+ "-c",
+ "ansible-galaxy install -r " .. opts.ansible_galaxy_requirements
+ }
+ P(cmd)
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "ansiblejob",
+ listed = true,
+ output_qf = false,
+ is_background_job = false,
+ })
+ end
+
+ functions.ansible_session = function (bang)
+ local interactive_cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--tty",
+ "--rm",
+ "--network=host",
+ "--user=" .. user,
+ "-e",
+ "USER=" .. user,
+ "-e",
+ "HOME=" .. home,
+ "--volume=" .. home .. ":" .. home,
+ "--workdir=" .. opts.cwd,
+ "mvinkio/ansible",
+ "/bin/bash"
+ }
+ local term_cmd = [[sp | term ]] .. table.concat(interactive_cmd, ' ')
+ P(term_cmd)
+ vim.cmd(term_cmd)
+ end
+
+ _G.ansible_functions = functions
+ cmd [[command! -bang AnsibleBug :lua _G.ansible_functions.ansible_dump("<bang>" ~= "!")]]
+ cmd [[command! -complete=file -nargs=1 -bang AnsiblePlaybook :lua _G.ansible_functions.ansible_run("<args>", "<bang>" == "!")]]
+ cmd [[command! -bang AnsibleGalaxyRequirements :lua _G.ansible_functions.install_requirements("<bang>" ~= "!")]]
+ cmd [[command! -bang AnsibleSession :lua _G.ansible_functions.ansible_session("<bang>" ~= "!")]]
+
+end
+return M
diff --git a/neovim/lua/vimrc/azure.lua b/neovim/lua/vimrc/azure.lua
new file mode 100644
index 0000000..9b6b245
--- /dev/null
+++ b/neovim/lua/vimrc/azure.lua
@@ -0,0 +1,39 @@
+local M = {}
+
+function M.setup(opts)
+ -- assert(opts.org_url, "org url is required. ")
+ -- assert(opts.pipeline_id, "pipeline id is required. ")
+ -- assert(opts.api_version, "api version is required. ")
+ -- assert(opts.basic_auth_token, "basic auth token is required. ")
+
+ -- local functions = {}
+ -- functions.pipeline_bug = function(output_qf)
+ -- local cmd = {
+ -- "/bin/sh",
+ -- "-c",
+ -- [[curl -sSL --compressed -X POST -H ]] ..
+ -- [['Authorization: Basic ]] .. opts.basic_auth_token .. [[' ]] ..
+ -- [[-H 'Content-Type: application/json' ]] ..
+ -- [[--data-raw "{ \"previewRun\": true }" ]] ..
+ -- opts.org_url .. "/_apis/pipelines/" .. opts.pipeline_id .. "/preview?api-version=" .. opts.api_version ..
+ -- " | jq .finalYaml | xargs -0 printf %b | sed -e 's@^\"@@' -e 's@\"$@@'"
+ -- }
+ -- P(cmd)
+ -- require('firvish.job_control').start_job({
+ -- cmd = cmd,
+ -- filetype = "log",
+ -- title = "pipeline",
+ -- listed = true,
+ -- output_qf = output_qf,
+ -- is_background_job = false,
+ -- cwd = opts.chart_dir,
+ -- })
+ -- end
+
+ -- _G.azure_functions = functions
+
+ -- cmd [[command! -bang PipelineBug :lua _G.azure_functions.pipeline_bug("<bang>" ~= "!")]]
+
+end
+
+return M
diff --git a/neovim/lua/vimrc/buffers.lua b/neovim/lua/vimrc/buffers.lua
new file mode 100644
index 0000000..f185b6f
--- /dev/null
+++ b/neovim/lua/vimrc/buffers.lua
@@ -0,0 +1,35 @@
+local M = {}
+
+function M.clean_trailing_spaces()
+
+ local save_cursor = vim.fn.getpos(".")
+ local old_query = vim.fn.getreg("/")
+ vim.cmd [[%s/\s\+$//e]]
+
+ vim.fn.setpos(".", save_cursor)
+ vim.fn.setreg("/", old_query)
+end
+
+function M.setup_white_space_highlight(bufnr)
+ if vim.b.vimrc_trailing_white_space_highlight_enabled then
+ return
+ end
+
+ -- if options.get_option_value("trailingwhitespacehighlight", bufnr) == false then
+ -- return
+ -- end
+
+ vim.cmd([[highlight link TrailingWhiteSpace Error]])
+ vim.cmd([[highlight NonText ctermfg=7 guifg=gray]])
+
+ vim.cmd("augroup vimrc_trailing_white_space_highlight_buffer_" .. bufnr)
+ vim.cmd([[autocmd! * <buffer>]])
+ vim.cmd([[autocmd BufReadPost <buffer> match TrailingWhiteSpace /\s\+$/]])
+ vim.cmd([[autocmd InsertEnter <buffer> match TrailingWhiteSpace /\s\+\%#\@<!$/]])
+ vim.cmd([[autocmd InsertLeave <buffer> match TrailingWhiteSpace /\s\+$/]])
+ vim.cmd([[augroup END]])
+
+ vim.b.vimrc_trailing_white_space_highlight_enabled = true
+end
+
+return M
diff --git a/neovim/lua/vimrc/dap.lua b/neovim/lua/vimrc/dap.lua
new file mode 100644
index 0000000..313e043
--- /dev/null
+++ b/neovim/lua/vimrc/dap.lua
@@ -0,0 +1,266 @@
+local vim = vim
+local cmd = vim.cmd
+local api = vim.api
+local M = {}
+
+local function register_debug_adapters(cwd)
+ local dap = require 'dap'
+ local python_args = {
+ "run",
+ "--rm",
+ "--volume=/var/run/docker.sock:/var/run/docker.sock",
+ "--interactive",
+ "--env-file=" .. cwd .. "/.env",
+ "--volume", cwd .. ":" .. cwd,
+ "--network=host",
+ "mvinkio/python",
+ "python", "-m", "debugpy.adapter"
+ }
+ dap.adapters.python = {
+ type = 'executable',
+ command = "docker",
+ args = python_args,
+ }
+
+ dap.adapters.go = function(callback, _)
+ -- _ config
+ local stdout = vim.loop.new_pipe(false)
+ local stderr = vim.loop.new_pipe(false)
+ local handle
+ local pid_or_err
+ local port = 38697
+
+ -- "docker",
+ -- "run",
+ -- "--rm",
+ -- "--interactive",
+ -- "-e=GOPROXY=https://proxy.golang.org",
+ -- "-e=GOOS=linux",
+ -- "-e=GOARCH=amd64",
+ -- "-e=GOPATH=" .. new_root_dir .. "/go",
+ -- "-e=GOCACHE=" .. new_root_dir .. "/.cache/go-build",
+ -- "--workdir=" .. new_root_dir,
+ -- "--volume=" .. new_root_dir .. ":" .. new_root_dir,
+ -- "--network=bridge",
+ -- "mvinkio/go",
+ -- "gopls"
+ local opts = {
+ stdio = { nil, stdout },
+ args = { "run",
+ "-i", "--rm",
+ "--security-opt=seccomp:unconfined",
+ "--volume=/var/run/docker.sock:/var/run/docker.sock",
+ "--env-file=" .. cwd .. "/.env",
+ "-e=GOPROXY=https://proxy.golang.org",
+ "-e=GOOS=linux",
+ "-e=GOARCH=amd64",
+ "-e=GOPATH=" .. cwd .. "/go",
+ "-e=GOCACHE=" .. cwd .. "/.cache/go-build",
+ "-v", cwd .. ":" .. cwd, -- TODO: use os.getenv here
+ "-w", cwd, -- TODO: use find root here
+ "--network", "host",
+ "mvinkio/go",
+ "dlv", "dap", "-l", "127.0.0.1:" .. port,
+ "--only-same-user=false",
+ "--log",
+ },
+ detached = false
+ }
+ handle, pid_or_err = vim.loop.spawn("docker", opts, function(code)
+ stdout:close()
+ stderr:close()
+ handle:close()
+ if code ~= 0 then
+ print('dlv exited with code', code)
+ end
+ end)
+ assert(handle, 'Error running dlv: ' .. tostring(pid_or_err))
+ stdout:read_start(function(err, chunk)
+ assert(not err, err)
+ if chunk then
+ P(chunk)
+ vim.schedule(function()
+ require('dap.repl').append(chunk)
+ end)
+ end
+ end)
+ stderr:read_start(function(err, chunk)
+ assert(not err, err)
+ if chunk then
+ P(chunk)
+ end
+ end)
+ -- Wait for delve to start
+ vim.defer_fn(
+ function()
+ callback({ type = "server", host = "127.0.0.1", port = port })
+ end,
+ 2000)
+ end
+
+end
+
+local function set_configurations()
+ local dap = require 'dap'
+ dap.configurations.python = {
+ {
+ -- The first three options are required by nvim-dap
+ type = 'python'; -- the type here established the link to the adapter definition: `dap.adapters.python`
+ request = 'launch';
+ name = "Launch file";
+
+ -- Options below are for debugpy, see https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings for supported options
+
+ program = "${file}"; -- This configuration will launch the current file if used.
+ -- TODO: use container for python interpreter
+ -- pythonPath = function()
+ -- -- debugpy supports launching an application with a different interpreter then the one used to launch debugpy itself.
+ -- -- The code below looks for a `venv` or `.venv` folder in the current directly and uses the python within.
+ -- -- You could adapt this - to for example use the `VIRTUAL_ENV` environment variable.
+ -- local cwd = vim.fn.getcwd()
+ -- if vim.fn.executable(cwd .. '/venv/bin/python') == 1 then
+ -- return cwd .. '/venv/bin/python'
+ -- elseif vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then
+ -- return cwd .. '/.venv/bin/python'
+ -- else
+ -- return '/usr/bin/python'
+ -- end
+ -- end;
+ },
+ }
+
+ -- docker run --rm -i -v /home/mike:/home/mike --network=host -w /home/mike/projects/example/hello mvinkio/godap dap -l 127.0.0.1:38697 --log --log-output="dap" --only-same-user=false
+
+ dap.configurations.go = {
+ {
+ type = "go",
+ name = "Debug",
+ request = "launch",
+ program = "${file}"
+ },
+ {
+ type = "go",
+ name = "Debug test", -- configuration for debugging test files
+ request = "launch",
+ mode = "test",
+ program = vim.fn.fnamemodify(vim.fn.expand('%'),':p:h')
+ },
+ -- works with go.mod packages and sub packages
+ {
+ type = "go",
+ name = "Debug test (go.mod)",
+ request = "launch",
+ mode = "test",
+ program = "./${relativeFileDirname}"
+ }
+ }
+ local set_go_keymaps = function()
+ vim.keymap.set(
+ "n",
+ "<leader>df",
+ [[<cmd>lua require'vimrc.dap'.continue(require'dap'.configurations.go[1])<CR>]],
+ { noremap = true }
+ )
+ vim.keymap.set(
+ "n",
+ "<leader>df",
+ [[<cmd>lua require'vimrc.dap'.continue(require'dap'.configurations.go[1])<CR>]],
+ { noremap = true }
+ )
+ end
+ local augroup = api.nvim_create_augroup("vimrc_go_dap_config", { clear = true })
+ api.nvim_create_autocmd("FileType", { pattern = "go", callback = set_go_keymaps, group = augroup })
+
+end
+
+local function set_keymaps()
+ local map = vim.api.nvim_set_keymap
+ -- taken from: https://github.com/Furkanzmc/dotfiles/blob/master/vim/lua/vimrc/dap.lua
+ -- version: 9561e7c700e0ffe74cf9fd61a0e4543ae14938c6
+ map("n", "<leader>dc", ":lua require'vimrc.dap'.continue()<CR>", { silent = true, noremap = true })
+ map("n", "<leader>dt", ":lua require'dap'.close()<CR>", { silent = true, noremap = true })
+ map("n", "<leader>ds", ":lua require'dap'.step_into()<CR>", { silent = true, noremap = true })
+
+ map("n", "<leader>dk", ":lua require('dapui').eval()<CR>", { silent = true, noremap = true })
+ map("v", "<leader>dk", ":lua require('dapui').eval()<CR>", { silent = true, noremap = true })
+ map("n", "<leader>do", ":lua require'dap'.step_out()<CR>", { silent = true, noremap = true })
+
+ map("n", "<leader>dn", ":lua require'dap'.step_over()<CR>", { silent = true, noremap = true })
+ map("n", "<leader>du", ":lua require'dap'.up()<CR>", { silent = true, noremap = true })
+ map("n", "<leader>dd", ":lua require'dap'.down()<CR>", { silent = true, noremap = true })
+
+ map(
+ "n",
+ "<leader>db",
+ ":lua require'dap'.toggle_breakpoint()<CR>",
+ { silent = true, noremap = true }
+ )
+ map(
+ "n",
+ "<leader>dbc",
+ ":lua require'dap'.set_breakpoint(vim.fn.input('Breakpoint condition: '))<CR>",
+ { silent = true, noremap = true }
+ )
+ map(
+ "n",
+ "<leader>dbl",
+ ":lua require'dap'.set_breakpoint(nil, nil, vim.fn.input('Log point message: '))<CR>",
+ { silent = true, noremap = true }
+ )
+
+ map("n", "<leader>dui", ":lua require'dapui'.toggle()<CR>", { silent = true, noremap = true })
+ map("n", "<leader>dr", ":lua require'dap'.run_to_cursor()<CR>", { silent = true, noremap = true })
+ map(
+ "n",
+ "<leader>dl",
+ ":lua require'dap'.list_breakpoints(true)<CR>",
+ { silent = true, noremap = true }
+ )
+ -- map(
+ -- "n",
+ -- "<leader>dp",
+ -- ":lua require'dap.ui.variables'.scopes()<CR>",
+ -- { silent = true, noremap = true }
+ -- )
+end
+
+local function set_commands()
+ cmd([[command! DapUIOpen :lua require'dapui'.open()]])
+ cmd([[command! DapUIClose :lua require'dapui'.close()]])
+end
+
+function M.continue(config)
+ local dap = require 'dap'
+ register_debug_adapters(vim.fn.getcwd())
+ set_configurations()
+ if config then
+ dap.run(config)
+ else
+ dap.continue()
+ end
+end
+
+function M.setup_dap()
+ if vim.o.loadplugins == false then
+ return
+ end
+
+ cmd([[packadd nvim-dap]])
+ cmd([[packadd nvim-dap-ui]])
+
+ local vim_startup_dir = vim.fn.getcwd()
+ register_debug_adapters(vim_startup_dir)
+ set_configurations()
+
+ cmd [[augroup vimrc_dap]]
+ cmd [[au!]]
+ cmd [[au FileType dap-repl lua require('dap.ext.autocompl').attach()]]
+ cmd [[augroup END]]
+
+ -- Commands and keymaps
+ require('dapui').setup()
+ set_keymaps()
+ set_commands()
+end
+
+return M
diff --git a/neovim/lua/vimrc/helm.lua b/neovim/lua/vimrc/helm.lua
new file mode 100644
index 0000000..5597759
--- /dev/null
+++ b/neovim/lua/vimrc/helm.lua
@@ -0,0 +1,129 @@
+local vim = vim
+local cmd = vim.cmd
+local fn = vim.fn
+
+local M = {}
+function M.setup(opts)
+ assert(opts.release_name, "release name is required. ")
+ assert(opts.chart_dir, "chartdir is required. ")
+ assert(opts.docker_dir, "docker_dir is required. ")
+ assert(opts.docker_tag, "docker_tag is required. ")
+
+ local values_files = opts.values_files or {}
+ local values_sets = opts.values_sets or {}
+ local cluster = opts.cluster or "so"
+
+ local dependency_update = ""
+ if opts.dependency_update then
+ dependency_update = "--dependency-update"
+ end
+
+ table.insert(values_sets, "envshort=" .. cluster)
+ table.insert(values_sets, "cluster=" .. cluster)
+ table.insert(values_sets, "clusterHost=" .. cluster .. ".stater.com")
+
+ local functions = {}
+ functions.helm_upgrade = function(output_qf)
+ local cmd = {
+ "helm",
+ "upgrade",
+ "--install",
+ "--values",
+ string.join(values_files, ","),
+ opts.release_name,
+ "."
+ }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "upgrade",
+ listed = true,
+ output_qf = output_qf,
+ is_background_job = true,
+ cwd = opts.chart_dir,
+ })
+ end
+
+ functions.helm_delete = function(output_qf)
+ require('firvish.job_control').start_job({
+ cmd = {
+ "helm",
+ "delete",
+ opts.release_name,
+ },
+ filetype = "log",
+ title = "delete",
+ listed = true,
+ output_qf = output_qf,
+ is_background_job = true,
+ cwd = opts.chart_dir,
+ })
+ end
+
+ functions.helm_build = function(output_qf)
+ local cmd = {
+ "/bin/sh",
+ "-c",
+ string.format(
+ [[if docker build -t %s %s && ]] ..
+ [[docker push %s ; then ]] ..
+ [[helm delete %s ; ]] ..
+ [[helm upgrade --install %s --values %s --set %s %s . ; fi]],
+ opts.docker_tag,
+ opts.docker_dir,
+ opts.docker_tag,
+ opts.release_name,
+ dependency_update,
+ string.join(values_files, ","),
+ string.join(values_sets, ","),
+ opts.release_name
+ )
+ }
+ P(cmd)
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "delete",
+ listed = true,
+ output_qf = output_qf,
+ is_background_job = true,
+ cwd = opts.chart_dir,
+ })
+ end
+
+ functions.helm_debug = function(update_remote)
+ local cmd = {
+ "helm",
+ "template",
+ "--debug",
+ "--values",
+ string.join(values_files, ","),
+ "--set",
+ string.join(values_sets, ","),
+ opts.release_name,
+ "."
+ }
+ P(cmd)
+ if update_remote then
+ table.insert(cmd, 3, "--dependency-update")
+ end
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "delete",
+ listed = true,
+ output_qf = false,
+ is_background_job = false,
+ cwd = opts.chart_dir,
+ })
+ end
+
+ _G.helm_functions = functions
+ cmd [[command! -bang HelmPut :lua _G.helm_functions.helm_upgrade("<bang>" ~= "!")]]
+ cmd [[command! -bang HelmDelete :lua _G.helm_functions.helm_delete("<bang>" ~= "!")]]
+
+ cmd [[command! -bang HelmBuild :lua _G.helm_functions.helm_build("<bang>" ~= "!")]]
+ cmd [[command! -bang HelmBug :lua _G.helm_functions.helm_debug("<bang>" ~= "!")]]
+
+end
+return M
diff --git a/neovim/lua/vimrc/lsp.lua b/neovim/lua/vimrc/lsp.lua
new file mode 100644
index 0000000..94a2cc2
--- /dev/null
+++ b/neovim/lua/vimrc/lsp.lua
@@ -0,0 +1,322 @@
+local utils = require 'vimrc.utils'
+local M = {}
+
+-- TODO: parameterise
+-- TODO: extend stuff later
+
+M.client_log = {}
+
+-- local functions {{{
+local function on_publish_diagnostics(_, result, ctx, config)
+ vim.lsp.diagnostic.on_publish_diagnostics(_, result, ctx, config)
+ vim.diagnostic.setloclist({
+ open = false
+ })
+end
+
+local function setup_handlers(client, _)
+ client.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(on_publish_diagnostics, {
+ virtual_text = false,
+ underline = true,
+ update_in_insert = false,
+ severity_sort = true
+ })
+end
+
+local function setup_server_capabilities_maps(client, bufnr)
+ local map = vim.api.nvim_buf_set_keymap
+ local opts = { silent = true, noremap = true }
+ local capabilities = client.server_capabilities
+
+ if capabilities.completion ~= false then
+ vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")
+ end
+
+ if capabilities.hover ~= false then
+ vim.api.nvim_buf_set_option(bufnr, "keywordprg", ":LspHover")
+ end
+
+ if capabilities.rename == true then
+ map(bufnr, "n", "<leader>gr", "<Cmd>lua vim.lsp.buf.rename()<CR>", opts)
+ end
+
+ if capabilities.signature_help == true then
+ map(bufnr, "n", "<leader>gs", "<Cmd>lua vim.lsp.buf.signature_help()<CR>", opts)
+ end
+
+ if capabilities.goto_definition ~= false then
+ map(bufnr, "n", "<leader>gd", "<Cmd>lua vim.lsp.buf.definition()<CR>", opts)
+ end
+
+ if capabilities.declaration == true then
+ map(bufnr, "n", "<leader>gD", "<Cmd>lua vim.lsp.buf.declaration()<CR>", opts)
+ end
+
+ if capabilities.implementation == true then
+ map(bufnr, "n", "<leader>gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts)
+ end
+
+ if capabilities.find_references ~= false then
+ map(bufnr, "n", "<leader>gg", "<cmd>lua vim.lsp.buf.references()<CR>", opts)
+ end
+
+ map(bufnr, "n", "<leader>ge", "<cmd>lua require'vimrc.lsp'.line_diagnostic()<CR>", opts)
+
+ if capabilities.document_symbol ~= false then
+ map(bufnr, "n", "<leader>gds", "<cmd>lua vim.lsp.buf.document_symbol()<CR>", opts)
+ end
+
+ if capabilities.workspace_symbol ~= true then
+ map(bufnr, "n", "<leader>gw", "<cmd>lua vim.lsp.buf.workspace_symbol()<CR>", opts)
+ end
+
+ if capabilities.code_action ~= false then
+ map(bufnr, "n", "<leader>ga", "<cmd>lua vim.lsp.buf.code_action()<CR>", opts)
+ end
+
+ if capabilities.documentFormattingProvider == true then
+ vim.api.nvim_buf_set_option(bufnr, "formatexpr", "v:lua.vim.lsp.format()")
+ map(bufnr, "n", "<leader>gq", "<cmd>lua vim.lsp.buf.format({ async = true })<CR>", opts)
+ end
+
+ if capabilities.document_range_formatting == true then
+ map(bufnr, "v", "<leader>gq", "<Esc><Cmd>lua vim.lsp.buf.range_formatting()<CR>", opts)
+ end
+
+ if capabilities.hover ~= false then
+ vim.api.nvim_command("command! -buffer -nargs=1 LspHover lua vim.lsp.buf.hover()<CR>")
+ end
+end
+
+-- }}}
+
+-- diagnostics {{{
+function M.line_diagnostic()
+ local line = vim.fn.line('.') - 1
+ local diags = vim.diagnostic.get(0, {
+ lnum = line
+ })
+ for _, diag in ipairs(diags) do
+ utils.log_warning(diag.message, diag.source)
+ end
+end
+
+-- }}}
+
+-- setup {{{
+function M.setup()
+
+ local buffer_setup = function(client)
+ table.insert(M.client_log, client)
+ local bufnr = vim.api.nvim_get_current_buf()
+
+ setup_server_capabilities_maps(client, bufnr)
+ setup_handlers(client, bufnr)
+ require("lsp_signature").on_attach({
+ bind = true,
+ floating_window = false,
+ toggle_key = "<C-g><C-s>",
+ extra_trigger_chars = { "{", "}" },
+ hint_prefix = "@ ",
+ check_pumvisible = false
+ }, bufnr)
+ end
+
+ local buffer_setup_no_format = function(client)
+ client.server_capabilities.document_formatting = false
+ buffer_setup(client)
+ end
+
+ -- lspconfig {{{
+ local lspconfig = require 'lspconfig'
+ -- check if docker is executable first?
+ local runtime_path = vim.split(package.path, ';')
+ table.insert(runtime_path, "lua/?.lua")
+ table.insert(runtime_path, "lua/?/init.lua")
+
+ -- always load lua lsp
+ require('nlua.lsp.nvim').setup(require('lspconfig'), {
+ cmd = { "/lsp/bin/lua-language-server", "-E", "/lsp/main.lua" },
+ on_attach = buffer_setup_no_format,
+
+ -- Include globals you want to tell the LSP are real :)
+ globals = {
+ -- Colorbuddy
+ "Color", "c", "Group", "g", "s",
+ }
+ })
+
+ -- lspconfig.sumneko_lua.setup {
+ -- filetypes = { "lua" },
+ -- on_attach = buffer_setup_no_format,
+ -- settings = {
+ -- Lua = {
+ -- completion = {
+ -- keywordSnippet = "Disable",
+ -- showWord = "Disable",
+ -- },
+ -- diagnostics = {
+ -- enable = true,
+ -- globals = vim.list_extend({
+ -- -- Neovim
+ -- "vim",
+ -- -- Busted
+ -- "describe", "it", "before_each", "after_each", "teardown", "pending", "clear"
+ -- }, {})
+ -- },
+ -- runtime = {
+ -- version = "LuaJIT",
+ -- },
+ -- workspace = {
+ -- vim.list_extend(get_lua_runtime(), {}),
+ -- maxPreload = 10000,
+ -- preloadFileSize = 10000,
+ -- },
+ -- }
+ -- }
+ -- }
+
+ -- out = vim.fn.system('docker images -q mvinkio/azure-pipelines-lsp')
+ -- if string.len(out) ~= 0 then
+ -- lspconfig.yamlls.setup {
+ -- before_init = function(params)
+ -- params.processId = vim.NIL
+ -- end,
+ -- on_new_config = function(new_config, new_root_dir)
+ -- new_config.cmd = {
+ -- "node",
+ -- new_root_dir,
+ -- home .. "/projects/devops-pipelines/node_modules/azure-pipelines-language-server/out/server.js",
+ -- "--stdio"
+ -- }
+ -- end,
+ -- filetypes = { "yaml" },
+ -- root_dir = lspconfig.util.root_pattern(".git", vim.fn.getcwd()),
+ -- on_attach = buffer_setup_no_format,
+ -- settings = {
+ -- yaml = {
+ -- format = {
+ -- enable = true
+ -- },
+ -- schemas = {
+ -- [home .. "/projects/devops-pipelines/schema"] = "/*"
+ -- },
+ -- validate = true
+ -- }
+ -- }
+ -- }
+ -- else
+ -- utils.log_warning("No image mvinkio/azure-pipelines-lsp.", "vimrc/lsp", true)
+ -- end
+
+ local out = vim.fn.system('docker images -q mvinkio/python')
+ if string.len(out) ~= 0 then
+ lspconfig.pyright.setup {
+ cmd = {
+ "docker",
+ "run",
+ "--rm",
+ "--env-file=" .. vim.fn.getcwd() .. "/.env",
+ "--interactive",
+ "--workdir=" .. vim.fn.getcwd(),
+ "--volume=" .. vim.fn.getcwd() .. ":" .. vim.fn.getcwd(),
+ "mvinkio/python",
+ "pyright-langserver", "--stdio"
+ },
+ on_new_config = function(new_config, new_root_dir)
+ new_config.cmd = {
+ "docker",
+ "run",
+ "--rm",
+ "--env-file=" .. new_root_dir .. "/.env",
+ "--interactive",
+ "--workdir=" .. new_root_dir,
+ "--volume=" .. new_root_dir .. ":" .. new_root_dir,
+ "mvinkio/python",
+ "pyright-langserver", "--stdio"
+ }
+ end,
+ filetypes = { "python" },
+ root_dir = lspconfig.util.root_pattern(".git", vim.fn.getcwd()),
+ on_attach = buffer_setup_no_format,
+ }
+ else
+ utils.log_warning("No image mvinkio/python.", "vimrc/lsp", true)
+ end
+
+ out = vim.fn.system('docker images -q mvinkio/go')
+ if string.len(out) ~= 0 then
+ lspconfig.gopls.setup {
+ before_init = function(params)
+ params.processId = vim.NIL
+ end,
+ on_new_config = function(new_config, new_root_dir)
+ new_config.cmd = {
+ "docker",
+ "run",
+ "--rm",
+ "--interactive",
+ "-e=GOPROXY=https://proxy.golang.org",
+ "-e=GOOS=linux",
+ "-e=GOARCH=amd64",
+ "-e=GOPATH=" .. new_root_dir .. "/go",
+ "-e=GOCACHE=" .. new_root_dir .. "/.cache/go-build",
+ "--workdir=" .. new_root_dir,
+ "--volume=" .. new_root_dir .. ":" .. new_root_dir,
+ "--network=bridge",
+ "mvinkio/go",
+ "gopls"
+ }
+ end,
+ -- cmd = { "docker", "run", "--rm", "-i", "-v", home .. ":" .. home, "mvinkio/gopls" },
+ filetypes = { "go", "gomod", "gotmpl" },
+ on_attach = buffer_setup_no_format,
+ }
+ else
+ utils.log_warning("No image mvinkio/go.", "vimrc/lsp", true)
+ end
+
+ -- out = vim.fn.system('docker images -q mvinkio/sveltels')
+ -- if string.len(out) ~= 0 then
+ -- lspconfig.svelte.setup {
+ -- before_init = function(params)
+ -- params.processId = vim.NIL
+ -- end,
+ -- cmd = {
+ -- "docker",
+ -- "run",
+ -- "--rm",
+ -- "--interactive",
+ -- "--volume=" .. home .. ":" .. home,
+ -- "--network=none",
+ -- "mvinkio/sveltels"
+ -- },
+ -- on_attach = buffer_setup,
+ -- }
+ -- else
+ -- utils.log_warning("No image mvinkio/sveltels.", "vimrc/lsp", true)
+ -- end
+
+ -- }}}
+
+ local null_ls = require("null-ls")
+ local my_black = null_ls.builtins.formatting.black.with({
+ filetypes = { "python" },
+ command = "black",
+ args = { "$FILENAME" }
+ })
+ null_ls.setup({
+ debug = vim.fn.expand("$VIMRC_NULL_LS_DEBUG") == "1",
+ update_on_insert = false,
+ on_attach = buffer_setup,
+ sources = {
+ my_black,
+ null_ls.builtins.completion.luasnip
+ }
+ })
+end
+
+-- }}}
+
+return M
+-- vim: fdm=marker
diff --git a/neovim/lua/vimrc/snippets.lua b/neovim/lua/vimrc/snippets.lua
new file mode 100644
index 0000000..e4d60d7
--- /dev/null
+++ b/neovim/lua/vimrc/snippets.lua
@@ -0,0 +1,133 @@
+local ls = require "luasnip"
+local s = ls.snippet
+local sn = ls.snippet_node
+local isn = ls.indent_snippet_node
+local t = ls.text_node
+local i = ls.insert_node
+local f = ls.function_node
+local c = ls.choice_node
+local d = ls.dynamic_node
+local r = ls.restore_node
+local events = require("luasnip.util.events")
+local ai = require("luasnip.nodes.absolute_indexer")
+local fmt = require("luasnip.extras.fmt").fmt
+local m = require("luasnip.extras").m
+local lambda = require("luasnip.extras").l
+local postfix = require("luasnip.extras.postfix").postfix
+
+local map = vim.api.nvim_set_keymap
+
+local M = {}
+
+local go_snippets = function()
+ ls.add_snippets("go", {
+
+ s(";r", fmt([[
+ <>, err := <>
+ if err != nil {
+ <>
+ }
+ ]], {
+ i(1,"ret"), i(2,"fun"), i(3, "return err")
+ }, {
+ delimiters = "<>"
+ })),
+
+ s(";e", fmt([[
+ if <>, <> := <>; <> {
+ <>
+ }
+ ]], {
+ i(1,"v"),i(2,"err"),i(3,"fun"), i(4, "err != nil"), i(5,"return err")
+ }, {
+ delimiters = "<>"
+ })),
+
+ s(";fr", fmt([[
+ for <>, <> := range <> {
+ <>
+ }
+ ]], {
+ i(1,"_"),i(2,"_"), i(3,"iterable"), i(4,"body")
+ }, {
+ delimiters = "<>"
+ })),
+
+ s(";sj", fmt([[
+ <> <> `json:"<>"`
+ ]], {
+ i(1,"field"),i(2,"type"), d(3, function(args)
+ for i, line in pairs(args[1]) do
+ args[1][i] = line:gsub("(%u)", function(ch) return '_' .. ch:lower() end):gsub("^_", '')
+ end
+ return sn(nil, {i(1,args[1])})
+ end,
+ {1})
+ }, {
+ delimiters = "<>"
+ })),
+
+ s(";test", fmt([[
+func Test<>(t *testing.T) {
+ for i, c := range []struct {
+ expected <>
+ }{
+ } {
+ t.Run(fmt.Sprintf("%d %s", i, c.expected), func(t *testing.T) {
+ <>
+ })
+ }
+}
+ ]], {
+ i(1,"test"),i(2,"type"), i(3,"body")
+ }, {
+ delimiters = "<>"
+ })),
+
+
+
+ })
+end
+
+
+function M.setup()
+ go_snippets()
+ ls.config.setup({
+ load_ft_func =
+ -- Also load both lua and json when a markdown-file is opened,
+ -- javascript for html.
+ -- Other filetypes just load themselves.
+ require("luasnip.extras.filetype_functions").extend_load_ft({
+ markdown = { "lua", "json" },
+ html = { "javascript" }
+ })
+ })
+ -- press <Tab> to expand or jump in a snippet. These can also be mapped separately
+ -- via <Plug>luasnip-expand-snippet and <Plug>luasnip-jump-next.
+ vim.keymap.set(
+ "i",
+ "<Tab>",
+ [[luasnip#expand_or_jumpable() ? '<Plug>luasnip-expand-or-jump' : '<Tab>']],
+ { silent = true, noremap = true, expr = true }
+ )
+ vim.keymap.set(
+ { "i", "s" },
+ "<S-Tab>",
+ [[<cmd>lua require'luasnip'.jump(-1)<CR>]],
+ { silent = true, noremap = true }
+ )
+ vim.keymap.set(
+ "s",
+ "<Tab>",
+ [[<cmd>lua require'luasnip'.jump(1)<CR>]],
+ { silent = true, noremap = true }
+ )
+ vim.keymap.set(
+ { "i", "s" },
+ "<C-E>",
+ [[luasnip#choice_active() ? '<Plug>luasnip-next-choice' : '<C-E>']],
+ { silent = true, noremap = true, expr = true }
+ )
+end
+
+return M
diff --git a/neovim/lua/vimrc/term.lua b/neovim/lua/vimrc/term.lua
new file mode 100644
index 0000000..409ff72
--- /dev/null
+++ b/neovim/lua/vimrc/term.lua
@@ -0,0 +1,91 @@
+local api = vim.api
+local cmd = vim.cmd
+
+local M = {}
+local jobid = nil
+local winid = nil
+
+
+local function bo(option, value)
+ api.nvim_buf_set_option(0, option, value)
+end
+
+local function launch_term(cmd, opts)
+ opts = opts or {}
+ vim.cmd("belowright new")
+ winid = vim.fn.win_getid()
+ api.nvim_win_set_var(0, 'REPL', 1)
+ bo('buftype', 'nofile')
+ bo('bufhidden', 'wipe')
+ bo('buflisted', false)
+ bo('swapfile', false)
+ opts = vim.tbl_extend('error', opts, {
+ on_exit = function()
+ jobid = nil
+ end
+ })
+ jobid = vim.fn.termopen(cmd, opts)
+end
+
+local function close_term()
+ if not jobid then return end
+ vim.fn.jobstop(jobid)
+ if api.nvim_win_is_valid(winid) then
+ api.nvim_win_close(winid, true)
+ end
+ winid = nil
+end
+
+function M.toggle()
+ if jobid then
+ close_term()
+ else
+ launch_term(vim.fn.environ()['SHELL'] or 'sh')
+ end
+end
+
+
+function M.run()
+ local filepath = api.nvim_buf_get_name(0)
+ close_term()
+ launch_term(filepath)
+end
+
+
+function M.sendLine(line)
+ if not jobid then return end
+ vim.fn.chansend(jobid, line .. '\n')
+end
+
+function M.sendSelection()
+ if not jobid then return end
+ local start_row, start_col = unpack(api.nvim_buf_get_mark(0, '<'))
+ local end_row, end_col = unpack(api.nvim_buf_get_mark(0, '>'))
+ local mode = vim.fn.visualmode()
+ local offset
+ if mode == '' then -- in block mode all following lines are indented
+ offset = start_col
+ elseif mode == 'V' then
+ end_row = end_row + 1
+ offset = 0
+ else
+ offset = 0
+ end
+ local lines = api.nvim_buf_get_lines(0, start_row - 1, end_row, false)
+ for idx, line in pairs(lines) do
+ local l
+ if idx == 1 and idx == #lines then
+ l = line:sub(start_col + 1, end_col + 1)
+ elseif idx == 1 then
+ l = line:sub(start_col + 1)
+ elseif idx == #lines then
+ l = line:sub(offset + 1, end_col > #line and #line or end_col + 1)
+ elseif offset > 0 then
+ l = line:sub(offset + 1)
+ else
+ l = line
+ end
+ vim.fn.chansend(jobid, l .. '\n')
+ end
+end
+return M
diff --git a/neovim/lua/vimrc/terragrunt.lua b/neovim/lua/vimrc/terragrunt.lua
new file mode 100644
index 0000000..84ac867
--- /dev/null
+++ b/neovim/lua/vimrc/terragrunt.lua
@@ -0,0 +1,141 @@
+local vim = vim
+local cmd = vim.cmd
+local fn = vim.fn
+
+local M = {}
+function M.setup(opts)
+ local env_file = opts.env_file or ''
+ local home = os.getenv('HOME')
+ local user = os.getenv('USER')
+ local cwd = opts.cwd or home
+ P(cwd)
+
+ local functions = {}
+
+ functions.clean = function()
+ require('firvish.job_control').start_job({
+ cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--env-file",
+ env_file,
+ "--volume",
+ cwd .. ":" .. cwd,
+ "--workdir",
+ cwd,
+ "alpine/terragrunt",
+ "/bin/sh",
+ "-c",
+ [[find ]] .. cwd .. [[ -type d -name .terragrunt-cache -prune -exec rm -rf {} \; &&]]
+ .. [[find ]] .. cwd .. [[ -type f -name .terraform.lock.hcl -prune -exec rm -rf {} \;]]
+ },
+ filetype = "log",
+ title = "clean",
+ listed = true,
+ output_qf = false,
+ is_background_job = true
+ })
+ end
+
+ functions.plan = function(terragrunt_path, dirty)
+ if not dirty then
+ functions.clean()
+ end
+ P(terragrunt_path)
+ local cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--env-file",
+ env_file,
+ "--volume",
+ cwd .. ":" .. cwd,
+ "--workdir",
+ cwd .. "/" .. terragrunt_path,
+ "alpine/terragrunt",
+ "terragrunt",
+ "plan"
+ }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "plan",
+ listed = true,
+ output_qf = true,
+ is_background_job = false
+ })
+ end
+
+ functions.apply = function(terragrunt_path, dirty)
+ if not dirty then
+ functions.clean()
+ end
+ local cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--env-file",
+ env_file,
+ "--volume",
+ cwd .. ":" .. cwd,
+ "--workdir",
+ cwd .. "/" .. terragrunt_path,
+ "alpine/terragrunt",
+ "terragrunt",
+ "apply",
+ "--terragrunt-non-interactive",
+ "--auto-approve"
+ }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "plan",
+ listed = true,
+ output_qf = true,
+ is_background_job = false
+ })
+ end
+
+ functions.destroy = function(terragrunt_path, dirty)
+ if not dirty then
+ functions.clean()
+ end
+ local cmd = {
+ "docker",
+ "run",
+ "--interactive",
+ "--rm",
+ "--env-file",
+ env_file,
+ "--volume",
+ cwd .. ":" .. cwd,
+ "--workdir",
+ cwd .. "/" .. terragrunt_path,
+ "alpine/terragrunt",
+ "terragrunt",
+ "destroy",
+ "--terragrunt-non-interactive",
+ "--auto-approve"
+ }
+ require('firvish.job_control').start_job({
+ cmd = cmd,
+ filetype = "log",
+ title = "plan",
+ listed = true,
+ output_qf = true,
+ is_background_job = false
+ })
+ end
+
+ _G.terragrunt_functions = functions
+ cmd [[command! -nargs=1 -complete=dir -bang TGplan :lua _G.terragrunt_functions.plan("<args>", "<bang>" ~= "!")]]
+ cmd [[command! -nargs=1 -complete=dir -bang TGapply :lua _G.terragrunt_functions.apply("<args>", "<bang>" ~= "!")]]
+ cmd [[command! -nargs=1 -complete=dir -bang TGdestroy :lua _G.terragrunt_functions.destroy("<args>", "<bang>" ~= "!")]]
+ cmd [[command! TGclean :lua _G.terragrunt_functions.clean()]]
+
+end
+return M
diff --git a/neovim/lua/vimrc/utils.lua b/neovim/lua/vimrc/utils.lua
new file mode 100644
index 0000000..386efd2
--- /dev/null
+++ b/neovim/lua/vimrc/utils.lua
@@ -0,0 +1,70 @@
+local vim = vim
+local cmd = vim.cmd
+local M = {}
+
+-- paths {{{
+-- }}}
+--
+-- logging {{{
+function M.log_error(msg, source, persist)
+ if source then
+ msg = "[" .. source .. "] " .. msg
+ end
+ cmd [[echohl ErrorMsg]]
+ if persist then
+ cmd("echomsg '" .. msg .. "'")
+ else
+ cmd("echo '" .. msg .. "'")
+ end
+ cmd [[echohl Normal]]
+end
+
+function M.log_warning(msg, source, persist)
+ if source then
+ msg = "[" .. source .. "]" .. msg
+ end
+ msg = string.gsub(msg, "'", '"')
+ cmd [[echohl WarningMsg]]
+ if persist then
+ cmd("echomsg '" .. msg .. "'")
+ else
+ cmd("echo '" .. msg .. "'")
+ end
+ cmd [[echohl Normal]]
+end
+-- }}}
+
+-- tables {{{
+function table.filter()
+ print"hi"
+end
+
+function table.keys(tbl)
+ local k = {}
+ for key, val in pairs(tbl) do
+ table.insert(k, key)
+ end
+ return k
+end
+
+-- }}}
+
+-- string {{{
+function string.join(str, join_token)
+ local j = ""
+ local join = join_token or ""
+ if #str == 1 then
+ return str[1]
+ end
+ for i, token in ipairs(str) do
+ if i > 1 then
+ j = j .. join .. token
+ else
+ j = j .. token
+ end
+ end
+ return j
+end
+-- }}}
+return M
+-- vim: fdm=marker