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.
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.luamyVar='this is global'-- init.lualocalmyVal='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.
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.
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:
localfunctionbootstrap(name,url,path)ifvim.fn.isdirectory(path)==0thenprint(name..": installing in data dir...")vim.fn.system{"git","clone","--depth","1",url,path}vim.cmd[[redraw]]print(name..": finished installing")endendbootstrap("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çãolocal:
(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..:
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:
-- Luaprint(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:
localfunctionadd(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)endreturnadd
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).