Skip to content

Instantly share code, notes, and snippets.

@bangedorrunt
Created September 25, 2021 09:53
Show Gist options
  • Save bangedorrunt/b7570fad84e6923398a217f9d46e29f3 to your computer and use it in GitHub Desktop.
Save bangedorrunt/b7570fad84e6923398a217f9d46e29f3 to your computer and use it in GitHub Desktop.
troubleshoot.fnl
;;;; Helper Functions Declaration
;; (import-macros {: defn
;; : into
;; : empty
;; : when-let
;; : if-let
;; : when-some
;; : if-some} :cljlib.macros)
(local {: inc
: dec
: first
: last
: rest
: any?
: every?
:some some?
: keys
: vals
: reduce
: reduce-kv
: merge
:mapv map
: filter
: string?
: number?
: nil?
: contains?
;; :map? tbl?
;; : set?
;; :vector? seq?
;; : vector
: empty?
: cons
: conj
: dissoc} (require :cljlib))
;; (local {: format} string)
;; Reserve name for `into` macros
;; tbl->seq
;; seq->tbl
;; str->seq
(fn ->str [obj]
"Converts an object to its string representation"
(tostring obj))
(fn str->seq [s]
"Convert an string into a sequence of characters."
(icollect [c (string.gmatch s ".")]
c))
;; (fn ->key-opts [seq]
;; "Returns a set following the structure of `{:key true}` from a sequence"
;; (reduce #(merge $1 {$2 true}) {} seq))
(fn ->key-opts [xs]
"Convert a sequence into a table of each value of the sequence as the
key and the value being `true`."
(collect [_ v (ipairs xs)]
(values v true)))
(fn fn? [obj]
"Returns true if the object is a function
This only works at compilation time"
(and (list? obj) (or (= (->str (first obj)) :hashfn)
(= (->str (first obj)) :fn))))
(fn exists? [module-name]
"Returns true if the module exists and false if it doesn't"
(let [(ok? _) (pcall require module-name)]
ok?))
;;; Macros Declaration
;; NOTE: https://github.com/rktjmp/hotpot.nvim/discussions/6
;; Put Unique Global
;;
;; (val :any prefix? :string) -> (uuid :string)
;;
;; Takes any given value, generates a unique name (with optional prefix)
;; and inserts value into _G. Returns unique name.
(fn pug [val prefix?]
;; gensym will generate a unique id across a compile pass, but hotpot/aniseed
;; may compile files in separate passes as they are modified, so symbols may
;; collide you can avoid this by passing a unique prefix per-file or using
;; something like the "uid" below, based on compile time
(let [inter-compile-uid (_G.os.date "%s")
name (if prefix?
(.. (->str (gensym prefix?)) inter-compile-uid)
(.. (->str (gensym :pug)) inter-compile-uid))]
`(do
(tset _G ,name ,val)
,name)))
;; Wrap given in v:lua x pug call
(fn vlua [what prefix?]
`(.. "v:lua." ,(pug what prefix?) "()"))
;; BUG: `vlua` seems to have side-effect with `aniseed` plugin
;; current version using gensym doesn't evaluate `vlua` at runtime
(fn command! [name expr]
(let [name (->str name)]
(if (fn? expr)
`(vim.cmd (string.format "command! %s call %s" ,name ,(vlua expr)))
`(vim.cmd (string.format "command! %s %s" ,name ,expr)))))
(fn buf-command! [name expr]
(let [name (->str name)]
(if (fn? expr)
`(vim.cmd (string.format "command! -buffer %s call %s" ,name ,(vlua expr)))
`(vim.cmd (string.format "command! -buffer %s %s" ,name ,expr)))))
;; (fn command! [name f]
;; `(vim.cmd ,(string.format "command! %s %s" (->str name) (->str f))))
;; (fn buf-command! [name f]
;; `(vim.cmd ,(string.format "command! -buffer %s %s" (->str name) (->str f))))
(fn lua-command! [name f]
(let [f (.. "lua " (->str f))]
`(command! ,name ,f)))
(fn lua-buf-command! [name f]
(let [f (.. "lua " (->str f))]
`(buf-command! ,name ,f)))
(fn unless [condition ...]
"Takes a single condition and evaluates the rest as a body if it's nil or
false. This is intended for side-effects."
`(when (not ,condition)
,...))
(fn set! [...]
"Set one or multiple vim options using the `vim.opt` API
The option name must be a symbol
If the option doesn't have a corresponding value, if it begins with no the
value becomes false, and if it doesn't it becomes true"
(fn expr [name ?value]
(let [name (->str name)
value (if (nil? ?value) (not (name:match :^no)) ?value)
name (if (nil? ?value) (or (name:match "^no(.*)$") name) name)]
(if (fn? value)
`(tset vim.opt ,name (string.format "%s" ,(vlua value)))
(match (name:sub -1)
"+" `(: (. vim.opt ,(name:sub 1 -2)) :append ,value)
"-" `(: (. vim.opt ,(name:sub 1 -2)) :remove ,value)
"^" `(: (. vim.opt ,(name:sub 1 -2)) :prepend ,value)
_ `(tset vim.opt ,name ,value)))))
(fn exprs [...]
(match [...]
(where [& rest] (empty? rest)) []
(where [name value & rest] (not (sym? value)))
[(expr name value) (unpack (exprs (unpack rest)))]
[name & rest] [(expr name) (unpack (exprs (unpack rest)))]
_ []))
(let [exprs (exprs ...)]
(if (> (length exprs) 1)
`(do
,(unpack exprs))
(unpack exprs))))
(fn set-local! [...]
"Set a local vim option using the `vim.opt_local` API"
(fn expr [name ?value]
(let [name (->str name)
value (if (nil? ?value) (not (name:match :^no)) ?value)
name (if (nil? ?value) (or (name:match "^no(.*)$") name) name)]
(if (fn? value)
`(tset vim.opt_local ,name (string.format "%s" ,(vlua value)))
(match (name:sub -1)
"+" `(: (. vim.opt_local ,(name:sub 1 -2)) :append ,value)
"-" `(: (. vim.opt_local ,(name:sub 1 -2)) :remove ,value)
"^" `(: (. vim.opt_local ,(name:sub 1 -2)) :prepend ,value)
_ `(tset vim.opt_local ,name ,value)))))
(fn exprs [...]
(match [...]
(where [& rest] (empty? rest)) []
(where [name value & rest] (not (sym? value)))
[(expr name value) (unpack (exprs (unpack rest)))]
[name & rest] [(expr name) (unpack (exprs (unpack rest)))]
_ []))
(let [exprs (exprs ...)]
(if (> (length exprs) 1)
`(do
,(unpack exprs))
(unpack exprs))))
(fn let! [...]
"Set a vim variable using the vim.[g b w t] API"
(fn expr [name value]
(let [name (->str name)
scope (when (contains? [:g/ :b/ :w/ :t/] (name:sub 1 2))
(name:sub 1 1))
name (if (nil? scope) name (name:sub 3))]
`(tset ,(match scope
:b `vim.b
:w `vim.w
:t `vim.t
_ `vim.g) ,name ,value)))
(fn exprs [...]
(match [...]
(where [& rest] (empty? rest)) []
[name value & rest] [(expr name value) (unpack (exprs (unpack rest)))]
_ []))
(let [exprs (exprs ...)]
(if (> (length exprs) 1)
`(do
,(unpack exprs))
(unpack exprs))))
(fn augroup! [name ...]
"Defines an autocommand group using the `vim.cmd` API."
`(do
,(unpack
(-> [`(vim.cmd ,(string.format "augroup %s" name))
`(vim.cmd "autocmd!")]
(conj ...)
(conj `(vim.cmd "augroup END"))))))
(fn buf-augroup! [name ...]
"Defines a buffer-local autocommand group using the `vim.cmd` API."
`(do
,(unpack
(-> [`(vim.cmd ,(string.format "augroup %s" name))
`(vim.cmd "autocmd! * <buffer>")]
(conj ...)
(conj `(vim.cmd "augroup END"))))))
(fn autocmd! [events pattern command]
"Defines an autocommand"
(let [events (if (sequence? events) events [events])
events (-> (map ->str events)
(table.concat ","))
pattern (if (sequence? pattern) pattern [pattern])
pattern (-> (map ->str pattern)
(table.concat ","))]
(if (fn? command)
`(vim.cmd (string.format "autocmd %s %s call %s" ,events ,pattern
,(vlua command)))
`(vim.cmd (string.format "autocmd %s %s %s" ,events ,pattern ,command)))))
(fn buf-autocmd! [events command]
"Defines an autocommand"
(let [events (if (sequence? events) events [events])
events (-> (map ->str events)
(table.concat ","))]
(if (fn? command)
`(vim.cmd (string.format "autocmd %s <buffer> call %s" ,events
,(vlua command)))
`(vim.cmd (string.format "autocmd %s <buffer> %s" ,events ,command)))))
(fn wk-map! [mode
lhs
{:buffer buffer?
:silent silent?
:noremap noremap?
:nowait nowait?}
description]
"A mapping using which-key"
`(let [(ok?# which-key#) (pcall require :which-key)]
(when ok?#
(which-key#.register {,lhs ,description}
{:mode ,mode
:buffer ,(if buffer? 0)
:silent ,(if silent? true)
:noremap ,(if (not noremap?) false)
:nowait ,(if nowait? true)}))))
(fn nmap! [[modes & options] lhs rhs ?description]
"Defines a vim mapping using the `vim.api.nvim_set_keymap` API or the
`vim.api.nvim_buf_set_keymap` if the option `:buffer` was passed.
Support all the options the API supports.
If the `rhs` argument is a function then automatically includes the `:expr`
option."
(fn nvim-set-keymap [mode lhs rhs options]
(let [buffer? (?. options :buffer)
options (dissoc options :buffer)]
(if buffer?
`(vim.api.nvim_buf_set_keymap 0 ,mode ,lhs ,rhs ,options)
`(vim.api.nvim_set_keymap ,mode ,lhs ,rhs ,options))))
(fn is-rhs-a-fn? [rhs]
(if (fn? rhs)
(vlua rhs)
rhs))
(let [modes (-> modes
->str
str->seq)
options
(if (fn? rhs)
(conj options :expr)
options)
options (->key-opts options)
exprs (map #(nvim-set-keymap $ lhs (is-rhs-a-fn? rhs) options) modes)
exprs (if (and ?description (exists? :which-key))
(conj exprs (unpack (map #(wk-map! $ lhs options ?description) modes)))
exprs)]
(if (> (length exprs) 1)
`(do
,(unpack exprs))
(unpack exprs))))
(fn noremap! [[modes & options] lhs rhs ?description]
"Defines a vim mapping using the `vim.api.nvim_set_keymap` API or the
`vim.api.nvim_buf_set_keymap` if the option `:buffer` was passed.
Support all the options the API supports.
If the `rhs` argument is a function then automatically includes the `:expr`
option.
Automatically includes the `:noremap` option."
(let [options (cons :noremap options)]
`(nmap! ,(cons modes options) ,lhs ,rhs ,?description)))
(fn buf-nmap! [[modes & options] lhs rhs ?description]
"Defines a vim mapping using the `vim.api.nvim_buf_set_keymap` if the option
`:buffer` was passed.
Support all the options the API supports.
If the `rhs` argument is a function then automatically includes the `:expr`
option."
(let [options (cons :buffer options)]
`(nmap! ,(cons modes options) ,lhs ,rhs ,?description)))
(fn buf-noremap! [[modes & options] lhs rhs ?description]
"Defines a vim mapping using the `vim.api.nvim_buf_set_keymap` if the option
`:buffer` was passed.
Support all the options the API supports.
If the `rhs` argument is a function then automatically includes the `:expr`
option.
Automatically includes the `:noremap` option."
(let [options (->> options
(cons :buffer)
(cons :noremap))]
`(nmap! ,(cons modes options) ,lhs ,rhs ,?description)))
(fn hi! [group opts]
"Defines a highlight"
(let [f (fn [k v]
(match k
:fg (let [fg (. opts :fg)]
`(.. :ctermfg= ,fg " guifg=" ,fg))
:bg (let [bg (. opts :bg)]
`(.. :ctermbg= ,bg " guibg=" ,bg))
_ `(.. ,(->str k) "=" ,v)))
args (reduce-kv (fn [acc k v]
`(.. ,acc " " ,(f k v)))
group opts)]
`(->> ,args
(string.format "hi %s")
vim.cmd)))
(fn t [key]
"Returns the string with termcodes replaced"
`(vim.api.nvim_replace_termcodes ,(->str key) true true true))
(fn feedkeys [key]
`(vim.api.nvim_feedkeys ,(t key) :n true))
(fn has? [property]
"Returns true if vim has a propety"
`(match (vim.fn.has ,property)
1 true
0 false
_# nil))
{: pug
: vlua
: set!
: set-local!
: let!
: command!
: buf-command!
: lua-command!
: lua-buf-command!
: augroup!
: buf-augroup!
: autocmd!
: buf-autocmd!
: nmap!
: noremap!
: buf-nmap!
: buf-noremap!
: hi!
: t
: feedkeys
: has?
: unless}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment