This is a collection of macros intended to the functionality of various Neovim related Fennel needs. Neovim based macros are found in macros/nvim
, with the rest of the macro groups being self explanatory.
These macros are considered deprecated in full. Please use the katcros v2 repo for updated macros with complete syntax. This readme is updated solely for the functionality found here.
Macros available are intended to keep with Fennel style conventions as much as possible. The conventions should be understandable at a glance, but for clarity in the project they will be described here.
The Neovim API is generally very clear in its naming on what each API function acheives. nvim_del
is for global deletion operations, nvim_win_del
for window deletion operations, and so on. In order to maintain this level of explicit clarity, I have used a clear naming convention for each of these base operations:
Convention | Operation | Description |
---|---|---|
cre- |
create | For creation of objects, but not return of one |
def- |
definition | For definition of created objects, returns a value |
cle- |
clearing | For clearing of an object's values, but not deletion |
del- |
deletion | For deleting an object entirely |
set- |
setter | For setting the value of an object |
get- |
getter | For getting the value of an object |
do- |
execution | Execute the object |
;; No returned value, cannot be manipulated by itself
;; "cre-command usercommand"
(cre-command :UserCommand (fn [] (print "Hello world")) "Usercomamnd hello world")
;; Returns a value, can be manipulated with 'autocmd' variable
;; "def-command usercommand"
(local user-command (def-command :UserCommand (fn [] (print "Hello world")) "BufEnter hello world")
;; Clear out all "BufEnter" autocommands
;; "cle-awk event bufenter"
(cle-auc! {:event :BufEnter})
;; Delete 'autocmd' autocommand explicitly
;; "del-awk autocmd"
(del-auc! autocmd)
;; Set option 'number'
;; "set-opt number true"
(set-opt number true)
;; Get value of option 'number'
;; "get-opt number"
(get-opt number)
;; Do VimL function
;; "do-vim-el"
(do-viml has "nvim-0.7")
Each convention prefix is meant to pronounced literally, for instance cre-command
is pronounced as "kree-command".
In Fennel's style guide, !
as a suffix refers to file-system operations. I have extended this to imply state updates. cle-
and del-
are explicit updates of Neovim's state, and thus will always be suffixed by !
. This character can be pronounced as "bang". cle-auc!
would be pronounced as "kle-awk-bang".
-
as a suffix implies a shortening from Vim/Neovim functions. For instance, nno-
would not be pronounced as 'en-en-oh', but as "normal mode remap" from nnoremap
. The use of these is minimized where appropriate, and likely will be removed entirely from these macros at a future date.
In Fennel's style guide, ->
is used for conversion functions. For instance taking a string and making it into a sequential table would be string->table
. ->
thus gets read as "to", making this function "string to table". This convention is used where appropriate.
Conversely, <-
becomes "from", and implies a source to look from or a file to read from if it is a file system operation. A function to clear an autocommand specifically from a list of events would be cle-auc<-event!
, which would be read as "kle-awk-from-event". This is generally used when one only wishes to do 1 level of search.
In order to maintain consistency, macros here have an expectation of signature behavior. It varies a bit from macro type to macro type.
(cre-macro immediate-info command ?description ?args-table)
For immediate-info
, this varies immensely based on the macro in use. For mappings macros, this is actually 2 symbols/lists:
(map- mode-table left-hand-side command ?description ?args-table)
For an autocommand macro, it is also 2 symbols/lists. It is kept this way to maintain the general idea of how the Vimscript versions of these functions work overall, rather than go out of the way to make a new syntax.
Additionally, the description and/or args-table are always optionally if desired. If either are passed nil then the other will still function as expected.
immediate-info
(generally) must end up as a string, you can pass a symbol or list that returns a string if needed.
(let [user-command "MyReallyAwesomeAndVeryLongUserCommand"]
(cre-command user-command (fn [] (print "hi"))))
This macro behaves the same as the equivalent creation macro, but returns a value. This is useful if you want to further manipulate said values:
(let [user-command (def-command "UserCommand" (fn [] (print "hi")))]
(del-command! user-command))
For these macros, a "deletion" is specifically about removing the entire object. After the expanded macro is run, the object passed will not exist according to Neovim. A "clearing", on the other hand, will only remove the values present for the object. Neovim will still be aware of the object:
(cle-auc<-event! :BufEnter) ; clear any autocommands found for BufEnter
(del-command! "UserCommand") ; delete the entire user command "UserCommand"
Deletion/clearing will depend on how the macro in use works, and is in part determined by the underlying API (e.g. nvim_clear_autocmds
vs nvim_del_user_command
).
The argument passed depends on the macro in question, although it is usually self-evident.
"Get" macros are designed to get the value of some object. This is purely a data access. The data type returned depends on the macro:
(get-auc {:group "SomeCoolGroup"}) ; get autocommands from group "SomeCoolGroup"
(print (get-opt mouse)) ; print the value of option "mouse"
"Set" macros are designed to set the value of some object. This (mostly, see below) implies that the value exists. This is mostly for options macros.
(set-opt mouse true)
(set-var :g :cool_var true)
Note that due to how Vim variables work, this macro is slightly incongruent in that the variable does not need to exist before being set. This behavior may change in the future.
The do-
macros that interface with vim.cmd
have the ability to take a key value table that expands to the proper key=value
syntax for said functions. The following example will expand to:
(do-ex highlight :Normal {:guifg :white})
(vim.cmd {:cmd "highlight" :args [Normal "guifg=white"] :output true})
All do-
macros can take any amount of arguments as needed by the function.
This macro wraps any 0/1 boolean VimL function and outputs a proper Fennel boolean for you. See the example:
(if (do-viml filereadable :test.txt) (print "is readable"))
(if (= (vim.fn.filereadable :test.txt) 1) (print "is readable"))
These macros deal with interfacing with Neovim directly. They are split up accordingly
Handles key maps, attempts to mimic the simplicity of Vimscript's syntax. These all function similarly. A left-hand-side and right-hand-side argument are always needed, but a description and options table is optional, with the options table always being last.
; options table, no description
(map-macro lhs rhs {:buffer true})
; expansion
(vim.keymap.set :mode lhs rhs {:buffer true})
; description and options table
(map-macro lhs rhs "Map description" {:buffer true})
; expansion
(vim.keymap.set :mode lhs rhs {:buffer true :desc "Map description"})
; description, no options table
(map-macro lhs rhs "Map description")
; expansion
(vim.keymap.set :mode lhs rhs {:desc "Map description"})
; no other arguments
(map-macro lhs rhs)
; expansion
(vim.keymap.set :mode lhs rhs)
Note that the mode is not explicitly set by an argument. This is to mimic Vimscript further.
Macro | Mode | NoRemap |
---|---|---|
nno- |
Normal | true |
ino- |
Insert | true |
vno- |
Visual | true |
tno- |
Terminal | true |
cno- |
Command-line | true |
ono- |
Operator | true |
nm- |
Normal | false |
im- |
Insert | false |
vm- |
Visual | false |
tm- |
Terminal | false |
cm- |
Command-line | false |
om- |
Operator | false |
nomap- |
Multiple | true |
map- |
Multiple | false |
map-
and nomap-
take a sequential table of character strings for the corresponding mode. This allows you to map across multiple modes, and are the only macros in which the mode is required.
; macro forms
(nomap- [:n :v] ";" ":" "Swap char search and command-line enter")
(nomap- [:n :v] ":" ";" "Swap command-line enter and char search")
(nno- :<leader>r :<C-l> "Return <C-l> functionality")
(fn files [opts]
((. (require :fzf-lua) :files) opts))
(nno- :<leader>f (fn [] (files)) "Open FZF file window" {:silent true})
; expanded forms
(vim.keymap.set [:n :v] ";" ":" {:desc "Swap char search and command-line enter"})
(vim.keymap.set [:n :v] ":" ";" {:desc "Swap command-line enter and char search"})
(vim.keymap.set :n :<leader>r :<C-l> {:desc "Return <C-l> functionality"})
(fn files [opts]
((. (require :fzf-lua) :files) opts))
(vim.keymap.set :n :<leader>f (fn [] (files)) {:silent true :desc "Open FZF file window"})
Handles various utilities that aren't associated with any specific grouping
Macros allow full manipulation of user-commands.
Creates a user-command:
(cre-command command-name command description args-table)
If the command is to be Vimscript, it must be passed as an entirely enclosed string. A Lua function reference or a defined function can be used.
(cre-command :ReallyCoolCommand (fn [] (print "butts"))
"Does some really cool things"
{:nargs 0})
(fn some-function [args] (print (vim.inspect args)))
(cre-command :PrintCommandArgs some-function {:nargs :*})
(cre-command :ChangeColorscheme "colorscheme blue"
"Changes colors to blue")
(vim.api.nvim_create_user_command command-name command {:desc description arg-key arg-value})
;; macro form
(fn files [opts]
((. (require :fzf-lua) :files) opts))
(cre-command :FZFOpenFile (fn [] (files)) "Open files")
;; expansion
(fn files [opts]
((. (require :fzf-lua) :files) opts))
(vim.api.nvim_create_user_command :FZFOpenFile (fn [] (files)) {:desc "Open files"})
Defines a user-command:
(def-command command-name command ?description ?args-table)
This macro behaves the same as cre-command
, but returns command-name
to be passed to variables. If the returned value is not needed, use cre-command
instead.
(do (vim.api.nvim_create_user_command command-name command {:desc description arg-key arg-value})
command-name)
;; macro form
(local user-command (def-command :TempUserCommand
(fn [] (print "hello"))
{:buffer true}))
;; expansion
(local user-command (do (vim.api.nvim_create_user_command :TempUserCommand
(fn [] (print "hello"))
{:buffer 0})
:TempUserCommand))
Deletes a user-command:
(del-command command-name ?buffer)
User-commands created for a buffer must pass ?buffer
due to limitations in the API.
(del-command command-name)
(vim.api.nvim_del_user_command command-name)
(del-command command-name true)
(vim.api.nvim_buf_del_user_command command-name 0)
(del-command command-name buffer)
(vim.api.nvim_buf_del_user_command command buffer)
Runs a user command:
(do-command command-name args)
(do-command command-name)
(vim.cmd {:cmd command-name :output true})
(do-command command-name :arg)
(vim.cmd {:cmd command-name :args [:arg] :output true})
Runs a Ex command:
(do-ex function :arg)
; expansion
(vim.cmd {:cmd :function :args [:arg] :output true})
(do-ex function :arg {:key :value})
(vim.cmd {:cmd :function :args [:arg "key=value"] :output true})
Run a VimL function, returning a boolean for boolean returning functions:
(do-viml function)
((. vim.fn "function"))
(do-viml did_filetype)
(do (let [result_2_auto ((. vim.fn "did_filetype"))] (if (= result_2_auto 0) false true)))
(do-viml expand "%" vim.v.true)
((. vim.fn "expand") "%" vim.v.true)
(do-viml has :nvim)
(do (let [result_2_auto ((. vim.fn "has") "arg")] (if (= result_2_auto 0) false true)))
Handles color management.
Set a colorscheme.
(col- "kat.nvim")
; expansion
(vim.cmd "colorscheme kat.nvim")
Handles setting of options and scoped variables. All option macros can take a flag for append
, prepend
and remove
to keep all functionality. These flags are always the last option of the macro.
To simplify syntax, there are macros for setting only 1 option:
This sets a single option, not keeping in mind scope of the option (global & local)
(set-opt option value)
(tset vim.opt "option" value)
(set-opt option value :append)
(: (. vim.opt "option") "append" value)
This sets an option that has the buf
or win
scope as a local option. Returns an error if a global option is passed.
(set-local-opt spell true)
(tset vim.opt_local "spell" true)
(set-local-opt spell true :append)
(: (. vim.opt_local "spell") "append" true)
This sets an option that has the global
scope as a global option. Returns an error if a local option is passed.
(set-global-opt mouse :nvi)
(tset vim.opt_global "mouse" "nvi")
(set-global-opt mouse :nvi :append)
(: (. vim.opt_global "mouse") "append" "nvi")
This automatically sets an option as either local or global.
(set-opt-auto mouse :nvi)
(tset vim.opt_global "mouse" "nvi")
(set-opt-auto spell true)
(tset vim.opt_local "spell" true)
These macros set multiple of options, following the same rules as the mono-option macros. These macros are sorted to always return the same order of operations. These are not sorted as the passed options, they are sorted with table.sort
:
(set-opts {spell true mouse :nvi})
(do (tset vim.opt "spell" true) (do (tset vim.opt "mouse" "nvi")))
set-opts
set-local-opts
set-global-opts
set-opts-auto
Gets an option.
(get-opt spell)
(: (. vim.opt "spell") "get")
Sets a vim variable. Supports scope indexing. When using a scope index, returns an error on compilation if anything other than b
, w
, or t
scope is used.
(set-var :g variable "Value")
(tset (. vim "g") "variable" "Value")
(set-var (. :b 1) variable "Value")
(tset (. (. vim "b") 1) "variable" "Value")
Sets multiple vim variable using a key/value table. Supports scope indexing. When using a scope index, returns an error on compilation if anything other than b
, w
, or t
scope is used.
(option.set-vars :g {:variable_1 "Value" :variable_2 true})
(do (tset (. vim "g") "variable_2" true) (tset (. vim "g") "variable_1" "Value"))
(option.set-vars (. :b 1) {:variable_1 "Value" :variable_2 true})
(do (tset (. (. vim "b") 1) "variable_2" true) (tset (. vim "g") "variable_1" "Value"))
Gets a vim variable. Supports scope indexing. When using a scope index, returns an error on compilation if anything other than b
, w
, or t
scope is used.
(get-var :g variable)
(. (. vim "g") "variable")
(get-var (. :b 1) variable)
(. (. (. vim "b") 1) "variable")
Handles dealing with autocommands and autogroups.
Defines an autogroup to be returned for a variable.
(local augroup (def-aug- "GroupName" true))
; expansion
(local augroup (vim.api.nvim_create_augroup "GroupName" {:clear false}))
The boolean is optional. It inverts the functionality of nvim_create_augroup
. If set to false or nil (i.e. no argument after the name), then the default behavior of nvim_create_augroup
will be used. This means augroups will clear upon each call.
Creates an autocommand. It is not designed to be accepted by a variable for manipulation. Like maps and user command macros, it can take an optional description and options table. The events, pattern, and callback are required.
; no description or options table, lua callback
(auc- :Event "*.file" (fn [] (print "callback")))
; expansion
(vim.api.nvim_create_autocmd :Event {:pattern "*.file" :callback (fn [] (print "callback"))})
; description, no options table, vimscript callback
(auc- :Event "*.file" "echo 'command'" "Autocmd description")
; expansion
(vim.api.nvim_create_autocmd :Event {:pattern "*.file" :command "echo 'command'" :desc "Autocmd description"})
; no description, options table, called lua callback
(fn [] auto-callback (print "called function"))
(auc- :Event "*.file" auto-callback {:buffer 0})
; expansion
(vim.api.nvim_create_autocmd :Event {:pattern "*.file" :callback auto-callback :buffer 0})
; description and options table, called lua callback
(fn [] auto-callback (print "called function"))
(auc- :Event "*.file" auto-callback "Autocmd description" {:buffer 0})
; expansion
(vim.api.nvim_create_autocmd :Event {:pattern "*.file" :callback auto-callback :desc "Autocmd description" :buffer 0})
Absorbs auc-
calls within its list, and injects the group throughout each. Only accepts auc-
after the group variable.
(aug- group
(auc- :Event "*" (fn [] (print "callback"))))
; expansion minus auc-
(auc- :Event "*" (fn [] (print "callback")) {:group group})
; full expansion
(vim.api.nvim_create_autocmd :Event {:pattern "*" :callback (fn [] (print "callback")) :group group})
The group must have been previously defined, it cannot be passed through with this macro. For additional notice, only auc-
calls are accepted. Attempting to use anything else will result in a compile-time error. This is not a way to be programmatic about autocommand creation, it is only equivalent to the ->
threading macros in function. Any programmatic work of autocommands must be done in a list outside of this scope.
Clears an autocommand using vim.api.nvim_clear_autocommands
.
(cle-auc! (:buffer 0})
(vim.api.nivm_clear_autocmds {:buffer 0})
Takes the appropriate type, single and plural, and only that type and clears the autocommands using vim.api.nvim_clear_autocommands
cle-auc<-event!
: takes a string or sequential table of strings corresponding to event namecle-auc<-pattern!
: takes a string or sequential table of strings corresponding to a patterncle-auc<-buffer!
: takes a int buffer numbercle-auc<-group!
: takes a string or sequential table of strings corresponding to group name
(cle-auc<-event! :Event)
(vim.api.nvim_clear_autocmd {:event "Event"})
Clears an autocommand using vim.api.nvim_get_autocmds
.
(get-auc! (:buffer 0})
(vim.api.nivm_get_autocmds {:buffer 0})
Takes the appropriate type, single and plural, and only that type and gets the autocommands using vim.api.nvim_get_autocmds
get-auc<-event!
: takes a string or sequential table of strings corresponding to event nameget-auc<-pattern!
: takes a string or sequential table of strings corresponding to a patternget-auc<-group!
: takes a string or sequential table of strings corresponding to group name
(get-auc<-event! :Event)
(vim.api.nvim_get_autocmds {:event "Event"})
Deletes an autogroup by name or by id.
(del-aug! :Group)
(vim.api.nvim_del_augroup_by_name "Group")
(del-aug! 0)
(vim.api.nvim_del_augroup_by_id 0)
Fires an autocmd using vim.api.nvim_exec_autocmds
.
(do-auc :Event)
(vim.api.nvim_exec_autocmds "Event" {})
(do-auc [:Event1 :Event2] {:group :Group})
(vim.api.nvim_exec_autocmds ["Event1" "Event2"] {:group "Group"})
; macro form
(let [highlight (def-aug- "highlightOnYank")]
(aug- highlight
(auc- "TextYankPost" :*
(fn [] ((. (require :vim.highlight) :on_yank)))
"Highlight yank region")))
(let [terminal (def-aug- "terminalSettings")]
(aug- terminal
(auc- "TermOpen" :* (fn [] (setl- number false)) "No number")
(auc- "TermOpen" :* (fn [] (setl- relativenumber false)) "No relative number")
(auc- "TermOpen" :* (fn [] (setl- spell false)) "No spell")
(auc- "TermOpen" :* (fn [] (setl- bufhidden :hide)) "Bufhidden")))
; expansion
(let [highlight (vim.api.nvim_create_augroup "highlightOnYank" {:clear true})]
(vim.api.nvim_create_autocmd "TextYankPost"
{:pattern :*
:callback (fn [] ((. (require :vim.highlight) :on_yank)))
:desc "Highlight yank region"
:group highlight}))
(let [terminal (vim.api.nvim_create_augroup "terminalSettings" {:clear true})]
(vim.api.nvim_create_autocmd "TermOpen"
{:pattern :*
:callback (fn [] (setl- number false))
:group terminal
:desc "No number"})
(vim.api.nvim_create_autocmd "TermOpen"
{:pattern :*
:callback (fn [] (setl- relativenumber false))
:group terminal
:desc "No relative number"})
(vim.api.nvim_create_autocmd "TermOpen"
{:pattern :*
:callback (fn [] (setl- spell false))
:group terminal
:desc "No spell"})
(vim.api.nvim_create_autocmd "TermOpen"
{:pattern :*
:callback (fn [] (setl- bufhidden :hide))
:group terminal
:desc "Bufhidden"})))
These macros modify some usage for packer.nvim, mostly for my own preferences.
Condenses the standard packer setup into a one line call, does not support configuring packer.nvim in the same call at the moment.
(plugInit
(use :wbthomason/packer.nvim)
(use :Olical/aniseed)
(use :lewis6991/impatient.nvim))
; expansion
((. (require :packer) :startup)
(fn []
(do
(use :wbthomason/packer.nvim)
(use :Olical/aniseed)
(use :lewis6991/impatient.nvim))))
Compiles to use
. No other use than an alias.
These macros are attempts at adapting forms to something a bit more Lisp-like, since Fennel compiles to Lua.
Replaces the standard table value lookup.
; with a passed value
(opt- origin lookup {:value true})
; expansion
((. (require origin) lookup {:value true}))
; without a passed value
(opt- origin lookup)
; expansion
((. (require origin) lookup))
These macros are primarily meant for Neovim, but can be used for anything if needed.
If using these macros, say for Neovim configs or a standalone Fennel project (through Hotpot or Tangerine), simply add this repo to your package manager. This will allow access to these macros globally without requiring integration into your project directly. This method is not suggested for plugins as macros can become out of sync, whereas a git subtree only updates whenever the plugin is updated by you.
Since these are compile time dependencies, you may find that your Fennel config files won't be able to compile, you can add this repo to your init.lua
bootstrap before the compile call. See below for an example:
local execute = vim.api.nvim_command
local fn = vim.fn
local fmt = string.format
-- make the package path ~/.local/share/nvim/plug
local packer_path = fn.stdpath("data") .. "/site/pack"
function ensure (user, repo)
local install_path = fmt("%s/packer/start/%s", packer_path, repo, repo)
if fn.empty(fn.glob(install_path)) > 0 then
execute(fmt("!git clone https://github.com/%s/%s %s", user, repo, install_path))
execute(fmt("packadd %s", repo))
end
end
ensure("wbthomason", "packer.nvim")
ensure("katawful", "katcros-fnl")
-- load compiler
ensure("Olical", "aniseed")
-- load aniseed environment
vim.g["aniseed#env"] = {module = "init"}
The path for this method is: katcros-fnl.macro-path
.
This is the preferred method for any Fennel project. Currently I do not have any scripts that manage this, you will have to manage and update them for you.
The path for this method depends on the implementation.
To test these macros, run the script in scripts/setup-test-deps.sh
. Then run make test
using the makefile. Plenary and an installation of Olical/nfnl is used for testing.
Compilation is provided through Olical/nfnl
This is licensed under Unlicense.