;;; macros.fnl - useful macros ;; Helpful keymaping functions (lambda bind! [modes key cmd ?a1 ?a2] (let [desc (or ?a2 ?a1) buf (if (and ?a1 ?a2) ?a1 ?a2) opts { :noremap true :silent true :buffer buf }] (when desc (tset opts :desc desc)) (if (table? modes) (let [output {}] (each [_ mode (ipairs modes)] (table.insert output `(vim.keymap.set ,mode ,key ,cmd ,opts))) output) `(vim.keymap.set ,modes ,key ,cmd ,opts)))) ;; Better autocommands (lambda hook! [hooks ?patterns callback] (let [hook_table (if (= (type hooks) :table) hooks [ hooks ]) pattern_table (if (not ?patterns) [] (= (type ?patterns) :table) ?patterns [ ?patterns ])] (var group (.. (tostring (math.random 0 1000000000) "-config-hook"))) (each [_ hook (ipairs hook_table)] (set group (.. group "-" hook))) (each [_ pattern (ipairs pattern_table)] (set group (.. group "-" pattern))) `(vim.api.nvim_create_autocmd ,hook_table { :group (vim.api.nvim_create_augroup ,group { :clear true }) :pattern ,pattern_table :callback ,callback }))) ;; Utility functions for use! (lambda create-list-entries [...] (let [output { :active-entry nil :entries {} }] (lambda output.process [self entry] (var result false) (when self.active-entry (if (and (. self :entries self.active-entry :is-first) (not (list? entry))) (do (tset self :entries self.active-entry :data entry) (set self.active-entry nil) (set result true)) (= (type entry) :string) (set self.active-entry nil) (do (table.insert (. self :entries self.active-entry :data) entry) (tset self :entries self.active-entry :is-first false) (set result true)))) (when (and (= (type entry) :string) (. self :entries entry)) (assert-compile (not (. self :entries entry :present)) (.. "':" entry "' cannot appear more than once in `use'")) (set self.active-entry entry) (tset self :entries entry :present true) (set result true)) result) (lambda output.splice-into [self other] (each [name entry (pairs self.entries)] (when entry.present (tset other name entry.data)))) (each [_ val (ipairs [...])] (tset output :entries val { :present false :is-first true :data `(lambda [])})) output)) ;; Nicer macro for use ;; :config acts like use-package's :init and :config options, ;; taking in the following lisp expressions as its arguments (lambda use! [repo ...] (local output [ repo ]) (var last_key nil) (var list_entries (create-list-entries :config :setup :run)) (each [_ val (ipairs [...])] (if last_key (do (tset output last_key val) (set last_key nil)) (not (list_entries:process val)) (set last_key val))) (list_entries:splice-into output) `(use ,output)) ;; Call a plugin's `setup function' (lambda setup! [pkg ...] (local output [ ]) (var last_key nil) (each [_ val (ipairs [...])] (if last_key (do (tset output last_key val) (set last_key nil)) (set last_key val))) `((. (require ,pkg) :setup) ,output)) ;; Call module function (lambda module-call! [mod func ...] `((. (require ,mod) ,func) ,...)) ;; Return module function (lambda module-fn! [mod func] `(. (require ,mod) ,func)) ;; Basically does what the Emacs function of the same name does. ;; Wraps the code inside with a `winsaveview' and `winrestview' (lambda save-excursion! [...] (let [args [...] len (# args)] (tset args len `(let [retvar# ,(. args len)] (vim.fn.winrestview save#) retvar#)) `(let [save# (vim.fn.winsaveview)] ,(unpack args)))) {: bind! : hook! : use! : setup! : module-call! : module-fn! : save-excursion!}