udayvir-singh
tangerine.nvim
Fennel

🍊 Sweet Fennel integration for Neovim

Last updated Jun 24, 2026
227
Stars
21
Forks
9
Issues
0
Stars/day
Attention Score
30
Language breakdown
Fennel 79.9%
Shell 14.6%
Makefile 5.4%
β–Έ Files click to expand
README

:tangerine: Tangerine :tangerine:

Neovim version GNU Neovim in Emacs version

About β€’ Installation β€’ Setup β€’ Commands β€’ API β€’ Development

About

Tangerine provides a painless way to add fennel to your config.

Features

  • :fire: BLAZING fast, compile times in milliseconds
  • :ocean: 100% support for interactive evaluation
  • :bamboo: Control over when and how to compile
  • :ribbon: Natively loads nvim/init.fnl

Comparison to other plugins

HOTPOT :stew:

  • Abstracts too much away from the user.
  • Hooks onto lua package searchers to compile [harder to debug].
ANISEED :herb:
  • 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 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"

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 path = vim.fn.stdpath("data") .. "/site/pack/".. pack .. "/start/" .. name end

if vim.fn.isdirectory(path) == 0 then print(name .. ": installing in data dir...")

vim.fn.system {"git", "clone", url, path} if ref then vim.fn.system {"git", "-C", path, "checkout", ref} end

vim.cmd "redraw" print(name .. ": finished installing") end end

-- for stable version [recommended] bootstrap("https://github.com/udayvir-singh/tangerine.nvim", "v2.9")

-- for git head bootstrap("https://github.com/udayvir-singh/tangerine.nvim")

  • Call tangerine setup() function, see config for valid options:
-- ~/.config/nvim/plugin/0-tangerine.lua

require "tangerine".setup {}

  • Invoke :FnlCompile manually or add hooks in setup.

:tanabata_tree: Now start writing your config in ~/.config/nvim/init.fnl.

:hibiscus: Optionally you can also install hibiscus for macros.

Package Management

Only use a package manager if you haven't used ref option in bootstrap function.

Packer

(local packer (require :packer))

(packer.startup (lambda [use] (use :udayvir-singh/tangerine.nvim)))

Using hibiscus macros:

(require-macros :hibiscus.packer)

(packer-setup {}) ; bootstraps packer

(packer (use! :udayvir-singh/tangerine.nvim))

Paq

(local paq (require :paq))

(paq [ :udayvir-singh/tangerine.nvim ])

Lazy

(local lazy (require :lazy))

(lazy.setup [ :udayvir-singh/tangerine.nvim ])

Setup

Default config

Tangerine comes with sane defaults so that you can get going without having to add much to your config:

local nvim_dir = vim.fn.stdpath [[config]]

{ vimrc = nvim_dir .. "/init.fnl", source = nvim_dir .. "/fnl", target = nvim_dir .. "/lua", rtpdirs = {},

custom = { -- list of custom [source target] chunks, for example: -- {"~/.config/awesome/fnl", "~/.config/awesome/lua"} },

compiler = { float = true, -- show output in floating window clean = true, -- delete stale lua files force = false, -- disable diffing (not recommended) verbose = true, -- enable messages showing compiled files

globals = vim.tblkeys(G), -- list of alowed globals in fennel code

-- wrapper function that provides access to underlying fennel compiler -- useful if you want to modify fennel API or want to provide your own fennel compiler adviser = function (fennel) -- for example, adding a custom macro path: -- fennel["macro-path"] = fennel["macro-path"] .. ";/custom/path/?.fnl" return fennel end,

-- version of fennel to use, [ latest, 1-5-1, 1-5-0, 1-4-2, 1-4-1, 1-4-0, 1-3-1, 1-3-0, 1-2-1, 1-2-0, 1-1-0, 1-0-0 ] version = "latest",

-- hooks for tangerine to compile on: -- "onsave" run every time you save fennel file in {source} dir -- "onload" run on VimEnter event -- "oninit" run before sourcing init.fnl [recommended than onload] hooks = {} },

eval = { float = true, -- show results in floating window luafmt = function() -- function that returns formatter with flags for peeked lua -- optionally install lua-format by running $ luarocks install --local --server=https://luarocks.org/dev luaformatter return {"~/.luarocks/bin/lua-format", "--column-limit", "80"} end,

diagnostic = { virtual = true, -- show errors in virtual text timeout = 10 -- how long should the error persist } },

keymaps = { -- set them to <Nop> if you want to disable them eval_buffer = "gE", peek_buffer = "gL", goto_output = "gO", float = { next = "<C-K>", prev = "<C-J>", kill = "<Esc>", close = "<Enter>", resizef = "<C-W>=", resizeb = "<C-W>-" } },

highlight = { float = "Normal", success = "String", errors = "DiagnosticError" }, }

Example Config

Here is config that I use in my dotfiles:

{
    -- save fnl output in a separate dir, it gets automatically added to package.path
    target = vim.fn.stdpath [[data]] .. "/tangerine",

-- compile files in &rtp rtpdirs = { "plugin", "colors", "$HOME/mydir" -- absolute paths are also supported },

compiler = { -- disable popup showing compiled files verbose = false,

-- compile every time you change fennel files or on entering vim hooks = {"onsave", "oninit"} } }

Commands

Compiling

:FnlCompileBuffer

Compiles current active fennel buffer.

:FnlCompile[!]

Diff compiles all indexed fennel files.

If bang! is present then forcefully compiles all source files.

:FnlClean[!]

Deletes stale or orphaned lua files in target dir.

If bang! is present then it deletes all compiled lua files.

Evaluation

:Fnl {expr}

Executes and Evalutate {expr} of fennel.

:Fnl (print "Hello World")
  -> Hello World

:Fnl (values some_var) -> :return [ 1 2 3 4 ]

:FnlFile {file}

Evaluates {file} of fennel and outputs the result.

:FnlFile path/source.fnl

:FnlFile % ;; not recommended

:[range]FnlBuffer

Evaluates all lines or [range] in current fennel buffer.

mapped to gE by default.

Peeking

:[range]FnlPeek

Peek lua output for [range] in current fennel buffer.

mapped to gL by default.

:FnlGotoOutput

Open lua output of current fennel buffer in a new buffer.

mapped to gO by default.

Window

:FnlWinNext [N]

Jump to [N]th next floating window created by tangerine..

mapped to CTRL-K in floats by default.

:FnlWinPrev [N]

Jump to [N]th previous floating window created by tangerine.

mapped to CTRL-J in floats by default.

:FnlWinResize [N]

Increase or Decrease floating window height by [N] factor.

mapped to CTRL-W = to increase and CTRL-W - decrease by default.

:FnlWinClose

Closes current floating window under cursor.

mapped to ENTER in floats by default.

:FnlWinKill

Closes all floating windows made by tangerine.

mapped to ESC in floats by default.

FAQ

Q: How to make tangerine compile automatically when you open vim

A: add hooks in config:

require [[tangerine]].setup {
    compiler = {
        -- if you want to compile before loading init.fnl (recommended)
        hooks = {"oninit"}

-- if you want to compile after VimEnter event has fired hooks = {"onenter"} } }


Q: How to tuck away compiled output in a separate directory

A: change target in config:

require [[tangerine]].setup {
    target = "/path/to/your/dir"
}


Q: How to make impatient work with tangerine

A: bootstrap and require impatient before calling tangerine:

bootstrap "https://github.com/lewis6991/impatient.nvim"

require [[impatient]]

require [[tangerine]].setup {...}


Q: How to use lua files interchangeably with fennel files

A: lua files can simply be stored in fnl dir:

fnl
β”œβ”€β”€ options.lua
└── autocmd.fnl
; require both as normal modules in your config
(require :options)
(require :autocmd)


Q: How to fix errors in macros while migrating from hotpot

A: make sure that macro files are suffixed with -macros.fnl.

utils
β”œβ”€β”€ neovim-macros.fnl
└── packer-macros.fnl

Refer to #2 and #30 for more information

Api

By default tangerine provides the following api:

:Fnl tangerine.api

-> :return { :compile { :all (function 0) :buffer (function 1) :custom (function 2) :dir (function 3) :file (function 4) :rtp (function 5) :string (function 6) :vimrc (function 7) } :clean { :rtp (function 8) :target (function 9) :orphaned (function 10) } :eval { :buffer (function 11) :file (function 12) :peek (function 13) :string (function 14) } :win { :next (function 15) :prev (function 16) :close (function 17) :killall (function 18) :resize (function 19) } :goto_output (function 20) :serialize (function 21) }

Compiler Api

compile-string

(tangerine.api.compile.string {str} {opts?})

Compiles string {str} of fennel, returns string of lua.

Parameters:
{
  :filename <string>
  :globals  <list>
}

compile-file

(tangerine.api.compile.file {source} {target} {opts?})

Compiles fennel {source} and writes output to {target}.

Parameters:
{
  :filename <string>
  :globals  <list>
}

compile-dir

(tangerine.api.compile.dir {source} {target} {opts?})

Diff compiles files in {source} dir and outputs to {target} dir.

Parameters:
{
  :force   <boolean>
  :float   <boolean>
  :verbose <boolean>
  :globals <list>
}

{opts.force} disables diffing if set to true

Example:
(tangerine.api.compile.dir
  :path/fnl
  :path/lua
  {:force false :float true :verbose true})

compile-buffer

(tangerine.api.compile.buffer {opts?})

Compiles the current active fennel buffer.

Parameters:
{
  :float    <boolean>
  :verbose  <boolean>
  :filename <string>
  :globals  <list>
}

compile-vimrc

(tangerine.api.compile.vimrc {opts?})

Diff compiles config.vimrc to config.target dir.

Parameters:
{
  :force    <boolean>
  :float    <boolean>
  :verbose  <boolean>
  :filename <string>
  :globals  <list>
}

{opts.force} disables diffing if set to true

compile-rtp

(tangerine.api.compile.rtp {opts?})

Diff compiles fennel files in config.rtpdirs or {opts.rtpdirs}.

Parameters:
{
  :rtpdirs  <list>
  :force    <boolean>
  :float    <boolean>
  :verbose  <boolean>
  :globals  <list>
}

{opts.force} disables diffing if set to true

Example:
(tangerine.api.compile.rtp {
  :rtpdirs ["colors" "plugin" "$HOME/mydir"]
  :force   false
  :float   true
  :verbose true })

compile-custom

(tangerine.api.compile.custom {opts?})

Diff compiles fennel files indexed in config.custom or {opts.custom}.

Parameters:
{
  :custom   <list>
  :force    <boolean>
  :float    <boolean>
  :verbose  <boolean>
  :globals  <list>
}

{opts.force} disables diffing if set to true

Example:
(tangerine.api.compile.custom {
  :custom  [["~/path/fnl" "~/path/lua"]]
  :force   false
  :float   true
  :verbose true })

compile-all

(tangerine.api.compile.all {opts?})

Diff compiles all indexed fennel files in config.

Parameters:
{
  :force    <boolean>
  :float    <boolean>
  :verbose  <boolean>
  :globals  <list>
  :rtpdirs  <list>
  :custom   <list>
}

{opts.force} disables diffing if set to true

Cleaning Api

Provides functions to clean stale / orphaned lua files in target dirs.

clean-target

(tangerine.api.clean.target {source} {target} {opts?})

Deletes orphaned? {target} after comparing against {source}.

Parameters:
{
  :force <boolean>
}

{opts.force} deletes {target} without comparing if set to true

clean-rtp

(tangerine.api.clean.rtp {opts?})

Deletes all orphaned lua files in config.rtpdirs or {opts.rtpdirs}.

Parameters:
{
  :force    <boolean>
  :float    <boolean>
  :verbose  <boolean>
  :rtpdirs  <list>
}

{opts.force} deletes all compiled files if set to true

clean-orphaned

(tangerine.api.clean.orphaned {opts?})

Deletes all orphaned lua files indexed inside target dirs.

Parameters:
{
  :force    <boolean>
  :float    <boolean>
  :verbose  <boolean>
  :rtpdirs  <list>
}

{opts.force} deletes all compiled files if set to true

Evaluation Api

eval-string

(tangerine.api.eval.string {str} {opts?})

Evaluates string {str} of fennel, pretty prints the output.

Parameters:
{
  :float    <boolean>
  :virtual  <boolean>
  :filename <string>
  :offset   <number> ;; line offset for errors
}

eval-file

(tangerine.api.eval.file {path} {opts?})

Evaluates {path} of fennel, pretty prints the output.

Parameters:
{
  :float    <boolean>
  :virtual  <boolean>
  :filename <string>
}

eval-buffer

(tangerine.api.eval.buffer {start} {end} {opts?})

Evaluates lines {start} to {end} in current fennel buffer.

Parameters:
{
  :float    <boolean>
  :virtual  <boolean>
  :filename <string>
}

eval-peek

(tangerine.api.eval.peek {start} {end} {opts?})

Peek lua output for lines {start} to {end} inside a scratch buffer.

Parameters:
{
  :float    <boolean>
  :virtual  <boolean>
  :filename <string>
}

Utils Api

goto_output

(tangerine.api.goto_output)

Open lua source of current fennel buffer in a new buffer.

serialize

(tangerine.api.serialize {...})

Returns human-readable representation of {...}.

Example:
(tangerine.api.serialize example)
-> ":return [ 1 2 3 4 ]"

Windows Api

Provides functions to interact with floating windows created by tangerine.

win-next

(tangerine.api.win.next {steps?})

Switch to next floating window by 1 or N {steps?}.

win-prev

(tangerine.api.win.prev {steps?})

Switch to previous floating window by 1 or N {steps?}.

win-resize

(tangerine.api.win.resize {factor})

Changes height of current floating window by {factor} of N.

win-close

(tangerine.api.win.close)

Closes current floating window, switching to nearest neighbor afterwards.

win-killall

(tangerine.api.win.killall)

Closes all floating windows created by tangerine.

Fennel Api

fennel-load

(tangerine.fennel {version?})

Provides access to fennel compiler used by tangerine.

{version} can be one of [ "latest" "1-5-1" "1-5-0" "1-4-2" "1-4-1" "1-4-0" "1-3-1" "1-3-0" "1-2-1" "1-2-0" "1-1-0" "1-0-0" ]

Development

Requirements

| Program | Description | | --------------------------------------------------- | ----------------------------- | | pandoc | generates vimdoc | | lua | runs included fennel | | make | runs build instructions | | watchexec | build on changes (optional) | | bash | runs shell scripts | | utils | coreutils findutils gawk curl |

only GNU utils work; 9base or busybox should not work

Building from source

git clone https://github.com/udayvir-singh/tangerine.nvim
cd tangerine.nvim

make <git-hooks> make <target>

Refer to make help or below for information on targets.

Make Targets

| Target | Description | | ------------- | -------------------------------------------- | | fnl | compiles fennel files | | deps | copy required deps in lua folder | | vimdoc | runs panvimdoc to generate vimdocs | | fnldoc | generates module level documentation | | | | | build | combines fnl deps vimdoc fnldoc | | watch-build | watches source dir, runs :build on changes | | | | | clean | deletes build and install dir | | install | install tangerine on this system | | | | | runner | compiles test runner library | | test | runs unit tests, will erase nvim config |

To build tangerine run:

$ make clean build

or

$ make watch-build

To install tangerine run:

$ make install

Git Hooks

| Target | Description | | ------------ | -------------------------------------------------------------- | | git-pull | safely fetches git repo, prevents conflicts with local changes | | git-skip | makes git ignore changes to build files | | git-unskip | reverts git-skip, makes build files trackable |

Example workflow:
$ make git-skip # first thing that you should be running

makes changes to tangerine

$ ... $ make clean build

commit changes

$ git commit -a -m "<msg>"

cleanly fetch from origin

$ make git-pull

LOC Helpers

Helpers to generate detailed summary about lines of code in source files:

$ make loc-{language}
Supported Languages:
  • fennel / test
  • bash
  • markdown
  • makefile
  • yaml
Examples:
$ make loc-fennel

$ make loc-bash


:: γŠγ‚Œγ¨γ—γ¦η™½ηœΌγγ‚‰γ™γ‚‹θ›™γ‹γͺ ::

πŸ”— More in this category

Β© 2026 GitRepoTrend Β· udayvir-singh/tangerine.nvim Β· Updated daily from GitHub