Neovim, Rust e Il Bianconiglio
Disclaimer: Il senso di questo post e' dimostrare come il gamedev (la programmazione grafica, in generale) in Rust, su terminale Linux, nonostante possa sembrare spaventoso e masochistico, sia un'esperienza estremamente gratificante. La riflessione si basa quasi esclusivamente sul processo creativo. Se l'obiettivo e' creare un sofisticato software nel minor tempo possibile, il consiglio condiviso e' quello di tenersi lontani da Rust e neovim e utilizzare engine dedicati (Godot, Unity, Unreal etc. etc.).
Qualche giorno fa sulla homepage di hackernews e' apparso un post sulla procrastinazione produttiva. Io so pochissimo di psicologia e ogni volta che leggo di ricette per aumentare la produttivita' penso al turbocapitalismo e ho un brivido lungo la spina dorsale. Ma e' un mio problema, non sono un professionista e tendo a dare molta piu' importanza al processo che al risultato finale - ma visto che viviamo nell'epoca della AI generativa, forse il mio approccio non e' sbagliatissimo.
Comunque, tornando alla procrastinazione, l'autore del post ne distingue due tipologie, tra le tante: una totalmente dannosa e una (forse) utile.
La prima e' quella che ti siedi al cavalletto, impugni la matita, guardi il foglio e decidi che forse sarebbe il caso di lanciare una lavatrice.
E poi l'aspirapolvere, una tisana perche' no?, oh santo cielo e' quasi ora di cena meglio procacciarsi del cibo!
Su questo c'e' un episodio esemplare dei Simpsons in cui Lisa decide di scrivere un romanzo per giovani adulti, resa plastica di questa follia cerebrale.

