DEV Community

Cover image for Configurando Neovim com Fennel
Douglas Massolari
Douglas Massolari

Posted on • Edited on

Configurando Neovim com Fennel

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Em fennel, podemos, simplesmente, fazer:

; Fennel
(local {: var : var2} (require :module))
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Enquanto, em fennel, podemos usar o match:

; Fennel
(lambda get-desc [key]
  (match key
    :k1 "Key 1"
    :k2 "Key 2"
    :k3 "Key 3"))
Enter fullscreen mode Exit fullscreen mode

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:

  1. tangerine

    GitHub logo udayvir-singh / tangerine.nvim

    🍊 Sweet Fennel integration for Neovim

    🍊 Tangerine 🍊

    Neovim version GNU Neovim in Emacs version

    AboutInstallationSetupCommandsAPIDevelopment

    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

    1. Create file plugin/0-tangerine.lua to bootstrap tangerine:

    Important

    If you are using lazy.nvim then you should create init.lua instead of plugin/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
    Enter fullscreen mode Exit fullscreen mode
  2. hibiscus




    GitHub logo

    udayvir-singh
    /
    hibiscus.nvim



    🌺 Flavored Fennel Macros for Neovim




    Hibiscus.nvim

    🌺 Highly opinionated macros to elegantly write your neovim config.

    Companion library for tangerine but it can also be used standalone.

    Neovim version

    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
    Enter fullscreen mode Exit fullscreen mode

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" }
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

é isso, em fennel:

(print (vim.fn.stdpath :config))
Enter fullscreen mode Exit fullscreen mode

: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
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)