Rewrite the little Go program

- Use Cobra instead of Magefile
- Use Neovim's Go client library to get the data and config
  directories. This is more reliable than guessing from XDG variables
  then falling back to $HOME.
This commit is contained in:
Hoang Nguyen 2024-01-31 00:00:00 +07:00
parent 962c4cd89e
commit 0f01c172ff
Signed by: folliehiyuki
GPG Key ID: B0567C20730E9B11
16 changed files with 417 additions and 400 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/pkg-install

View File

@ -1,15 +0,0 @@
{
"neoconf": {
"live_reload": false,
"import": {
"vscode": false
}
},
"lspconfig": {
"gopls": {
"gopls.buildFlags": [
"-tags=mage"
]
}
}
}

View File

@ -60,6 +60,6 @@ This Neovim config used to live within [my dotfiles](/FollieHiyuki/dotfiles-ansi
### Why not using [mason.nvim](https://github.com/williamboman/mason.nvim)?
Most things installed via **mason.nvim** are binaries linked to glibc, which are incompatible to run on the Linux distribution I use - [AlpineLinux](https://alpinelinux.org). I resolved to custom [mage](https://magefile.org) tasks instead. The [tasks' source code](./magefile.go) is pretty simple and naive, but it works for me.
Most things installed via **mason.nvim** are binaries linked to glibc, which are incompatible to run on the Linux distribution I use - [AlpineLinux](https://alpinelinux.org). I resolved to custom Go program instead. The code is pretty simple and naive, but it works for me.
External LSP servers that can't be installed directly from AlpineLinux's repositories, or with simple `cargo/go install` commands, are defined inside [pkgs.yaml](./pkgs.yaml) file.

5
go.mod
View File

@ -5,8 +5,9 @@ go 1.20
require (
github.com/go-playground/validator/v10 v10.16.0
github.com/goccy/go-yaml v1.11.2
github.com/magefile/mage v1.15.0
github.com/neovim/go-client v1.2.2-0.20230716041012-dd77a916541b
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
golang.org/x/sync v0.6.0
mvdan.cc/sh/v3 v3.7.0
)
@ -16,9 +17,11 @@ require (
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect

12
go.sum
View File

@ -1,3 +1,4 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -17,22 +18,29 @@ github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QX
github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ=
github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/neovim/go-client v1.2.2-0.20230716041012-dd77a916541b h1:JDchP8fWykRzJLiq5cUQ47vKkP8UY0I1yzgGDTSpYRI=
github.com/neovim/go-client v1.2.2-0.20230716041012-dd77a916541b/go.mod h1:UBsOERb5epbeQT0nyPTZkmUPTffRYBcHvrXXidr1NQQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

View File

@ -1,6 +1,6 @@
{
"LuaSnip": { "branch": "master", "commit": "2dbef19461198630b3d7c39f414d09fb07d1fdd2" },
"SchemaStore.nvim": { "branch": "main", "commit": "f877fa4f16b04c8d2e253539e1803a0850833272" },
"SchemaStore.nvim": { "branch": "main", "commit": "11d661ae5e08f19b5256661a6491a66fa26cdcfc" },
"aerial.nvim": { "branch": "master", "commit": "e9661d4f739508377f42528fd08a85c4c8feba6e" },
"alpha-nvim": { "branch": "main", "commit": "4b36c1ca9ea475bdc006896657cf1ccc486aeffa" },
"better-escape.nvim": { "branch": "master", "commit": "7e86edafb8c7e73699e0320f225464a298b96d12" },
@ -12,16 +12,16 @@
"cmp-fish": { "branch": "main", "commit": "8ae7bfb1b3251d433c4adf4e64396ef929fbd91f" },
"cmp-nvim-lsp": { "branch": "main", "commit": "5af77f54de1b16c34b23cba810150689a3a90312" },
"cmp_luasnip": { "branch": "master", "commit": "05a9ab28b53f71d1aece421ef32fee2cb857a843" },
"conform.nvim": { "branch": "master", "commit": "3d59cbd01a4b74124c5a6fb23b8cc63e5c2db3d5" },
"conform.nvim": { "branch": "master", "commit": "4588008a7c5b57fbff97fdfb529c059235cdc7ee" },
"dial.nvim": { "branch": "master", "commit": "27eb570085db2ef44bff4f620d3806039184651c" },
"diffview.nvim": { "branch": "main", "commit": "3dc498c9777fe79156f3d32dddd483b8b3dbd95f" },
"dressing.nvim": { "branch": "master", "commit": "42d767b04c50a6966c9633e3968bc65c0c2f2bfc" },
"emmylua-nvim": { "branch": "master", "commit": "0fd565dbaef5a24a5fb6c015d7d7fa670a474fac" },
"dressing.nvim": { "branch": "master", "commit": "6f212262061a2120e42da0d1e87326e8a41c0478" },
"emmylua-nvim": { "branch": "master", "commit": "015200711c5c6178a026d96ab042a3788b41310f" },
"flash.nvim": { "branch": "main", "commit": "48817af25f51c0590653bbc290866e4890fe1cbe" },
"flatten.nvim": { "branch": "main", "commit": "a999f1abd8fa9f8e2447a65726f9f44288c26f3f" },
"friendly-snippets": { "branch": "main", "commit": "69a2c1675b66e002799f5eef803b87a12f593049" },
"flatten.nvim": { "branch": "main", "commit": "92c59ae6200e6b04aff167577ebb8a9035a6d2b3" },
"friendly-snippets": { "branch": "main", "commit": "b8fae73a479ae0a1c54f5c98fa687ae8a0addc53" },
"git-conflict.nvim": { "branch": "main", "commit": "4c8e252b87d54d944c1e56bfb477f78b6fdaf661" },
"gitsigns.nvim": { "branch": "main", "commit": "c5ff7628e19a47ec14d3657294cc074ecae27b99" },
"gitsigns.nvim": { "branch": "main", "commit": "2c2463dbd82eddd7dbab881c3a62cfbfbe3c67ae" },
"headlines.nvim": { "branch": "master", "commit": "e3d7bfdf40e41a020d966d35f8b48d75b90367d2" },
"heirline.nvim": { "branch": "master", "commit": "1b6f12e011f225a26aa162905cbf68804479e7e6" },
"inc-rename.nvim": { "branch": "main", "commit": "6f9b5f9cb237e12935144cdc535322b8c93c1b25" },
@ -30,20 +30,20 @@
"lazy.nvim": { "branch": "main", "commit": "28126922c9b54e35a192ac415788f202c3944c9f" },
"lsp_lines.nvim": { "branch": "main", "commit": "cf2306dd332e34a3e91075b40bdd4f6db824b2ee" },
"markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" },
"mini.ai": { "branch": "main", "commit": "f7787cff9cc42004f722ca1e64e6af4e64e34177" },
"mini.align": { "branch": "main", "commit": "708c0265b1513a00c83c181bff3d237741031cd1" },
"mini.ai": { "branch": "main", "commit": "3ad9d455a91b8bf3c24d4e50518d9a6b9dddb42c" },
"mini.align": { "branch": "main", "commit": "20f5a1a93d86a7e82eaca9334212e54058ef3562" },
"mini.animate": { "branch": "main", "commit": "6cec625114007527ff8a82316dba858046f9746f" },
"mini.bufremove": { "branch": "main", "commit": "020243bfed8c8b941f2c20626faf3ea39c0c0e1b" },
"mini.comment": { "branch": "main", "commit": "67f00d3ebbeae15e84584d971d0c32aad4f4f3a4" },
"mini.comment": { "branch": "main", "commit": "b0b359ada4293cdcea7ab4072dfd5b031aac3f8e" },
"mini.move": { "branch": "main", "commit": "03a16d64e58da0a871de6493c3d8fa1101baef46" },
"mini.pairs": { "branch": "main", "commit": "552062017ff207e1f35f7028bfb3f27c7421d22d" },
"mini.surround": { "branch": "main", "commit": "7bf8915ba15d7a4f3c2afe7868d3c15a858d73f1" },
"neo-tree.nvim": { "branch": "main", "commit": "e578fe7a5832421b0d2c5b3c0a7a1e40e0f6a47a" },
"neoconf.nvim": { "branch": "main", "commit": "cfc29315288515849aa54c05828d49f01f033b66" },
"mini.surround": { "branch": "main", "commit": "5ceb6a12d3761bc719fbdad5432c89333deb1498" },
"neo-tree.nvim": { "branch": "main", "commit": "c2a9e81699021f4ccaac7c574cc42ca4211a499a" },
"neoconf.nvim": { "branch": "main", "commit": "bf04719b3b25ba5f6a059c0a10ee67e5996bdc4c" },
"neogen": { "branch": "main", "commit": "70127baaff25611deaf1a29d801fc054ad9d2dc1" },
"neogit": { "branch": "master", "commit": "ce0c369ccdba3f644a3b28f4c053421f435352c9" },
"neotest": { "branch": "master", "commit": "dcdb40ea48f9c7b67a5576f6bb2e5f63ec15f2c0" },
"neotest-go": { "branch": "main", "commit": "d29d20d912aca81a07c50022d880cc66f0d26542" },
"neotest": { "branch": "master", "commit": "73043d666780e35185a77589e01bec96a52db910" },
"neotest-go": { "branch": "main", "commit": "22513619bcb156939c22ea7cd1a99f754fcb1fde" },
"neotest-jest": { "branch": "main", "commit": "a394106cf053eef86d65ae04c4b93a1a7bd60aef" },
"neotest-python": { "branch": "master", "commit": "2e83d2bc00acbcc1fd529dbf0a0e677cabfe6b50" },
"neotest-vitest": { "branch": "main", "commit": "87e91bfd9419a8c74bf0d105e2ae31b9692daf0b" },
@ -51,50 +51,50 @@
"noice.nvim": { "branch": "main", "commit": "bf67d70bd7265d075191e7812d8eb42b9791f737" },
"nui.nvim": { "branch": "main", "commit": "35da9ca1de0fc4dda96c2e214d93d363c145f418" },
"nvim-cmp": { "branch": "main", "commit": "538e37ba87284942c1d76ed38dd497e54e65b891" },
"nvim-cokeline": { "branch": "main", "commit": "a62eaff75c59da2ca261fefc2992e1cac04bd30e" },
"nvim-cokeline": { "branch": "main", "commit": "7310f192af74c6912ca7a40ae1b16253aa95e50e" },
"nvim-coverage": { "branch": "main", "commit": "cf4b5c61dfac977026a51a2bcad9173c272986ce" },
"nvim-dap": { "branch": "master", "commit": "9adbfdca13afbe646d09a8d7a86d5d031fb9c5a5" },
"nvim-dap-repl-highlights": { "branch": "master", "commit": "902d533b2682c26d6be4f8a4d1dfd895cbba4840" },
"nvim-dap-ui": { "branch": "master", "commit": "d845ebd798ad1cf30aa4abd4c4eff795cdcfdd4f" },
"nvim-dap-virtual-text": { "branch": "master", "commit": "d4542ac257d3c7ee4131350db6179ae6340ce40b" },
"nvim-lint": { "branch": "master", "commit": "2cf9ad095130755d7d87f1730bcf33c91ee822e4" },
"nvim-lspconfig": { "branch": "master", "commit": "8917d2c830e04bf944a699b8c41f097621283828" },
"nvim-lint": { "branch": "master", "commit": "8e5920f9ce9f24c283a2e64be5fe58d1d37d1744" },
"nvim-lspconfig": { "branch": "master", "commit": "021906284dcfb938bc236f8295af2650c60cb807" },
"nvim-navic": { "branch": "master", "commit": "8649f694d3e76ee10c19255dece6411c29206a54" },
"nvim-notify": { "branch": "master", "commit": "80b67b265530632505193553d05127ae7fe09ddd" },
"nvim-scrollbar": { "branch": "main", "commit": "35f99d559041c7c0eff3a41f9093581ceea534e8" },
"nvim-spectre": { "branch": "master", "commit": "d8906855f1949ac97b1e77aaf8d3fe12ed158ddc" },
"nvim-treesitter": { "branch": "master", "commit": "177a775fd8b2a9de97011a43f13c74ad8d3739c8" },
"nvim-treesitter-context": { "branch": "master", "commit": "85cf977181fb8e816e47ac05df7f756e9cb72caf" },
"nvim-spectre": { "branch": "master", "commit": "d958cc3ea8b3fb2f0922dff6177630a40751a3a9" },
"nvim-treesitter": { "branch": "master", "commit": "5f950cdcb82d2cf74e6b30338897b3a4897f52b3" },
"nvim-treesitter-context": { "branch": "master", "commit": "9c06b115abc57c99cf0aa81dc29490f5001f57a1" },
"nvim-treesitter-textobjects": { "branch": "master", "commit": "19a91a38b02c1c28c14e0ba468d20ae1423c39b2" },
"nvim-ts-autotag": { "branch": "main", "commit": "8515e48a277a2f4947d91004d9aa92c29fdc5e18" },
"nvim-ts-autotag": { "branch": "main", "commit": "a65b202cfd08e0e69e531eab737205ff5bc082a4" },
"nvim-ts-context-commentstring": { "branch": "main", "commit": "1277b4a1f451b0f18c0790e1a7f12e1e5fdebfee" },
"nvim-web-devicons": { "branch": "master", "commit": "140edfcf25093e8b321d13e154cbce89ee868ca0" },
"nvim-web-devicons": { "branch": "master", "commit": "aaec87dbdaa776bfa0a13c8694bec9bcb7454719" },
"nvim-window-picker": { "branch": "main", "commit": "41cfaa428577c53552200a404ae9b3a0b5719706" },
"oil.nvim": { "branch": "master", "commit": "bf753c3e3f8736939ad5597f92329dfe7b1df4f5" },
"orgmode": { "branch": "master", "commit": "651078a2fe60b12c93903e3a2b655491c951bf9d" },
"orgmode": { "branch": "master", "commit": "ab045e3084d5987e8939d25d69b09baaf762278c" },
"overseer.nvim": { "branch": "master", "commit": "68a2d344cea4a2e11acfb5690dc8ecd1a1ec0ce0" },
"pantran.nvim": { "branch": "main", "commit": "461799624948bfb66f73b20b6fffa7e4c8ca6d08" },
"parinfer-rust": { "branch": "master", "commit": "0e4d52e712641ad351a1bfe6cee3d34d63ed087b" },
"persistence.nvim": { "branch": "main", "commit": "4982499c1636eac254b72923ab826ee7827b3084" },
"plenary.nvim": { "branch": "master", "commit": "663246936325062427597964d81d30eaa42ab1e4" },
"plenary.nvim": { "branch": "master", "commit": "4f71c0c4a196ceb656c824a70792f3df3ce6bb6d" },
"project.nvim": { "branch": "main", "commit": "8c6bad7d22eef1b71144b401c9f74ed01526a4fb" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "35530b4ad48f01bdaa852da34e4de9930fb54c89" },
"rest.nvim": { "branch": "main", "commit": "84e81a19ab24ccf05c9233d34d4dfce61c233abe" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "c84eeb689e4cc86808e823c11eac4239c75292ce" },
"rest.nvim": { "branch": "main", "commit": "2d7bd3d398940ce2692941e6cd052c072207b9f9" },
"scope.nvim": { "branch": "main", "commit": "cd27af77ad61a7199af5c28d27013fb956eb0e3e" },
"sniprun": { "branch": "master", "commit": "0079f9c4675a6825f84e108bbff866f67dd8762f" },
"ssr.nvim": { "branch": "main", "commit": "bb323ba621ac647b4ac5638b47666e3ef3c279e1" },
"stickybuf.nvim": { "branch": "master", "commit": "2160fcd536d81f5fa43f7167dba6634e814e3154" },
"telescope-file-browser.nvim": { "branch": "master", "commit": "6f735a63dc24b9aed527cd505a31864223c8a6d8" },
"telescope-file-browser.nvim": { "branch": "master", "commit": "6dd6522bc1a4cbe5883eb0272f5cc7a54ae3858b" },
"telescope-fzf-native.nvim": { "branch": "main", "commit": "6c921ca12321edaa773e324ef64ea301a1d0da62" },
"telescope-symbols.nvim": { "branch": "master", "commit": "a6d0127a53d39b9fc2af75bd169d288166118aec" },
"telescope-tabs": { "branch": "master", "commit": "c3572358e60817f40f0de31bc01906af0993d4c4" },
"telescope.nvim": { "branch": "master", "commit": "36dce6261ae3660bb57ba942361067b2028aec31" },
"telescope.nvim": { "branch": "master", "commit": "e54fbf4ab2b64640b639b75c006c23b4bc51c86f" },
"todo-comments.nvim": { "branch": "main", "commit": "833d8dd8b07eeda37a09e99460f72a02616935cb" },
"toggleterm.nvim": { "branch": "main", "commit": "b49df5cdce67a8964d1b027dae94bde212092b51" },
"treesj": { "branch": "main", "commit": "14808da3cddd62fc86ede53a5ea1fd1635897e75" },
"trouble.nvim": { "branch": "main", "commit": "f1168feada93c0154ede4d1fe9183bf69bac54ea" },
"twilight.nvim": { "branch": "main", "commit": "8b7b50c0cb2dc781b2f4262a5ddd57571556d1e4" },
"undotree": { "branch": "master", "commit": "d9c8b4ef872e078e8c4080812e5a3ed56d151c00" },
"undotree": { "branch": "master", "commit": "a1758ba9990b7189f601a3a5acdfc8ca3907a700" },
"venn.nvim": { "branch": "main", "commit": "e4d68341a73dd56c64955058821a58295fb337b1" },
"vim-log-highlighting": { "branch": "master", "commit": "1037e26f3120e6a6a2c0c33b14a84336dee2a78f" },
"vim-matchup": { "branch": "master", "commit": "e2cca1747ab175b8d839a5d28679427564643a57" },

View File

@ -25,8 +25,8 @@ local function get_binary_paths(binaries)
return table.concat(paths, ',')
end
local function node_root_dir(fname)
return lsputil.root_pattern('tsconfig.json', 'jsconfig.json', 'package.json')(fname)
local function deno_root_dir(fname)
return lsputil.root_pattern('deno.json', 'deno.jsonc')(fname)
end
return {
@ -67,11 +67,7 @@ return {
-- Ref: https://docs.deno.com/runtime/manual/advanced/language_server/overview
denols = {
-- Ensure `tsserver` and `denols` aren't attached to a single buffer at the same time
root_dir = function(fname)
if node_root_dir(fname) == nil then
return lsputil.root_pattern('deno.json', 'deno.jsonc', '.git')(fname)
end
end,
root_dir = deno_root_dir,
init_options = { enable = true, lint = true },
settings = {
deno = {
@ -104,15 +100,16 @@ return {
gleam = {},
-- this uses a project's local instance of glint-language-server (`pnpm add -D @glint/core`)
glint = {
filetypes = {
'handlebars',
'html.handlebars',
'typescript.glimmer',
'javascript.glimmer',
},
},
-- this uses a project's local instance of glint-language-server
-- Ref: https://typed-ember.gitbook.io/glint/getting-started
-- glint = {
-- filetypes = {
-- 'handlebars',
-- 'html.handlebars',
-- 'typescript.glimmer',
-- 'javascript.glimmer',
-- },
-- },
-- NOTE: for Bazel setup (GOPACKAGESDRIVER), use neoconf.nvim
-- Ref: https://github.com/bazelbuild/rules_go/wiki/Editor-setup
@ -298,7 +295,11 @@ return {
tsserver = {
cmd = { servers_path .. '/tsserver/node_modules/.bin/typescript-language-server', '--stdio' },
root_dir = node_root_dir,
root_dir = function(fname)
if deno_root_dir(fname) == nil then
return lsputil.root_pattern('package.json', 'tsconfig.json', 'jsconfig.json', '.git')(fname)
end
end,
single_file_support = false,
},

View File

@ -7,6 +7,7 @@ return {
changedelete = { text = '-' },
untracked = { text = '+' },
},
attach_to_untracked = true,
current_line_blame_opts = { virt_text_pos = 'right_align' },
preview_config = { border = require('user.config.vars').border },
on_attach = function(bufnr)

View File

@ -24,7 +24,7 @@ return {
['@string.special'] = { link = 'Special' },
['@string.special.symbol'] = { fg = c.purple },
['@string.special.url'] = { link = '@markup.link.url' },
['@string.special.path'] = { fg = c.fg, underline = true },
['@string.special.path'] = { fg = c.green, underline = true },
['@character'] = { link = 'Character' },
['@character.special'] = { link = 'SpecialChar' },

12
mage.go
View File

@ -1,12 +0,0 @@
//go:build ignore
// +build ignore
package main
import (
"os"
"github.com/magefile/mage/mage"
)
func main() { os.Exit(mage.Main()) }

View File

@ -1,321 +0,0 @@
//go:build mage
// +build mage
package main
// NOTE: intentionally not handle errors with mage to have nice terminal output via logrus
// TODO: run commands with sh.Run instead of exec.Command (when https://github.com/magefile/mage/issues/213 is fixed)
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"slices"
"sort"
"strings"
"github.com/go-playground/validator/v10"
"github.com/goccy/go-yaml"
"github.com/magefile/mage/mg"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
)
type Target struct {
Env map[string]string `yaml:"env,omitempty" validate:"excluded_unless=Type custom,omitempty,min=1,dive,keys,min=1,endkeys,required"`
Type string `yaml:"type" validate:"required,oneof=python nodejs custom"`
Category []string `yaml:"category" validate:"required,min=1,dive,oneof=lsp dap lint"`
Pkgs []string `yaml:"pkgs,omitempty" validate:"excluded_if=Type custom,omitempty,min=1,dive,min=1"`
Script []string `yaml:"script,omitempty" validate:"required_if=Type custom,omitempty,min=1,dive,min=1"`
}
type Install mg.Namespace
var Aliases = map[string]any{
// Shorter task names to type
"i": Install.Pkg,
"i:lsp": Install.Lsp,
"i:dap": Install.Dap,
"i:lint": Install.Linters,
"i:all": Install.All,
// `Install` is already registered as a type, so can't make an original func name of it
"install": Install.Pkg,
}
var (
validate = validator.New()
targets, readErr = readTargets("pkgs.yaml")
)
func init() {
logrus.SetFormatter(&logrus.TextFormatter{
DisableTimestamp: true,
DisableQuote: true,
})
logrus.SetOutput(os.Stderr)
logrus.SetLevel(logrus.InfoLevel)
}
// List lists installable targets
func List() {
if readErr != nil {
logrus.Fatal(readErr)
}
// Categorize the list
categories := map[string][]string{
"LSP servers": {},
"DAP servers": {},
"Linters/Formatters": {},
}
for target, spec := range targets {
// NOTE: there will be duplications (.e.g elixirls), but it is fine
if slices.Contains(spec.Category, "lsp") {
categories["LSP servers"] = append(categories["LSP servers"], target)
}
if slices.Contains(spec.Category, "dap") {
categories["DAP servers"] = append(categories["DAP servers"], target)
}
if slices.Contains(spec.Category, "lint") {
categories["Linters/Formatters"] = append(categories["Linters/Formatters"], target)
}
}
// Iterate over a list, to make the output stable
categoryList := []string{}
for name := range categories {
categoryList = append(categoryList, name)
}
sort.Strings(categoryList)
for _, name := range categoryList {
fmt.Printf("* %s:\n", name)
sort.Strings(categories[name])
for _, target := range categories[name] {
fmt.Printf(" - %s\n", target)
}
}
}
// Pkg installs the specified target(s)
func (Install) Pkg(target string) {
if readErr != nil {
logrus.Fatal(readErr)
}
// Split the input into individual targets
t := strings.Split(target, " ")
if err := installGroup(targets, t); err != nil {
logrus.Fatal(err)
}
}
// Lsp installs all LSP servers
func (Install) Lsp() {
if readErr != nil {
logrus.Fatal(readErr)
}
logrus.Warn("Installing LSP servers")
if err := installGroup(targets, filterTargets(targets, "lsp")); err != nil {
logrus.Fatal(err)
}
}
// Dap installs all DAP servers
func (Install) Dap() {
if readErr != nil {
logrus.Fatal(readErr)
}
logrus.Warn("Installing DAP servers")
if err := installGroup(targets, filterTargets(targets, "dap")); err != nil {
logrus.Fatal(err)
}
}
// Linters installs all linters and formatters
func (Install) Linters() {
if readErr != nil {
logrus.Fatal(readErr)
}
logrus.Warn("Installing linters and formatters")
if err := installGroup(targets, filterTargets(targets, "lint")); err != nil {
logrus.Fatal(err)
}
}
// All installs everything
func (Install) All() {
if readErr != nil {
logrus.Fatal(readErr)
}
logrus.Warn("Installing everything")
if err := installGroup(targets, filterTargets(targets, "all")); err != nil {
logrus.Fatal(err)
}
}
// readTargets returns the list of installable targets from YAML input file
func readTargets(file string) (map[string]Target, error) {
input, err := os.ReadFile(file)
if err != nil {
return nil, err
}
targets := map[string]Target{}
err = yaml.Unmarshal(input, &targets)
if err != nil {
return nil, err
}
// Validate the received input before return it
for target, spec := range targets {
err = validate.Struct(spec)
if err != nil {
return nil, fmt.Errorf("[%s] invalid input: %v", target, err)
}
}
return targets, nil
}
// filterTargets returns the list of installable targets with category filter
func filterTargets(targets map[string]Target, category string) []string {
result := []string{}
for name, spec := range targets {
// Treat `all` as matching everything
if slices.Contains(spec.Category, category) || category == "all" {
result = append(result, name)
}
}
return result
}
// installGroup installs a list of targets concurrently if they exist within specified group
func installGroup(targets map[string]Target, t []string) error {
errGroup := new(errgroup.Group)
for _, target := range t {
target := target
errGroup.Go(func() error {
return install(targets, target)
})
}
return errGroup.Wait()
}
// install installs the specified target if it exists inside the provided target list
func install(targets map[string]Target, target string) error {
targetLog := logrus.WithField("target", target)
spec, ok := targets[target]
if !ok {
return fmt.Errorf("invalid target '%s'", target)
}
targetLog.Warn("Installing target")
data_dir, ok := os.LookupEnv("XDG_DATA_HOME")
if !ok {
data_dir = filepath.Join(os.Getenv("HOME"), ".local", "share")
}
install_dir := filepath.Join(data_dir, "nvim", spec.Category[0], target)
// Create the parent directory to install packages into (in the case of `python` or `nodejs`).
// For `custom` type, the script should make this directory in its own way
if spec.Type == "nodejs" || spec.Type == "python" {
err := os.MkdirAll(install_dir, 0o755)
if err != nil {
return err
}
// Use the target's name as its package name, if not specified
if len(spec.Pkgs) == 0 {
spec.Pkgs = append(spec.Pkgs, target)
}
}
switch spec.Type {
case "nodejs":
for _, pkg := range spec.Pkgs {
cmd := [3]string{"pnpm", "add", fmt.Sprintf("%s@latest", pkg)}
targetLog.Infof("%s %s %s", cmd[0], cmd[1], cmd[2])
execCmd := exec.Command(cmd[0], cmd[1:]...)
execCmd.Dir = install_dir
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
if err := execCmd.Run(); err != nil {
return fmt.Errorf("failed to install %s: %v", pkg, err)
}
}
case "python":
// Always upgrade pip along the way
spec.Pkgs = append(spec.Pkgs, "pip")
// Create a venv directory to install the target's packages into
execCmd := exec.Command("python3", "-m", "venv", install_dir)
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
if err := execCmd.Run(); err != nil {
return fmt.Errorf("failed to create venv: %v", err)
}
for _, pkg := range spec.Pkgs {
cmd := [4]string{filepath.Join("bin", "pip3"), "install", "-U", pkg}
targetLog.Infof("%s %s %s %s", cmd[0], cmd[1], cmd[2], cmd[3])
execCmd = exec.Command(cmd[0], cmd[1:]...)
execCmd.Dir = install_dir
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
if err := execCmd.Run(); err != nil {
return fmt.Errorf("failed to install %s: %v", pkg, err)
}
}
case "custom":
// Always export `PKG_INSTALL_DIR` for convenient usage in script block.
// Also inherit from the shell env, to have proper PATH and so on.
envs := append(os.Environ(), "PKG_INSTALL_DIR="+install_dir)
for k, v := range spec.Env {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
runner, err := interp.New(
interp.Env(expand.ListEnviron(envs...)),
interp.StdIO(nil, os.Stdout, os.Stderr),
)
if err != nil {
return err
}
ctx := context.TODO() // just create a dummy context for each custom target
for _, line := range spec.Script {
targetLog.Info(line)
script, err := syntax.NewParser().Parse(strings.NewReader(line), "")
if err != nil {
return err
}
err = runner.Run(ctx, script)
if err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,18 @@
package main
import "github.com/neovim/go-client/nvim"
func findNvimDataDir() (string, error) {
var nvim_data_dir string
vim, err := nvim.NewChildProcess(nvim.ChildProcessArgs("--embed"))
if err != nil {
return "", err
}
if err := vim.ExecLua("return vim.fn.stdpath(...)", &nvim_data_dir, "data"); err != nil {
return "", err
}
return nvim_data_dir, vim.Close()
}

View File

@ -0,0 +1,127 @@
package main
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
)
func installPkgGroup(data_dir string, targets map[string]Target, t []string) error {
errGroup := new(errgroup.Group)
for _, target := range t {
target := target
errGroup.Go(func() error {
return installPkg(data_dir, targets, target)
})
}
return errGroup.Wait()
}
func installPkg(data_dir string, targets map[string]Target, target string) error {
// Verify that the target exists
spec, ok := targets[target]
if !ok {
return fmt.Errorf("invalid target '%s'", target)
}
targetLog := logrus.WithField("target", target)
targetLog.Warn("Installing target")
// Install targets into categorized directories
install_dir := filepath.Join(data_dir, spec.Category[0], target)
// Create the parent directory to install packages into (in the case of `python` or `nodejs`).
// For `custom` type, the script should make this directory in its own way
if spec.Type == "nodejs" || spec.Type == "python" {
err := os.MkdirAll(install_dir, 0o755)
if err != nil {
return err
}
// Use the target's name as its package name, if not specified
if len(spec.Pkgs) == 0 {
spec.Pkgs = append(spec.Pkgs, target)
}
}
switch spec.Type {
case "nodejs":
for _, pkg := range spec.Pkgs {
cmd := [3]string{"pnpm", "add", fmt.Sprintf("%s@latest", pkg)}
targetLog.Infof("%s %s %s", cmd[0], cmd[1], cmd[2])
execCmd := exec.Command(cmd[0], cmd[1:]...)
execCmd.Dir = install_dir
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
if err := execCmd.Run(); err != nil {
return fmt.Errorf("failed to install %s: %v", pkg, err)
}
}
case "python":
// Always upgrade pip along the way
spec.Pkgs = append(spec.Pkgs, "pip")
// Create a venv directory to install the target's packages into
execCmd := exec.Command("python3", "-m", "venv", install_dir)
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
if err := execCmd.Run(); err != nil {
return fmt.Errorf("failed to create python venv: %v", err)
}
for _, pkg := range spec.Pkgs {
cmd := [4]string{filepath.Join("bin", "pip3"), "install", "-U", pkg}
targetLog.Infof("%s %s %s %s", cmd[0], cmd[1], cmd[2], cmd[3])
execCmd = exec.Command(cmd[0], cmd[1:]...)
execCmd.Dir = install_dir
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
if err := execCmd.Run(); err != nil {
return fmt.Errorf("failed to install %s: %v", pkg, err)
}
}
case "custom":
// Always export `PKG_INSTALL_DIR` for convenient usage in script block.
// Also inherit from the shell env, to have proper PATH and so on.
envs := append(os.Environ(), "PKG_INSTALL_DIR="+install_dir)
for k, v := range spec.Env {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
runner, err := interp.New(
interp.Env(expand.ListEnviron(envs...)),
interp.StdIO(nil, os.Stdout, os.Stderr),
)
if err != nil {
return err
}
ctx := context.TODO() // just create a dummy context for each custom target
for _, line := range spec.Script {
targetLog.Info(line)
script, err := syntax.NewParser().Parse(strings.NewReader(line), "")
if err != nil {
return err
}
err = runner.Run(ctx, script)
if err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,47 @@
package main
import (
"fmt"
"slices"
"sort"
)
func listPkgs(targets map[string]Target) error {
// Categorize the list
categories := map[string][]string{
"LSP servers": {},
"DAP servers": {},
"Linters/Formatters": {},
}
for target, spec := range targets {
// NOTE: there will be duplications (items belonging to multiple categories, .e.g elixirls), but it is intentional
if slices.Contains(spec.Category, "lsp") {
categories["LSP servers"] = append(categories["LSP servers"], target)
}
if slices.Contains(spec.Category, "dap") {
categories["DAP servers"] = append(categories["DAP servers"], target)
}
if slices.Contains(spec.Category, "lint") {
categories["Linters/Formatters"] = append(categories["Linters/Formatters"], target)
}
}
// Iterate over a list, to make the output stable
categoryList := []string{}
for name := range categories {
categoryList = append(categoryList, name)
}
sort.Strings(categoryList)
for _, name := range categoryList {
fmt.Printf("* %s:\n", name)
sort.Strings(categories[name])
for _, target := range categories[name] {
fmt.Printf(" - %s\n", target)
}
}
return nil
}

View File

@ -0,0 +1,92 @@
package main
import (
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func init() {
logrus.SetFormatter(&logrus.TextFormatter{
DisableTimestamp: true,
DisableQuote: true,
})
logrus.SetOutput(os.Stderr)
logrus.SetLevel(logrus.InfoLevel)
}
func main() {
var list, lsp, dap, lint bool
rootCmd := &cobra.Command{
Use: "pkg-install",
Short: "A small CLI program to install LSP/DAP servers and various linters/formatters",
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Just print help output if there is nothing to do
if cmd.Flags().NFlag() == 0 && len(args) == 0 {
return cmd.Help()
}
targets, err := readTargets()
if err != nil {
return err
}
if list {
return listPkgs(targets)
}
nvim_data_dir, err := findNvimDataDir()
if err != nil {
return err
}
// We can install multiple categories at the same time
if lsp {
logrus.Warn("Installing LSP servers")
err := installPkgGroup(nvim_data_dir, targets, filterTargets(targets, "lsp"))
if err != nil {
return err
}
}
if dap {
logrus.Warn("Installing DAP servers")
err := installPkgGroup(nvim_data_dir, targets, filterTargets(targets, "dap"))
if err != nil {
return err
}
}
if lint {
logrus.Warn("Installing linters and formatters")
err := installPkgGroup(nvim_data_dir, targets, filterTargets(targets, "lint"))
if err != nil {
return err
}
}
// Only install specified targets when no categories are selected
if cmd.Flags().NFlag() == 0 {
err := installPkgGroup(nvim_data_dir, targets, args)
if err != nil {
return err
}
}
return nil
},
}
rootCmd.PersistentFlags().BoolVar(&list, "list", false, "List installable targets")
rootCmd.PersistentFlags().BoolVar(&lsp, "lsp", false, "Install all LSP servers")
rootCmd.PersistentFlags().BoolVar(&dap, "dap", false, "Install all DAP servers")
rootCmd.PersistentFlags().BoolVar(&lint, "lint", false, "Install all linters and formatters")
err := rootCmd.Execute()
if err != nil {
logrus.Error(err)
os.Exit(1)
}
}

View File

@ -0,0 +1,67 @@
package main
import (
"fmt"
"os"
"path/filepath"
"slices"
"github.com/go-playground/validator/v10"
"github.com/goccy/go-yaml"
"github.com/neovim/go-client/nvim"
)
type Target struct {
Env map[string]string `yaml:"env,omitempty" validate:"excluded_unless=Type custom,omitempty,min=1,dive,keys,min=1,endkeys,required"`
Type string `yaml:"type" validate:"required,oneof=python nodejs custom"`
Category []string `yaml:"category" validate:"required,min=1,dive,oneof=lsp dap lint"`
Pkgs []string `yaml:"pkgs,omitempty" validate:"excluded_if=Type custom,omitempty,min=1,dive,min=1"`
Script []string `yaml:"script,omitempty" validate:"required_if=Type custom,omitempty,min=1,dive,min=1"`
}
var validate = validator.New()
func readTargets() (map[string]Target, error) {
var nvim_config_dir string
vim, err := nvim.NewChildProcess(nvim.ChildProcessArgs("--embed"))
if err != nil {
return nil, err
}
if err = vim.ExecLua("return vim.fn.stdpath(...)", &nvim_config_dir, "config"); err != nil {
return nil, err
}
if err = vim.Close(); err != nil {
return nil, err
}
input, err := os.ReadFile(filepath.Join(nvim_config_dir, "pkgs.yaml"))
if err != nil {
return nil, err
}
targets := map[string]Target{}
err = yaml.Unmarshal(input, &targets)
if err != nil {
return nil, err
}
// Validate the received input before return it
for target, spec := range targets {
err = validate.Struct(spec)
if err != nil {
return nil, fmt.Errorf("[%s] invalid input: %v", target, err)
}
}
return targets, nil
}
func filterTargets(targets map[string]Target, category string) []string {
result := []string{}
for name, spec := range targets {
if slices.Contains(spec.Category, category) {
result = append(result, name)
}
}
return result
}