La seconda, la procrastinazione utile, include invece quello che l'autore del post definisce "Rewarding Novelty", novita' gratificante. Mi siedo al solito cavalletto e decido che non mi piace il taglio della luce che entra dalla finestra, decido di riorganizzare gli spazi, cambio posizione della seduta etc. etc. Mi ritrovo in un ambiente nuovo, personalizzato e stimolante. Nella mia esperienza, l'ambiente digitale segue la stessa logica.
Perche' Neovim?
E non un IDE? Ovviamente tutta la mia prolissa introduzione per dire che non c'e' risposta a queste domande, si parte banalmente dalla volonta' di creare un ambiente su misura e si finisce nelle profondita' della tana del Bianconiglio. Oltre al totale controllo dell'editor, l'aspetto piu' interessante di neovim e' la grammatica dei comandi (di derivazione vi) che, per semplificare, e' composta da 'verbo' e 'oggetto' (es. d2j: cancello due righe). Una volta sviluppata la memoria muscolare ci si dimentica del mouse, delle freccette, del file browser: l'editor diventa estensione naturale del cervello (++dopamina).
Perche' NON Neovim?
Le motivazioni per non usare neovim sono probabilmente piu' di quelle per utilizzarlo, alcune speculari: curva di apprendimento ripida, l'uso del terminale per sviluppare applicazioni grafiche puo' sembrare controintuitivo, batterie non incluse, troppa personalizzazione, repulsione verso Lua (ognuno ha le proprie idiosincrasie). Se si vuole utilizzare un debugger si soffre l'assenza di una GUI - no problem se il debugging arriva massimo a log::debug!("hey!"). Infine mentre sto scrivendo siamo alla versione 0.12, la 1.0 e' ancora lontana.
Un approccio minimale
Disclaimer 2: Da qui in poi scendo nel tecnico. Prendete il codice come riferimento, questo non vuole essere un tutorial, la configurazione e' estremamente personale, basata sul mio uso e in generale diffidate da The Best NeoVim Configuration in the World. Detto questo: Repo su Gitlab
Ovviamente nessuno vieta di installare decine di plugin e ritrovarsi un clone di vscode (ma tanto vale usare un IDE?). Il mio approccio e' minimale e tendo a ridurre il numero di righe di configurazione. Su questo le ultime release mi vengono incontro introducendo un plugin manager (vim.pack, scritto tra l'altro dall'autore di mini, plugin di cui probabilmente abuso) e vim.lsp.
Init.lua
-- ...
-- Plugins
vim.pack.add({
'https://github.com/nvim-mini/mini.nvim',
'https://github.com/neovim/nvim-lspconfig',
'https://github.com/saghen/blink.cmp',
'https://github.com/nvim-lua/plenary.nvim',
'https://github.com/sindrets/diffview.nvim',
'https://github.com/NeogitOrg/neogit',
'https://github.com/MeanderingProgrammer/render-markdown.nvim'
})
-- ...LSP
Per chi programma l'utilizzo di un Language Server e' imprescindibile, per l'autocompletamento, le definizioni, i salti da implementazione a uso, per la formattazione, la diagnostica, etc. etc.. Per Rust la scelta semi-obbligata e' rust-analyzer e, sorpresona!, anche lui deve essere configurato attraverso il Language Server Protocol. Qui trovate tutte le sue impostazioni; rust-analyzer consuma molta ram, potreste voler abilitare/disabilitare alcune funzioni in caso lavoriate in un progetto con molte dipendenze.
after/lsp/rust-analyzer.lua
return {
on_attach = function(client, buf_id)
client.server_capabilities.completionProvider.triggerCharacters =
{ '.', ':', '#', '(' }
end,
settings = {
["rust-analyzer"] = {
cargo = {
allFeatures = true,
},
checkOnSave = true,
inlayHints = {
bindingModeHints = { enable = true },
closureReturnTypeHints = { enable = true },
lifetimeElisionHints = { enable = true },
parameterHints = { enable = true },
typeHints = { enable = true },
},
diagnostics = {
enable = true,
experimental = { enable = true },
},
procMacro = {
enable = true,
},
},
},
}~/.config/nvim/
Alcune considerazioni: gestisco rust-analyzer da pacman, per lasciare neovim piu' snello possibile, in alternativa e' possibile utilizzare mason.nvim. Discorso simile per treesitter: e' possibile installare i parser direttamente dal repository extra di Arch Linux. In alternativa c'e' treesitter.nvim ma dovrete comunque installare separatamente tree-sitter-cli. Per l'autocompletamento uso blink.cmp, che secondo me e' ancora un passo avanti rispetto a quello built-in.
plugin/30_plugins.lua
-- Configuration of plugins installed in init.lua
require('mini.basics').setup({
options = { basic = false },
mappings = {
-- Create `<C-hjkl>` mappings for window navigation
windows = true,
-- Create `<M-hjkl>` mappings for navigation in Insert and Command modes
move_with_alt = true,
},
}
)
require('mini.files').setup({ windows = { preview = false } })
require('mini.surround').setup()
require('mini.tabline').setup()
require('mini.statusline').setup()
require('mini.icons').setup()
require('mini.bufremove').setup()
require('mini.pick').setup()
require('mini.sessions').setup()
require('mini.notify').setup()
require('mini.extra').setup()
require('mini.ai').setup({
-- 'mini.ai' can be extended with custom textobjects
custom_textobjects = {
-- Make `aB` / `iB` act on around/inside whole *b*uffer
B = MiniExtra.gen_ai_spec.buffer(),
-- For more complicated textobjects that require structural awareness,
-- use tree-sitter. This example makes `aF`/`iF` mean around/inside function
-- definition (not call). See `:h MiniAi.gen_spec.treesitter()` for details.
F = require('mini.ai').gen_spec.treesitter({ a = '@function.outer', i = '@function.inner' }),
},
-- 'mini.ai' by default mostly mimics built-in search behavior: first try
-- to find textobject covering cursor, then try to find to the right.
-- Although this works in most cases, some are confusing. It is more robust to
-- always try to search only covering textobject and explicitly ask to search
-- for next (`an`/`in`) or last (`an`/`il`).
-- Try this. If you don't like it - delete next line and this comment.
search_method = 'cover',
}
)
vim.lsp.enable({
'lua_ls',
'rust_analyzer',
})
require('blink.cmp').setup({
keymap = { preset = 'default' },
appearance = {
nerd_font_variant = 'mono',
},
completion = {
trigger = {
show_on_trigger_character = true,
show_on_blocked_trigger_characters = { ' ', '\n', '\t' }
},
ghost_text = {
-- enabled = true
},
documentation = { auto_show = false },
menu = {
-- Delay before showing the completion menu while typing
auto_show_delay_ms = 500,
-- Disable auto menu
-- auto_show = false,
}
},
sources = {
default = { 'lsp', 'path', 'buffer' },
},
signature = { enabled = true },
fuzzy = {
implementation = 'prefer_rust',
},
}Note sui colorscheme
Qui e' facile impazzire e probabilmente ne faro' un post dedicato. Il consiglio che mi sono dato e' quello di evitare temi arlecchino,
usare pochi colori ognuno dei quali con un significato preciso, non abusare di grassetto, corsivo, sottolineature
e usare contrasto/temperatura per evidenziare/nascondere le diverse parti di codice.

Alacritty + Tmux
In Neovim e' possibile dividere la finestra, aprirne una del terminale e usare tab, nativamente. Molti utenti, me compreso, preferiscono usare il terminale fuori dall'editor e lasciare a neovim quello che sa fare meglio: l'editor. Non apro il capitolo terminal emulator o questo post non vedra' mai la luce (c'e' una wave pazzesca intorno alle console, probabilmente anche grazie alle AI). Importante che sia reattivo, supporti il truecolor e permetta di passare da una tab all'altra con una facile combo di tasti.
alacritty.toml:
[terminal.shell]
program = "tmux"
[env]
TERM = "xterm-256color"
[window]
decorations = "None"
startup_mode = "Maximized"
padding.x = 1
# dynamic_padding = true
[general]
import = [
"~/.config/alacritty/themes/themes/gruvbox_material_medium_dark.toml"
]
[font]
normal = { family = "JetBrains Mono Nerd Font", style = "Regular" }
size = 14.5
[cursor]
style = { shape = "Beam", blinking = "On" }
[keyboard]
bindings = []
tmux.conf:
# Terminal settings
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",xterm-256color:Tc"
# Ensure mouse mode is actually on
set -g mouse on
# set -g mode-keys vi
set -g prefix C-s
bind : command
bind C-s send-prefix
bind C-r source-file ~/.config/tmux/tmux.conf;
bind Space copy-mode
bind -T copy-mode-vi Escape send-keys -X cancel
# new tab
bind C-w new-window
bind C-a prev
bind C-d next
# STYLE
set -g window-style 'fg=colour247,bg=red'
set -g window-active-style 'fg=colour250,bg=black'
# panes
set -g pane-border-style 'fg=yellow, bg=black'
set -g pane-active-border-style 'fg=brown, bg=black'
# statusbar
set -g status-position top
set -g status-justify left
set -g status-style 'fg=blue, bg=default'
set -g status-left ''
set -g status-left-length 10
set -g status-right '#[fg=red,dim,bg=default]#(uptime | cut -f 4-5 -d " " | cut -f 1 -d ",") #[fg=white,bg=default]%a%l:%M:%S %p#[default]'
setw -g window-status-current-style 'fg=white bg=black bold'
setw -g window-status-current-format ' #I:#W#F '
setw -g window-status-style 'fg=colour59 bg=default'
setw -g window-status-format ' #I:#W#F '
setw -g window-status-bell-style 'fg=black bg=green'
# messages
set -g message-style 'fg=black bg=yellow bold'Note finali
Se siete a digiuno di neovim/vi ma lo state considerando da un po', il mio consiglio e' quello di prendere coraggio e partire da una distribuzione "chiavi in mano", sperimentando con le configurazioni e modellando il vostro ambiente agendo per sottrazione (es. LazyVim, MinMax etc. etc.). Per imparare invece i comandi principali, consiglio dei tutorial gamificati, come i leggendari VimAdventures o VimGolf.