Since i am currently working on a little tool for synching my old macbook pro, my workstation at home and my work thinkpad - I want to highlight a part of the lua configuration for said tool as bash, not as a lua string.
I use this tool (mehr2) to keep my installed packages on all systems in sync.
I plan to use the following configuration file:
1MEHR2 = {
2 packages = {
3 default = {
4 "git",
5 "picom",
6 "fish",
7 "imagemagick",
8 "firefox",
9 "flameshot",
10 "pipewire",
11 "dunst",
12 "rofi",
13 "i3",
14 "acpi",
15 "zathura",
16 "curl",
17 },
18 apt = { "build-essentials" },
19 pacman = { "base-devel", "pamixer", "hugo", "go", "ghostty" },
20 scratch = {
21 {
22 identifier = "rustup",
23 needs = { "curl" },
24 update = "rustup update",
25 script = [[
26 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
27 rustup component add rust-docs
28 rustup component add cargo
29 rustup component add clippy
30 rustup component add rustfmt
31 ]]
32 }
33 },
34 {
35 -- see: https://github.com/neovim/neovim/blob/master/BUILD.md
36 identifier = "nvim",
37 git = "github.com/neovim/neovim",
38 needs = { "make", "cmake", "gcc" },
39 branch = "nightly",
40 script = [[
41 make CMAKE_BUILD_TYPE=Release
42 make install
43 ]]
44 }
45 },
46 {
47 -- see: https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager
48 identifier = "zig",
49 execute_for = { "apt" },
50 update = "snap refresh zig",
51 needs = { "snap" },
52 script = "snap install zig --classic --beta"
53 },
54 {
55 -- see: https://ghostty.org/docs/install/build
56 identifier = "ghostty",
57 execute_for = { "apt" },
58 git = "github.com/ghostty-org/ghostty",
59 needs = { "zig", "gtk4" },
60 script = "zig build -p /usr -Doptimize=ReleaseFast"
61 },
62 {
63 identifier = "go",
64 execute_for = { "apt" },
65 script = [[
66 VERSION=$(curl -s "https://go.dev/VERSION?m=text" | head -n1)
67 wget https://go.dev/dl/$VERSION.linux-amd64.tar.gz
68 rm -rf /usr/local/go
69 tar -C /usr/local -xzf $VERSION.linux-amd64.tar.gz
70 ]]
71 },
72 {
73 -- see: https://gohugo.io/installation/linux/#build-from-source
74 identifier = "hugo",
75 execute_for = { "apt" },
76 needs = { "go" },
77 script = "go install github.com/gohugoio/hugo@latest"
78 },
79 },
80 cargo = { "exa", "bat", "ripgrep", "yazi" }
81 }
82}The default array defines packages the current package manager would install.
The packages in the apt, pacman and cargo arrays are only installed if
the corresponding package manager is executable on the target machine (some
differ in names, depending on the package manager, so I install them
specifically). However each entry in the scratch array is executed if any of
the entries in the execute_for field are found on the system. The execution
is done by passing the value of the script field to bash. If the
execute_for field is missing, the array entry is executed every time.
Since the script field contains a bash script, i want it to be highlighted as
such. Neovim and treesitter enable this exact usecase.
Neovim setup
I use packer to install treesitter:
1-- .config/nvim/lua/teo/packer.lua
2
3-- packer installation
4local fn = vim.fn
5local install_path = fn.stdpath('data') .. '/site/pack/packer/start/packer.nvim'
6if fn.empty(fn.glob(install_path)) > 0 then
7 packer_bootstrap = fn.system({ 'git', 'clone', '--depth', '1', 'https://github.com/wbthomason/packer.nvim',
8 install_path })
9 download_result = fn.system({ 'ls', '-l', install_path })
10 print("download_result: " .. download_result)
11end
12
13vim.cmd [[packadd packer.nvim]]
14
15return require('packer').startup(function(use)
16 -- syntax highlighting and parser
17 use { 'nvim-treesitter/nvim-treesitter', run = ':TSUpdate' }
18end):PackerSync will synchronise your neovim instance to your configuration
and install treesitter.
Treesitter setup
I configure treesitter in a minimalistic way:
1-- .config/nvim/after/plugin/treesitter.lua
2
3require'nvim-treesitter.configs'.setup {
4 ensure_installed = { "c", "lua", "rust", "javascript", "css", "html", "markdown", "javascript"},
5 sync_install = false,
6 auto_install = true,
7 highlight = {
8 enable = true,
9 additional_vim_regex_highlighting = false,
10 },
11}Injections
Queries are S-expressions and documented here
Treesitter injections are placed at
.config/nvim/queries/<filetype>/injections.scm1 and contain
configurations treesitter injects into files with the corresponding filetype.
For my specific usecase I search for the script table property/field:
1; Inject into script = [[...]] as bash
2((field
3 name: (identifier) @_name
4 value: (string
5 content: (string_content) @injection.content
6 (#eq? @_name "script")
7 (#set! injection.language "bash")
8 )))The injection works by quering the treesitter tree (inspectable via
:InspectTree) for a field where its value is a string and its name is equal
to "script". If so the string content is set to be highlighted as bash:
