GithubHelp home page GithubHelp logo

zest.nvim's Introduction

zest.nvim

These are your father's parentheses.
Elegant weapons for a more... civilized age.
xkcd/297

An opinionated library of macros that aims to streamline the process of configuring neovim with fennel, a lisp that compiles to lua.

For a full config example, see my dotfiles.

a short pitch

  • Provide a syntactically sweet way of interacting with select parts of lua api
  • Seamlessly integrate lua functions into keymaps, autocmds, etc
  • Be primarily a library of macros, do as much as possible at compile time
  • Output code that is readable and efficient
  • Remain compatible with everything, yet standalone

WIP If you have any feedback or ideas on how to improve zest, please share them with me! You can reach me in an issue or at @tsbohc on the conjure discord.

Deprecation notice I'll be overhauling the macros some time later this month. The old macros will stay for a while though. The -fn macros will be merged into regular ones.

setup

as a companion library

If you're already using a plugin that integrates fennel into neovim, such as aniseed or hotpot, follow these instructions:

  • Install with your favourite package manager
(use :tsbohc/zest.nvim)
  • Before using any of the macros, run zest.setup with no arguments
(let [zest (require :zest)]
  (zest.setup))
  • Import and alias the macros you wish to use in the current file
(import-macros
  {:opt-prepend opt^} :zest.macros)

standalone

When installed on its own, zest can be configured to mirror the source directory tree to target. When a relevant file is saved, zest will display a message and recompile it.

Unless configured, zest will not initialise its compiler.

Show an example of a standalone configuration
(let [zest (require :zest)
      h vim.env.HOME]
  (zest.setup
    {:target (.. h "/.garden/etc/nvim.d/lua")
     :source (.. h "/.garden/etc/nvim.d/fnl")
     :verbose-compiler true
     :disable-compiler false}))

macros

In each example, the top block contains the fennel code written in the configuration, while the bottom one shows the lua code that neovim will execute.

The examples are refreshed with every change to zest and are always up to date.

vlua

  • Store a function and return its v:lua, excluding the parentheses
(local v (vlua my_fn))
show lua
local v
do
  local ZEST_N_0_ = _G._zest.v["#"]
  local ZEST_ID_0_ = ("_" .. ZEST_N_0_)
  _G._zest["v"][ZEST_ID_0_] = my_fn
  _G._zest["v"]["#"] = (ZEST_N_0_ + 1)
  v = ("v:lua._zest.v." .. ZEST_ID_0_)
end

vlua-format

  • A string.format wrapper for vlua
(vim.cmd
  (vlua-format
    ":com -nargs=* Mycmd :call %s(<f-args>)"
    (fn [...]
      (print ...))))
show lua
local function _0_(...)
  local ZEST_N_0_ = _G._zest.v["#"]
  local ZEST_ID_0_ = ("_" .. ZEST_N_0_)
  local function _1_(...)
    return print(...)
  end
  _G._zest["v"][ZEST_ID_0_] = _1_
  _G._zest["v"]["#"] = (ZEST_N_0_ + 1)
  return ("v:lua._zest.v." .. ZEST_ID_0_)
end
vim.cmd(string.format(":com -nargs=* Mycmd :call %s(<f-args>)", _0_(...)))

options

  • A complete vim.opt wrapper
(opt-local-append completeopt ["menuone" "noselect"])
show lua
do end (vim.opt_local.completeopt):append({"menuone", "noselect"})

Full list of opt- macros:

opt-set      opt-local-set      opt-global-set
opt-get      opt-local-get      opt-global-get
opt-append   opt-local-append   opt-global-append
opt-prepend  opt-local-prepend  opt-global-prepend
opt-remove   opt-local-remove   opt-global-remove

keymaps

def-keymap

  • Map literals
(def-keymap :H [nv] "0")
show lua
do
  local ZEST_OPTS_0_ = {noremap = true}
  vim.api.nvim_set_keymap("n", "H", "0", ZEST_OPTS_0_)
  vim.api.nvim_set_keymap("v", "H", "0", ZEST_OPTS_0_)
end
  • Map lua expressions
(each [_ k (ipairs [:h :j :k :l])]
  (def-keymap (.. "<c-" k ">") [n] (.. "<c-w>" k)))
show lua
for _, k in ipairs({"h", "j", "k", "l"}) do
  vim.api.nvim_set_keymap("n", ("<c-" .. k .. ">"), ("<c-w>" .. k), {noremap = true})
end
  • Map pairs
(def-keymap [n]
  {:<ScrollWheelUp>   "<c-y>"
   :<ScrollWheelDown> "<c-e>"})
