fs.fnl
Branches > main > c6f0089 > files / nvim / pack / plugins / opt / conjure / fnl / conjure / fs.fnl
(local {: autoload : define} (require :conjure.nfnl.module))
(local core (autoload :conjure.nfnl.core))
(local str (autoload :conjure.nfnl.string))
(local config (autoload :conjure.config))
(local nfs (autoload :conjure.nfnl.fs))
(local vim _G.vim)
(local M (define :conjure.fs))
(local path-sep (nfs.path-sep))
(fn M.env [k]
(let [v (vim.fn.getenv k)]
(when (and (core.string? v) (not (core.empty? v)))
v)))
(fn M.config-dir []
"Return $XDG_CONFIG_HOME/conjure.
Defaulting the config directory to $HOME/.config."
(vim.fs.normalize (if (M.env "XDG_CONFIG_HOME")
"$XDG_CONFIG_HOME/conjure"
"~/.config/conjure")))
(fn M.absolute-path [path]
(vim.fs.normalize (vim.fn.fnamemodify path ":p")))
(fn M.findfile [name path]
"Wrapper around Neovim's findfile() that returns nil
instead of an empty string."
(let [res (vim.fn.findfile name path)]
(when (not (core.empty? res))
(M.absolute-path res))))
(fn M.split-path [path]
(vim.split path path-sep {:trimempty true}))
(fn M.join-path [parts]
(str.join path-sep (core.concat parts)))
(fn M.parent-dir [path]
(let [res (-> path
(M.split-path)
(core.butlast)
(M.join-path))]
(if (= "" res)
nil
(.. path-sep res))))
(fn M.upwards-file-search [file-names from-dir]
"Given a list of relative filenames and an absolute path to a directory,
return the absolute path of the first file that matches a relative path,
starting at the directory and then upwards towards the root directory. If no
match is found, return nil."
(when (and from-dir (not (core.empty? file-names)))
(let [result (core.some
(fn [file-name]
(M.findfile file-name from-dir))
file-names)]
(if result
result
(M.upwards-file-search file-names (M.parent-dir from-dir))))))
(fn M.resolve-above [names]
"Resolve a pathless list of file names to an absolute path by looking in the
containing and parent directories of the current file, current working
directory and finally $XDG_CONFIG_HOME/conjure.
The file names are considered in priority order, if a match is found for the
first file name in the first directory, everything will short circuit and
return that full path."
(or
(M.upwards-file-search names (vim.fn.expand "%:p:h"))
(M.upwards-file-search names (vim.fn.getcwd))
(M.upwards-file-search names (M.config-dir))))
(fn M.file-readable? [path]
(= 1 (vim.fn.filereadable path)))
(fn M.resolve-relative-to [path root]
"Successively remove parts of the path until we get to a relative path that
points to a file we can read from the root. If we run out of parts default to
the original path."
(fn loop [parts]
(if (core.empty? parts)
path
(if (M.file-readable? (M.join-path (core.concat [root] parts)))
(M.join-path parts)
(loop (core.rest parts)))))
(loop (M.split-path path)))
(fn M.resolve-relative [path]
"If g:conjure#relative_file_root is set, will resolve the path relative to
that. Will return the original path immediately if not."
(let [relative-file-root (config.get-in [:relative_file_root])]
(if relative-file-root
(M.resolve-relative-to path relative-file-root)
path)))
(fn M.apply-path-subs [path path-subs]
(core.reduce
(fn [path [pat rep]]
(path:gsub pat rep))
path
(core.kv-pairs path-subs)))
(fn M.localise-path [path]
"Apply the g:conjure#relative_file_root and g:conjure#path_subs configuration
to the given path."
(-> path
(M.apply-path-subs (config.get-in [:path_subs]))
(M.resolve-relative)))
(fn M.current-source []
(let [info (debug.getinfo 2 "S")]
(when (vim.startswith (core.get info :source) "@")
(string.sub info.source 2))))
(set M.conjure-source-directory
(let [src (M.current-source)]
(when src
;; Go three levels up!
(vim.fs.dirname (vim.fs.dirname (vim.fs.dirname src))))))
(fn M.file-path->module-name [file-path]
"Tries to match a file path up to an existing loaded Lua module."
(when file-path
(core.some
(fn [mod-name]
(let [mod-path (string.gsub mod-name "%." path-sep)]
(when (or
(vim.endswith file-path (.. mod-path ".fnl"))
(vim.endswith file-path (.. mod-path "/init.fnl")))
mod-name)))
(core.keys package.loaded))))
M