Tudo começa na Lua
Uma das funcionalidades mais aguardadas e amadas pelos usuários do Neovim é o suporte à lua.
Esse suporte veio, oficialmente, na versão 0.5 do Neovim, o que passou a permitir que os usuários pudessem jogar no lixo deixar de usar seus init.vim
e configurar o Neovim usando um belo init.lua
.
E uma feliz consequência disso é que, não só podemos usar lua, mas podemos usar pacotes do ecossistema lua e linguagens que compilam
para lua
Hello, Peter Fennel
Fennel é uma dessas linguagens que compilam para lua, o que significa que você vai escrever código fennel, o compilador vai gerar código lua, que vai ser lido e executado pelo Neovim.
Fennel -> Lua -> Neovim
Mas, por que Fennel?
Essa é uma pergunta bastante comum que as pessoas fazem quando eu digo que uso Fennel.
Na verdade, é uma pergunta comum que as pessoas fazem para todos que usam Fennel, pelo que parece, porque no site oficial do Fennel tem a resposta para, exatamente, essa pergunta.
Vou resumir a resposta deles em alguns pontos.
Fennel é menos propenso à alguns erros
Lua é uma ótima linguagem, porém existem alguns pontos que podem facilitar a ocorrência de erros.
Um dos pontos é a facilidade com que você acessa ou altera uma variável global.
Se você criar uma variável sem a palavra chave local
ela já é uma variável global. E para acessar o valor dessa variável, basta digitar o nome da variável, o que pode causar comportamentos inesperados, por exemplo:
-- settings.lua
myVar = 'this is global'
-- init.lua
local myVal = 'local'
print('this is the value of local: ' .. myVar)
Repare que, por um erro de digitação da palavra myVal
, trocando o l
pelo r
acabamos acessando o valor de uma variável global definida em outro lugar.
Erros como esse podem ser difíceis de descobrir.
Fennel previne erros como esse permitindo que o acesso à variáveis globais sejam feitos apenas através da tabela _G
.
Ao tentar simular o caso acima em Fennel, o compilador nos alertará que a variável myVar
não existe.
(local myVal "local")
(print (.. "This is the value of local: " myVar))
:: COMPILE ERROR
xxx settings/globals.fnl
Compile error in settings/globals.fnl:42
unknown identifier in strict mode: myVar
* Try looking to see if there's a typo.
* Try using the _G table instead, eg. _G.myVar if you really want a global.
* Try moving this code to somewhere that myVar is in scope.
* Try binding myVar as a local in the scope of this code.
Outro ponto, que pode facilitar a ocorrência de erros em lua, é a falta de validação do número de argumentos de uma função.
local function myAnd(x, y)
return x and y
end
print(myAnd(true)) -- nil
Repare que eu apenas passei 1 argumento, sendo que a função trabalha com 2 parâmetros, o código em lua rodou sem me avisar que esqueci de passar outro argumento para a função.
Em Fennel, podemos usar a palavra chave lambda
para criar funções que validam os parâmetros:
(lambda my-and [x y]
(and x y))
(print (my-and true)) ; Error [...] Missing argument y
Nota: Em fennel, ;
é o caractere usado para iniciar um comentário
(Sintaxe (do (Lisp!)))
Esse é um ponto um pouco polêmico porque algumas pessoas não gostam da sintaxe do Lisp, mas ela traz alguns benefícios:
- Tudo é uma expressão, ou seja, não temos statements
- Quando lidamos com operadores, não há ambiguidade do que vem primeiro, não temos "precedência de operadores". (Em lua, por exemplo,
A or B and C or D
)
Esses pontos tornam Fennel uma linguagem muito simples de se programar e de se dar manutenção.
Modernidade e facilidades
Além dos pontos mencionados acima, vale a pena ressaltar algumas funcionalidades interessantes que o Fennel traz para facilitar a nossa vida.
Com Fennel, temos desestruturação, pattern matching, macros e mais.
Desestruturação
Enquanto em lua fazemos:
-- Lua
local var = require'module'.var
local var2 = require'module'.var2
Em fennel, podemos, simplesmente, fazer:
; Fennel
(local {: var : var2} (require :module))
Pattern matching
Quando queremos testar o valor de uma variável várias vezes, em lua, fazemos uma sequência de if
:
-- Lua
local function get_desc(key)
if (key == "k1") then
return "Key 1"
elseif (key == "k2") then
return "Key 2"
elseif (key == "k3") then
return "Key 3"
else
return nil
end
Enquanto, em fennel, podemos usar o match
:
; Fennel
(lambda get-desc [key]
(match key
:k1 "Key 1"
:k2 "Key 2"
:k3 "Key 3"))
Como começar a usar
Agora que eu te convenci (pelo menos espero) a usar fennel, vamos ver como começar a usa-lo para configurar o Neovim!
Vamos usar dois plugins para isso:
-
tangerine
udayvir-singh / tangerine.nvim
🍊 Sweet Fennel integration for Neovim
About
Tangerine provides a painless way to add fennel to your config.
Features
- 🔥 BLAZING fast, compile times in milliseconds
- 🌊 100% support for interactive evaluation
- 🎍 Control over when and how to compile
- 🎀 Natively loads
nvim/init.fnl
Comparison to other plugins
HOTPOT 🍲
- Abstracts too much away from the user.
- Hooks onto lua package searchers to compile [harder to debug].
ANISEED 🌿
- Excessively feature rich for use in dotfiles.
- Blindly compiles all files that it founds, resulting in slow load times.
Installation
- Create file
plugin/0-tangerine.lua
to bootstrap tangerine:
Important
If you are using lazy.nvim then you should create
init.lua
instead ofplugin/0-tangerine.lua
.Refer to #20 for more information.
-- ~/.config/nvim/plugin/0-tangerine.lua or ~/.config/nvim/init.lua -- pick your plugin manager local pack = "tangerine" or "packer" or "paq" or "lazy
… -
hibiscus
Hibiscus.nvim
🌺 Highly opinionated macros to elegantly write your neovim config.
Companion library for tangerine but it can also be used standalone.
Rational
- 🍬 Syntactic eye candy over hellscape of lua api
- 🎋 Provides missing features in both fennel and nvim api
Installation
- Create file
plugin/0-tangerine.lua
to bootstrap hibiscus:
NOTE: if you are using lazy plugin manager, you should create
/init.lua
instead.-- ~/.config/nvim/plugin/0-tangerine.lua or ~/.config/nvim/init.lua -- pick your plugin manager local pack = "tangerine" or "packer" or "paq" or "lazy" local function bootstrap(url, ref) local name = url:gsub(".*/", "") local path if pack == "lazy" then path = vim.fn.stdpath("data") .. "/lazy/" .. name vim.opt.rtp:prepend(path) else
…
O tangerine integra de maneira bem transparente o Fennel com o Neovim, compilando os arquivos fennel para lua e trazendo umas ferramentas interessantes.
O hibiscus traz várias macros relacionadas ao ecossistema do Neovim que nos ajudam a escrever menos
O primeiro passo é criar o arquivo ~/.config/nvim/plugin/0-tangerine.lua
com o conteúdo:
local function bootstrap (name, url, path)
if vim.fn.isdirectory(path) == 0 then
print(name .. ": installing in data dir...")
vim.fn.system {"git", "clone", "--depth", "1", url, path}
vim.cmd [[redraw]]
print(name .. ": finished installing")
end
end
bootstrap (
"tangerine.nvim",
"https://github.com/udayvir-singh/tangerine.nvim",
vim.fn.stdpath "data" .. "/site/pack/packer/start/tangerine.nvim"
)
bootstrap (
"hibiscus.nvim",
"https://github.com/udayvir-singh/hibiscus.nvim",
vim.fn.stdpath "data" .. "/site/pack/packer/start/hibiscus.nvim"
)
require'tangerine'.setup{
compiler = {
verbose = false,
hooks = { "onsave", "oninit" }
}
}
Essa configuração supõe que você usa o Packer para gerenciar seus plugins, se você não usa, verifique no repositório do tangerine como instalar no seu gerenciador.
Com isso, ao reiniciar o Neovim, o tangerine e o hibiscus vão ser baixados e inicializados.
Isso significa que você já pode começar a configurar o Neovim em Fennel, criando um arquivo ~/.config/nvim/init.fnl
🎉
Quando você salvar esse arquivo, o tangerine já vai compila-lo e gerar o arquivo lua para ser carregado pelo Neovim, não sendo necessário fazer nenhuma configuração adicional para isso.
Dicas para começar com Fennel
A documentação é sua amiga!
As duas melhores fontes para entender como o Fennel funciona é o tutorial e a referência do Fennel.
Vou adiantar algumas coisas simples para você já entender o básico.
Parênteses
Você vai ver muitos parênteses no Fennel, eles servem para delimitar onde uma expressão começa e termina.
Por exemplo, para declarar uma variável no escopo local, em lua, você usa a palavra chave local
, já em fennel, você chama a função local
:
(local myVar "myValue") ; myVar = "myValue"
Se o valor da variável é resultado de uma concatenação, não vamos usar o operador ..
, mas sim, a função ..
:
(local name "Fennel")
(local myVar (.. "Hello, " name)) ; myVar = "Hello, Fennel"
Resumindo, toda a função que você chamar, você vai colocar entre parênteses.
API do Neovim
Tudo o que você faz com lua, você faz com fennel, então a mesma chamada que você faz para uma API do Neovim em lua, você vai fazer em Fennel.
Isso, em lua:
-- Lua
print(vim.fn.stdpath"config")
é isso, em fennel:
(print (vim.fn.stdpath :config))
:symbol
Você já deve ter reparado que, em alguns casos, eu escrevi algumas strings em lua usando :
em fennel (se não reparou, basta olhar para o último exemplo)
Isso, basicamente, é outro jeito de escrever uma string. No entanto, para escrever nesse formato, a string não pode conter espaços.
(= :str "str") ; true
Mapeamentos do tangerine
O plugin que usamos para integrar o Fennel com o Neovim tem uns mapeamentos e comandos que nos ajudam a garantir que estamos escrevendo um código que vai gerar o que queremos. Vou listar abaixo os que eu mais uso:
gL
Pode ser executado tanto no modo normal, quando no modo visual.
Esse mapeamento mostra o código lua que o seu código em Fennel vai gerar.
Por exemplo, se eu apertar gL
após selecionar o trecho:
(lambda add [x y]
(+ x y))
Ele abre uma janela contendo o código em lua:
local function add(x, y)
_G.assert((nil ~= y), "Missing argument y on globals.fnl:1")
_G.assert((nil ~= x), "Missing argument x on globals.fnl:1")
return (x + y)
end
return add
Bem útil para checar rapidamente se o código fennel que você está escrevendo vai gerar o código lua que você espera.
Nota: Esse mapeamento não funciona muito bem no visual mode se você selecionar um trecho que usa alguma macro, a não ser que no trecho tenha a importação da macro.
gO
Esse mapeamento abre o arquivo lua compilado pelo arquivo fennel que está aberto, ou seja, se você está com o arquivo plugins.fnl
aberto e aperta gO
, ele vai abrir o arquivo plugins.lua
que foi gerado pela compilação do plugins.fnl
.
Muito útil para fazer debug.
:Fnl
Com o comando :Fnl
você consegue executar qualquer código em fennel, da mesma forma que :lua
.
:Fnl (print "hey")
Vai imprimir hey
, equivalente à :lua print("hey")
Agora é contigo
A partir daqui, você já está preparado para se divertir usando Fennel (e é divertido mesmo).
Qualquer dúvida, pergunta aqui nos comentários!
Happy Vimming!
Top comments (0)