show lua
do
  local ZEST_OPTS_0_ = {noremap = true}
  vim.api.nvim_set_keymap("n", "<ScrollWheelUp>", "<c-y>", ZEST_OPTS_0_)
  vim.api.nvim_set_keymap("n", "<ScrollWheelDown>", "<c-e>", ZEST_OPTS_0_)
end

To disable noremap, include :remap after the modes.

def-keymap-fn

  • Define a function and map it to a key
(def-keymap-fn :<c-m> [n]
  (print "hello from fennel!"))
show lua
do
  local ZEST_VLUA_0_
  do
    local ZEST_ID_0_ = "_60_99_45_109_62_110_"
    local function _0_()
      return print("hello from fennel!")
    end
    _G._zest["keymap"][ZEST_ID_0_] = _0_
    ZEST_VLUA_0_ = ("v:lua._zest.keymap." .. ZEST_ID_0_)
  end
  local ZEST_RHS_0_ = (":call " .. ZEST_VLUA_0_ .. "()<cr>")
  vim.api.nvim_set_keymap("n", "<c-m>", ZEST_RHS_0_, {noremap = true})
end
  • Define an expression as a function
(def-keymap-fn :k [nv :expr]
  (if (> vim.v.count 0) "k" "gk"))
show lua
do
  local ZEST_VLUA_0_
  do
    local ZEST_ID_0_ = "_107_110_118_"
    local function _0_()
      if (vim.v.count > 0) then
        return "k"
      else
        return "gk"
      end
    end
    _G._zest["keymap"][ZEST_ID_0_] = _0_
    ZEST_VLUA_0_ = ("v:lua._zest.keymap." .. ZEST_ID_0_)
  end
  local ZEST_RHS_0_ = (ZEST_VLUA_0_ .. "()")
  local ZEST_OPTS_0_ = {expr = true, noremap = true}
  vim.api.nvim_set_keymap("n", "k", ZEST_RHS_0_, ZEST_OPTS_0_)
  vim.api.nvim_set_keymap("v", "k", ZEST_RHS_0_, ZEST_OPTS_0_)
end

autocmds

def-augroup

  • Define an augroup with autocmd! included
(def-augroup :my-augroup)
show lua
do
  vim.cmd("augroup my-augroup")
  vim.cmd("autocmd!")
  vim.cmd("augroup END")
end

def-autocmd

  • Define an autocommand
(def-autocmd [:BufNewFile my_event] [:*.html :*.xml]
  "setlocal nowrap")
show lua
vim.cmd(("au " .. table.concat({"BufNewFile", my_event}, ",") .. " *.html,*.xml setlocal nowrap"))

def-autocmd-fn

  • Define a function and bind it as an autocommand
(def-augroup :restore-position
  (def-autocmd-fn :BufReadPost "*"
    (when (and (> (vim.fn.line "'\"") 1)
               (<= (vim.fn.line "'\"") (vim.fn.line "$")))
      (vim.cmd "normal! g'\""))))
show lua
do
  vim.cmd("augroup restore-position")
  vim.cmd("autocmd!")
  do
    local ZEST_VLUA_0_
    do
      local ZEST_N_0_ = _G._zest.autocmd["#"]
      local ZEST_ID_0_ = ("_" .. ZEST_N_0_)
      local function _0_()
        if ((vim.fn.line("'\"") > 1) and (vim.fn.line("'\"") <= vim.fn.line("$"))) then
          return vim.cmd("normal! g'\"")
        end
      end
      _G._zest["autocmd"][ZEST_ID_0_] = _0_
      _G._zest["autocmd"]["#"] = (ZEST_N_0_ + 1)
      ZEST_VLUA_0_ = ("v:lua._zest.autocmd." .. ZEST_ID_0_)
    end
    vim.cmd(("autocmd BufReadPost * :call " .. ZEST_VLUA_0_ .. "()"))
  end
  vim.cmd("augroup END")
end

def-augroup-dirty

  • Define an augroup without autocmd!
(def-augroup-dirty :my-dirty-augroup)
show lua
do
  vim.cmd("augroup my-dirty-augroup")
  vim.cmd("augroup END")
end

commands

def-command-fn

  • Assign a function to an ex command
(def-command-fn :MyCmd [...]
  (print ...))
show lua
do
  local ZEST_VLUA_0_
  do
    local ZEST_ID_0_ = "_77_121_67_109_100_"
    local function _0_(...)
      return print(...)
    end
    _G._zest["command"][ZEST_ID_0_] = _0_
    ZEST_VLUA_0_ = ("v:lua._zest.command." .. ZEST_ID_0_)
  end
  vim.cmd(("command -nargs=* MyCmd :call " .. ZEST_VLUA_0_ .. "(<f-args>)"))
end

Arguments are handled automatically like so:

