Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/udayvir-singh/hibiscus.nvim

:hibiscus: Flavored Fennel Macros for Neovim
https://github.com/udayvir-singh/hibiscus.nvim

Last synced: about 2 months ago
JSON representation

:hibiscus: Flavored Fennel Macros for Neovim

Awesome Lists containing this project

README

        

# Hibiscus.nvim

> :hibiscus: Highly opinionated macros to elegantly write your neovim config.

Companion library for [tangerine](https://github.com/udayvir-singh/tangerine.nvim),
but it can also be used standalone.

![Neovim version](https://img.shields.io/badge/For_Neovim-0.8-dab?style=for-the-badge&logo=neovim&logoColor=dab)

## Rational

- :candy: Syntactic eye candy over hellscape of lua api
- :tanabata_tree: Provides missing features in both fennel and nvim api

# Installation

- Create file `plugin/0-tangerine.lua` to bootstrap hibiscus:

> NOTE: if you are using [lazy](https://github.com/folke/lazy.nvim) plugin manager,
> you should create `/init.lua` instead.

```lua
-- ~/.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:

```fennel
; 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)
```

:tada: Now start using these macros in your config

## Package Management

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

Packer

```fennel
(local packer (require :packer))

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

Using [hibiscus](https://github.com/udayvir-singh/hibiscus.nvim) macros:

```fennel
(require-macros :hibiscus.packer)

(packer-setup {}) ; bootstraps packer

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

Paq

```fennel
(local paq (require :paq))

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

Lazy

```fennel
(local lazy (require :lazy))

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

# Packer Macros

```fennel
(require-macros :hibiscus.packer)
```

#### packer-setup!

(packer-setup! {opts?})

Bootstraps packer and calls packer.init function with {opts?}.

#### packer!

(packer! {...})

Wrapper around packer.startup function, automatically adds packer to plugin list and syncs it.

#### use!

(use! {name} {...opts})

Much more lisp friendly wrapper over `packer.use` function.

##### Extra Options:

- `require` -- wrapper around `config`, loads string or list of module names.
- `depends` -- wrapper around `requires`, configures plugin dependencies with lisp friendly syntax.

##### Examples:

```fennel
(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"]
]))
```

# Neovim Macros

```fennel
(require-macros :hibiscus.vim)
; or
(import-macros {: augroup!} :hibiscus.vim)
```

## keymaps

#### map!

(map! {args} {lhs} {rhs} {desc?})

Defines vim keymap for the given modes from {lhs} to {rhs}.

##### Arguments:

{args} can contain the following values:

```fennel
; modes | options |
[ nivcx :remap :verbose :buffer :nowait :expr :unique :script ]
```

- `verbose`: opposite to `silent`
- `remap`: opposite to `noremap`

##### Examples:

```fennel
;; -------------------- ;;
;; VIMSCRIPT ;;
;; -------------------- ;;
(map! [n :buffer] :R "echo &rtp")
(map! [n :remap] :P "(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!")
```

## autocmds

#### augroup!

(augroup! {name} {cmds})

Defines autocmd group of {name} with {cmds} containing [args pattern cmd] chunks.

##### Arguments:

{args} can contain the following values:

```fennel
[ :nested :once :desc BufRead Filetype ...etc ]
```

##### Examples:

```fennel
;; -------------------- ;;
;; 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
```

## commands

#### command!

(command! {args} {lhs} {rhs})

Defines user command {lhs} to {rhs}.

##### Arguments:

{args} can contain the same opts as `nvim_create_user_command`:

```fennel
[
:buffer
:bar
:bang
:register
:range (or )
:addr
:count
:nargs
:complete (or )
]
```

##### Examples:

```fennel
;; -------------------- ;;
;; VIMSCRIPT ;;
;; -------------------- ;;
(command! [:range "%"] :Strip ",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))
```

## vimscript

#### exec!

(exec! {...})

Translates commands written in fennel to `vim.cmd` calls.

##### Example:

```fennel
(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:
```lua
vim.cmd("hi! link TSInclude Special")
vim.cmd("hi! DiagnosticVirtualTextError guibg=NONE")
vim.cmd("echo resolve(expand('~/path'))")
vim.cmd("echo '" .. variable .. "'")
```

## misc

#### concat!

(concat! {sep} {...})

Smartly concats all values in {...} with {sep} at compile time.
Useful for breaking down large strings without any overhead.

##### Example:

```fennel
(concat! "\n"
"first line"
"second line"
"third line") ; => "first line\nsecond line\nthird line"
```

## vim options

#### set!

Works like command `:set`, sets vim option {name}.

```fennel
(set! tabstop 4)
(set! nobackup)
(set! wrap!)

(each [_ opt (ipairs ["number" "rnu"])]
(set! opt true))
```

#### setlocal!

Works like command `:setlocal`, sets local vim option {name}.

```fennel
(setlocal! filetype "md")
(setlocal! number)
```

#### setglobal!

Works like command `:setglobal`, sets global vim option {name} without changing the local value.

```fennel
(setglobal! wrap)
```

#### set+

Appends {val} to string-style option {name}.

```fennel
(set+ wildignore "*.foo")
```

#### set^

Prepends {val} to string-style option {name}.

```fennel
(set^ wildignore ["*.foo" "*.baz"])
```

#### rem!

Removes {val} from string-style option {name}.

```fennel
(rem! wildignore "*.baz")
```

#### color!

Sets vim colorscheme to {name}.

```fennel
(color! :desert)
```

## variables

#### g!

Sets global variable {name} to {val}.

```fennel
(g! mapleader " ")
```

#### b!

Sets buffer scoped variable {name} to {val}.

```fennel
(b! gretting "Hello World!")
```

# Core Macros

```fennel
(require-macros :hibiscus.core)
; or
(import-macros {: fstring} :hibiscus.core)
```

## OOP

## class!

(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}.

##### Examples:

```fennel
;; -------------------- ;;
;; 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!

(method! {name} {args} {...})

Defines a method within the scope of class.

The `self` variable is accessible from the scope of every method.

##### Example:

```fennel
(class! foo
(method! init [] {}) ; required for all classes

(method! hello []
(print "hello world!")))
```

#### metamethod!

(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.

##### Example:

```fennel
(class! foo
(method! init [] {}) ; required for all classes

(metamethod! __tostring []
"example_string"))
```

#### instanceof?

(instanceof? {val} {class})

Checks if {val} is an instance of {class}.

##### Example:

```fennel
(class! foo
(method! init [] {}))

(local x (foo:new))

(instanceof? x foo) ; => true
(instanceof? {} foo) ; => false
```

## general

#### dump!

(dump! {...})

Pretty prints {...} into human readable form.

#### or=

(or= {x} {...})

Checks if {x} is equal to any one of {...}.

#### fstring!

(fstring! {str})

Wrapper around string.format, works like javascript's template literates.

- `${...}` is parsed as variable
- `$(...)` is parsed as fennel code

##### Examples:

```fennel
(local name "foo")
(fstring! "hello ${name}")

(fstring! "${name}: two + four is $(+ 2 4).")
```

#### enum!

(enum! {name} ...)

Defines enumerated values for names.

##### Example:

```fennel
(enum! A B C) ; A=1, B=2, C=3
```

#### time!

(time! {label} ...)

Prints execution time of {...} in milliseconds.

##### Example:

```fennel
(time! :add
(+ 1 2)) ; add: [XXX]ms
```

## checking values

```fennel
(nil? {x})
```

> checks if value of {x} is nil.

```fennel
(empty? {x})
```

> checks if {x} :: [string or table] is empty.

```fennel
(boolean? {x})
```

> checks if {x} is of boolean type.

```fennel
(string? {x})
```

> checks if {x} is of string type.

```fennel
(number? {x})
```

> checks if {x} is of number type.

```fennel
(odd? {int})
```

> checks if {int} is of odd parity.

```fennel
(even? {int})
```

> checks if {int} is of even parity.

```fennel
(fn? {x})
```

> checks if {x} is of function type.

```fennel
(table? {x})
```

> checks if {x} is of table type.

```fennel
(seq? {tbl})
```

> checks if {tbl} is valid list / array.

## number

```fennel
(inc! {int})
```

> increments {int} by 1 and returns its value.

```fennel
(++ {variable})
```

> increments {variable} by 1 and returns its value.

```fennel
(dec! {int})
```

> decrements {int} by 1 and returns its value.

```fennel
(-- {variable})
```

> decrements {variable} by 1 and returns its value.

## string

```fennel
(append! {variable} {str})
```

> appends {str} to {variable}.

```fennel
(tappend! {tbl} {key} {str})
```

> appends {str} to {key} of table {tbl}.

```fennel
(prepend! {variable} {str})
```

> prepends {str} to {variable}.

```fennel
(tprepend! {tbl} {key} {str})
```

> prepends {str} to {key} of table {tbl}.

```fennel
(split! {str} {sep})
```

> splits {str} into a list at each {sep}.

## table

```fennel
(tmap! {tbl} {handler})
```

> maps values in {tbl} with {handler}.
>
> {handler} takes in (val, key, tbl) as arguments and returns a new value.

```fennel
(filter! {list} {handler})
```

> filters values in {list} with {handler}.
>
> {handler} takes in (val) and returns a boolean.

```fennel
(merge-list! {list1} {list2})
```

> merges all values of {list1} and {list2} together, and returns a new list.

```fennel
(merge-tbl! {tbl1} {tbl2})
```

> merges {tbl2} onto {tbl1}, and returns a new table.

```fennel
(merge! {tbl1} {tbl2})
```

> merges {tbl1} and {tbl2}, correctly appending lists.

```fennel
(vmerge! {variable} {tbl})
```

> merges values of {tbl} onto {variable}.

# End Credits

- [aniseed](https://github.com/Olical/aniseed): for introducing me to fennel
- [zest](https://github.com/tsbohc/zest.nvim): for inspiring `hibiscus.vim` macros