🌺 Highly opinionated macros to elegantly write your neovim config.
Companion library for tangerine, but it can also be used standalone.
- 🍬 Syntactic eye candy over hellscape of lua api
- 🎋 Provides missing features in both fennel and nvim api
- 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
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/hibiscus.nvim", "v1.7")
-- for git head
bootstrap("https://github.com/udayvir-singh/hibiscus.nvim")
- Require a macro library at top of your fennel modules:
; require all macros
(require-macros :hibiscus.core)
(require-macros :hibiscus.vim)
; require specific macros [you can also rename them]
(import-macros {:fstring! f!} :hibiscus.core)
(import-macros {: map!} :hibiscus.vim)
🎉 Now start using these macros in your config
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/hibiscus.nvim)))
Using hibiscus macros:
(require-macros :hibiscus.packer)
(packer-setup {}) ; bootstraps packer
(packer
(use! :udayvir-singh/hibiscus.nvim))
Paq
(local paq (require :paq))
(paq [
:udayvir-singh/hibiscus.nvim
])
Lazy
(local lazy (require :lazy))
(lazy.setup [
:udayvir-singh/hibiscus.nvim
])
(require-macros :hibiscus.packer)
(packer-setup! {opts?})
Bootstraps packer and calls packer.init function with {opts?}.
(packer! {...})
Wrapper around packer.startup function, automatically adds packer to plugin list and syncs it.
(use! {name} {...opts})
Much more lisp friendly wrapper over packer.use
function.
require
-- wrapper aroundconfig
, loads string or list of module names.depends
-- wrapper aroundrequires
, configures plugin dependencies with lisp friendly syntax.
(packer!
(use! :udayvir-singh/hibiscus.nvim)
(use! :plugin-foo
:require ["path.mod1" "path.mod2"]) ; automatically requires these modules
(use! :plugin-baz
:depends [ ; define dependencies in same syntax as use!
"example1"
["example2" :after "hibiscus.nvim" :require "xyz"]
]))
(require-macros :hibiscus.vim)
; or
(import-macros {: augroup!} :hibiscus.vim)
(map! {args} {lhs} {rhs} {desc?})
Defines vim keymap for the given modes from {lhs} to {rhs}.
{args} can contain the following values:
; modes | options |
[ nivcx :remap :verbose :buffer :nowait :expr :unique :script ]
verbose
: opposite tosilent
remap
: opposite tonoremap
;; -------------------- ;;
;; VIMSCRIPT ;;
;; -------------------- ;;
(map! [n :buffer] :R "echo &rtp")
(map! [n :remap] :P "<Plug>(some-function)")
;; -------------------- ;;
;; FENNEL ;;
;; -------------------- ;;
(map! [nv :expr] :j
`(if (> vim.v.count 0) "j" "gj"))
(local greet #(print "Hello World!"))
(map! [n] :gH `greet ; optionally quote to explicitly indicate a function
"greets the world!")
(augroup! {name} {cmds})
Defines autocmd group of {name} with {cmds} containing [args pattern cmd] chunks.
{args} can contain the following values:
[ :nested :once :desc <desc> BufRead Filetype ...etc ]
;; -------------------- ;;
;; VIMSCRIPT ;;
;; -------------------- ;;
(augroup! :spell
[[FileType] [markdown gitcommit] "setlocal spell"])
(augroup! :MkView
[[BufWinLeave
BufLeave
BufWritePost
BufHidden
QuitPre :nested] ?* "silent! mkview!"]
[[BufWinEnter] ?* "silent! loadview"])
(augroup! :buffer-local
[[Event] `(buffer 0) "echo 'hello'"])
;; -------------------- ;;
;; FENNEL ;;
;; -------------------- ;;
(augroup! :highlight-yank
[[TextYankPost :desc "highlights yanked region."]
* #(vim.highlight.on_yank {:timeout 80})])
(local greet #(print "Hello World!"))
(augroup! :greet
[[BufRead] *.sh `(print :HOLLA)]
[[BufRead] * `hello] ; remember to quote functions to indicate they are callbacks
(command! {args} {lhs} {rhs})
Defines user command {lhs} to {rhs}.
{args} can contain the same opts as nvim_create_user_command
:
[
:buffer <number>
:bar <boolean>
:bang <boolean>
:register <boolean>
:range (or <boolean> <string>)
:addr <string>
:count <string>
:nargs <string>
:complete (or <string> <function>)
]
;; -------------------- ;;
;; VIMSCRIPT ;;
;; -------------------- ;;
(command! [:range "%"] :Strip "<line1>,<line2>s: \\+$::e")
;; -------------------- ;;
;; FENNEL ;;
;; -------------------- ;;
(fn greet [opts]
(print :hello opts.args))
(command! [:nargs 1 :complete #["world"]] :Greet `greet) ; quoting is optional in command! macro
(command! [:buffer 0 :bang true] :Lhs #(print $.bang))
(exec! {...})
Translates commands written in fennel to vim.cmd
calls.
(exec!
; setting highlights
[hi! link TSInclude Special]
[hi! DiagnosticVirtualTextError guibg=NONE]
; calling vimscript functions
[echo (resolve (expand "~/path"))]
; injecting commands by quoting [dangerous]
[echo `(.. "'" variable "'")])
Lua output:
vim.cmd("hi! link TSInclude Special")
vim.cmd("hi! DiagnosticVirtualTextError guibg=NONE")
vim.cmd("echo resolve(expand('~/path'))")
vim.cmd("echo '" .. variable .. "'")
(concat! {sep} {...})
Smartly concats all values in {...} with {sep} at compile time. Useful for breaking down large strings without any overhead.
(concat! "\n"
"first line"
"second line"
"third line") ; => "first line\nsecond line\nthird line"
Works like command :set
, sets vim option {name}.
(set! tabstop 4)
(set! nobackup)
(set! wrap!)
(each [_ opt (ipairs ["number" "rnu"])]
(set! opt true))
Works like command :setlocal
, sets local vim option {name}.
(setlocal! filetype "md")
(setlocal! number)
Works like command :setglobal
, sets global vim option {name} without changing the local value.
(setglobal! wrap)
Appends {val} to string-style option {name}.
(set+ wildignore "*.foo")
Prepends {val} to string-style option {name}.
(set^ wildignore ["*.foo" "*.baz"])
Removes {val} from string-style option {name}.
(rem! wildignore "*.baz")
Sets vim colorscheme to {name}.
(color! :desert)
Sets global variable {name} to {val}.
(g! mapleader " ")
Sets buffer scoped variable {name} to {val}.
(b! gretting "Hello World!")
(require-macros :hibiscus.core)
; or
(import-macros {: fstring} :hibiscus.core)
(class! {name} {...})
Defines a new class (object-oriented programming) with {name}.
An init
method must be present in all classes and it should return the base table for class.
To create a instance of class, call new
method on {name}.
;; -------------------- ;;
;; DEFINING CLASSES ;;
;; -------------------- ;;
(class! stack
(method! init [list] list) ; arguments of new method are passed here
(method! push [val]
"inserts {val} into the stack."
(table.insert self val)) ; self variable is accessible from all methods
(metamethod! __tostring []
"converts stack into a string."
(table.concat self " ")))
(class! stack-stream
(local state {:cursor 0})
(method! init [stack]
(set state.len (# stack)) ; private state
{: stack}) ; public state
(method! next []
"returns next item from stream."
(++ state.cursor)
(assert (<= state.cursor state.len)
"stack-stream: attempt to call next() on empty stream.")
(. self.stack state.cursor)))
;; -------------------- ;;
;; DEMO ;;
;; -------------------- ;;
(local st (stack:new [:a :b])) ; new method should be called to create a instance
(st:push :c)
(print (tostring st)) ; => "a b c"
(local stream (stack-stream:new st))
(print (stream:next)) ; => "a"
(print (stream:next)) ; => "b"
(method! {name} {args} {...})
Defines a method within the scope of class.
The self
variable is accessible from the scope of every method.
(class! foo
(method! init [] {}) ; required for all classes
(method! hello []
(print "hello world!")))
(metamethod! {name} {args} {...})
Defines a metamethod within the scope of class.
The self
variable is accessible from the scope of every metamethod.
See lua docs for list of valid metamethods.
(class! foo
(method! init [] {}) ; required for all classes
(metamethod! __tostring []
"example_string"))
(instanceof? {val} {class})
Checks if {val} is an instance of {class}.
(class! foo
(method! init [] {}))
(local x (foo:new))
(instanceof? x foo) ; => true
(instanceof? {} foo) ; => false
(dump! {...})
Pretty prints {...} into human readable form.
(or= {x} {...})
Checks if {x} is equal to any one of {...}.
(fstring! {str})
Wrapper around string.format, works like javascript's template literates.
${...}
is parsed as variable$(...)
is parsed as fennel code
(local name "foo")
(fstring! "hello ${name}")
(fstring! "${name}: two + four is $(+ 2 4).")
(enum! {name} ...)
Defines enumerated values for names.
(enum! A B C) ; A=1, B=2, C=3
(time! {label} ...)
Prints execution time of {...} in milliseconds.
(time! :add
(+ 1 2)) ; add: [XXX]ms
(nil? {x})
checks if value of {x} is nil.
(empty? {x})
checks if {x} :: [string or table] is empty.
(boolean? {x})
checks if {x} is of boolean type.
(string? {x})
checks if {x} is of string type.
(number? {x})
checks if {x} is of number type.
(odd? {int})
checks if {int} is of odd parity.
(even? {int})
checks if {int} is of even parity.
(fn? {x})
checks if {x} is of function type.
(table? {x})
checks if {x} is of table type.
(seq? {tbl})
checks if {tbl} is valid list / array.
(inc! {int})
increments {int} by 1 and returns its value.
(++ {variable})
increments {variable} by 1 and returns its value.
(dec! {int})
decrements {int} by 1 and returns its value.
(-- {variable})
decrements {variable} by 1 and returns its value.
(append! {variable} {str})
appends {str} to {variable}.
(tappend! {tbl} {key} {str})
appends {str} to {key} of table {tbl}.
(prepend! {variable} {str})
prepends {str} to {variable}.
(tprepend! {tbl} {key} {str})
prepends {str} to {key} of table {tbl}.
(split! {str} {sep})
splits {str} into a list at each {sep}.
(tmap! {tbl} {handler})
maps values in {tbl} with {handler}.
{handler} takes in (val, key, tbl) as arguments and returns a new value.
(filter! {list} {handler})
filters values in {list} with {handler}.
{handler} takes in (val) and returns a boolean.
(merge-list! {list1} {list2})
merges all values of {list1} and {list2} together, and returns a new list.
(merge-tbl! {tbl1} {tbl2})
merges {tbl2} onto {tbl1}, and returns a new table.
(merge! {tbl1} {tbl2})
merges {tbl1} and {tbl2}, correctly appending lists.
(vmerge! {variable} {tbl})
merges values of {tbl} onto {variable}.