[]       -nargs=0    --
[x]      -nargs=1 <q-args>
[...]    -nargs=* <f-args>
[x ...]  -nargs=* <f-args>
[x y]    -nargs=* <f-args>

notes

a tale of two macros

At compile time, there is no good way of knowing if a variable contains a function or a string. I think so, at least (enlighten me!). This means that the type of the argument has to be supplied to the macro explicitly.

This is the reason for the having both def-keymap and def-keymap-fn, for example.

That said, def-keymap and others can accept functions if they have been wrapped in vlua:

(fn my-fn []
  (print "dinosaurs"))

(def-keymap :<c-m> [n]
  (vlua-format
    ":call %s()<cr>"
    my-fn))

text objects

When it comes to defining text objects, they can be considered fancy keymaps. Here're the definitions of inner line and around line:

(def-keymap :il [xo :silent]
  (string.format ":<c-u>normal! %s<cr>"
    "g_v^"))
(def-keymap :al [xo :silent]
  (vlua-format ":<c-u>call %s()<cr>"
    (fn [] (vim.cmd "normal! $v0"))))

text operators

Text operators are the fanciest of keymaps. Here's a minimal example:

(fn def-operator [k f]
  (let [v-lua (vlua f)]
    (def-keymap k [n :silent] (string.format ":set operatorfunc=%s<cr>g@" v-lua))
    (def-keymap k [v :silent] (string.format ":<c-u>call %s(visualmode())<cr>" v-lua))
    (def-keymap (.. k k) [n :silent] (string.format ":<c-u>call %s(v:count1)<cr>" v-lua))))

(def-operator :q
  (fn [x] (print x))

complex autocmds

If you want to create complex autocmds, use vlua:

(vim.cmd
  (vlua-format
    (.. ":autocmd " ponder " * <buffer=42> ++once :call %s()")
    print-answer))

thanks

zest embeds fennel.lua -- I do not claim any ownership over this file

zest.nvim's People

Contributors

tsbohc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

olical raw1z

zest.nvim's Issues

def-keymap invalid variable names

The ZEST_OPTS_X_auto variable names use an incremented X value.
For example:

(ki- [n] 
 {:H "^"
  :L "$"})

generates

local ZEST_OPTS_19_auto = {noremap = true}
vim.api.nvim_set_keymap("n", "L", "$", ZEST_OPTS_18_auto)
return vim.api.nvim_set_keymap("n", "H", "^", ZEST_OPTS_18_auto)

def-keymap-fn throws error

def-keymap works but def-keymap-fn throws this:

; Sponsored by @rafaeldelboni ❤
; --------------------------------------------------------------------------------
; eval (file): ...s/nvim/.config/nvim/fnl/dotfiles/mappings.fnl
; Compile error in /home/p00f/.local/share/nvim/site/pack/packer/start/zest.nvim/fnl/zest/macros.fnl:117
;   unknown global in strict mode: ZEST_VLUA_23_auto
; 
;               `(.. ":call " ZEST_VLUA# "()<cr>"))
; * Try looking to see if there's a typo.
; * Try using the _G table instead, eg. _G.ZEST_VLUA_23_auto if you really want a global.
; * Try moving this code to somewhere that ZEST_VLUA_23_auto is in scope.
; * Try binding ZEST_VLUA_23_auto as a local in the scope of this code.

when i do

(import-macros {:def-keymap-fn fmap
                :def-keymap kmap} :zest.macros)
(kmap :<C-n> [n] ":bnext<CR>")
(fmap :de [n] (print 1))

Some enhancements that i found.

tools

Found some fennel tools in compiler's wiki. Ones that i use are:

code

Found way to check if arg to macro is a function at compile time.

(fn function? [f]
  "checks if a 'f' is function."
  (let [ref (?. f 1 1)]
    (or (= ref :hashfn) 
        (= ref :fn))))

how it works:

Fennel Passes variable as table:

{ "hashfn", -- it indexes this val.
    byteend = 78,
    bytestart = 73,
    line = 4,
    prefix = "hashfn",
    <metatable> = <1>{ "SYMBOL",
      __eq = <function 1>,
      __fennelview = <function 2>,                                      __lt = <function 3>,
      __tostring = <function 2>
   }
}

Caveats

Won't work if function is indirectly passed.
Eg-

(fn name [] 
  (do-something))

(macro-name name) ;; will not work

(macro-name #(name)) ;; will work

example of macro taken from my dotfiles.

(fn M.map- [args fs ts]
  (let [(modes opts) (keymap-options args)
        fs (tostring fs)]
    (if (function? ts)
        (def-fn-map modes fs ts opts)
        (def-map modes fs (tostring ts) opts))))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.