🌺 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.luato bootstrap hibiscus:
NOTE: if you are using lazy plugin manager, you should create
/init.luainstead.
-- ~/.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 tosilentremap: 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}.