Code Monkey home page Code Monkey logo

Comments (8)

shaunsingh avatar shaunsingh commented on May 27, 2024 1

I would love build system as you've proposed above, the doc-draft and usage all look great to me! As you said, it covers all bases and still remains easy to use.

I've been wanting something like this for a while, mainly to solve nyoom-engineering/nyoom.nvim#34, which requires a lua file placed under the colors dir. It's also nice to have another build system to use for plugins, for those of us who don't utilize aniseed's macros and features.

from hotpot.nvim.

rktjmp avatar rktjmp commented on May 27, 2024

Just thinking briefly, I would assume neovim is actually checking for file existence, not module existence, maybe even any file that matches the pattern health.lua or health/init.lua inside the rtp, since they have to look for sub modules too.

The other option is an exhaustive search of all package.loaded + ".health", which would miss stuff that hadn't yet been loaded (maybe due to a failing health check!) Lot faster and safer just to see if any matching file exists.

I had a super quick look and it seemed that the checkhealth code is probably hitting similar autoload code as ftplugins, etc, which load the .lua files directly into the interpreter instead of via the normal require ... infrastructure. Since it never hits lua's require stuff, we can never help redirect it to the fennel code.

So probably you'll have to do the same trick you're currently doing with ftplugins and include some lua shim to load your real code, just beware that Hotpot prioritises lua files over fnl files so they can't have the same module path.

So something like:

fnl/
  /conf
    /health-check.fnl #cant be health.fnl as that would just hit lua/conf/health.lua again
/lua
  /conf
    /health.lua # require("conf.health-check")

Your health.lua might end up being require("conf.health.lsp"); require("conf.health.mappings") etc, which might make the directory structure nicer and at least semi-mirrored.

The other option is including a build step somehow, which you could get ideas from https://github.com/rktjmp/paperplanes.nvim/blob/master/build.fnl https://github.com/rktjmp/paperplanes.nvim/blob/master/hotpotfile.fnl. A more complete build system has always been a maybe-feature for hotpot but I've never been sure how wanted it is by other people. It's probably extra complexity for not much gain in this case though and I'd at least start with the shim.

from hotpot.nvim.

datwaft avatar datwaft commented on May 27, 2024

Thanks! I will see if I can open an issue in the Neovim repository to see if this behaviour can be corrected.

from hotpot.nvim.

datwaft avatar datwaft commented on May 27, 2024

Can you post back on the original Hotpot issue with what you mean by this? I thought regular lua file was working?

I tried to force Hotpot to compile the fnl/conf/health.fnl file by requiring it on the fnl/conf/init.fnl file:

; fnl/conf/init.fnl
(require :conf.health)

I checked that the file exists and contains the expected code, but it doesn't seem that Neovim detects it as a health module.

from hotpot.nvim.

rktjmp avatar rktjmp commented on May 27, 2024

Requiring the file will put it in cache but not inside the RTP so Neovim still wont see it (assuming it's finding health checks via file existence not module existence), you'll have to make the lua file explicitly as outlined above, then require the health check mod from that lua file afaik.

from hotpot.nvim.

datwaft avatar datwaft commented on May 27, 2024

Thanks!

I followed your advice and created a lua/conf/health.lua file just for requiring the code in Fennel. It works perfectly as a workaround!

from hotpot.nvim.

rktjmp avatar rktjmp commented on May 27, 2024

(This is mostly me just rubber ducking but any opinions are also appreciated. This isn't a promise of a final implementation but AFAICT this is quite buildable.)

I have been thinking about this in the background, I think I may add an ahead of time build system (which is actually a pretty long standing idea, originally around writing plugins in fnl but for practical use there isn't any difference). @shaunsingh this may also be of use to you, maybe, if you have any input.

Basic usage probably something like this:

;; build all fnl files inside config dir
;; note: this could also be two calls to build with more "focused" source dirs
(build "/home/user/.config/.nvim"
      ;; config/fnl/*.fnl -> config/lua/*.lua
      "(~/%.config/nvim/)fnl/(.+)" (fn [root path]
                                       ;; ignore our own macro file, init-macros.fnl are 
                                       ;; *always* ignored but you could also adjust 
                                       ;; this to match any `/macros/` dir or whatever.
                                       (if (not (string.match path "my-macros%.fnl$"))
                                         (join-path root :lua path)))
      ;; config/ftplugins/*.fnl -> config/ftplugins/*.lua
      "(~/.config/nvim/ftplugins/.+)" (fn [whole-path] (values whole-path)))

The build function will behave in a similar way to hotpot regular, where it only recompiles if the file is stale, so you could reasonably just include the following in your init and suffer "very minimal performance impact". Alternatively you could define a keymap, command to run the build function, autocommand to run when you save fnl files or even set up libluv file watchers to rebuild on change, etc.

;; we *do not* expand paths, as that can be gnarly in different platforms, and 
;; complicates testing, so the user should pre-expand things.
;; `~` *may* work here, I just have to check it in libuv, I think from memory it didnt previously....
(let [path (.. (os.getenv :HOME) "/.config/nvim/fnl/health/")]
  (build path
    ;; We want to move anything from ~../fnl/health/*/*.fnl to ~../lua/health/*/*.lua
    ;; so we capture the path leading up to /fnl, and then the path "inside" /fnl,
    ;; so we can construct a mirror path that is inside /lua instead.
    "(.+)/fnl/(.+)" (fn [root in-health {: join-path}]
                      (join-path root :lua in-health))))

In your case, where you have pretty high confidence of the contents of your source dir, and only one output location, you could have a pretty simple setup:

(let [path (.. (os.getenv :HOME) "/.config/nvim/fnl/health/")]
  (build path
    ;; just match every *.fnl file, cause everything in fnl/health is what we want.
    ;; and just change /fnl to /lua
    "(.+)" #(string.gsub $1 "/fnl/" "/lua/")))

Initially I was thinking you could just have a map of source->target in hotpot.setup({}) but that wouldn't really work for health checks (or other "runtime" stuff like ftplugins etc) as they're never require()'d, so the loader would never be asked about source file anyway.

It's a sort of a complex impression at first, even just in terms of syntax with the patterns and what gets passed to functions, but I think it's probably flexible enough to cover any case while not being too painful.

Current doc-draft

Build fennel code found inside a directory, according to user defined rules.
Files are only built if the output file is missing or if the source file is
newer.

Paths should be given as absolute values and no expansion is performed
automatically.

Arguments are as given,

`root-dir`

Directory to recursively search inside for `*.fnl` files. Any file named
`init-macros.fnl` is ignored, as macros do not compile to lua. Other 
"Macro files" must be manually ignored, see below.

`options-table or nil or omitted`

Options to pass to fennel compiler. Format mirrors hotpot's `compiler`
configuration key. If not given or given as an explicit nil, the same options
as given to hotpot.setup are used.

`pattern`

A string that each found file path will be tested against.

Ex: "(.+)/fnl/health/(.+)"

`function`

A function that's called if a file path matched the pattern. The function
should return an absolute path, ending in .fnl or .lua. The extension is
ignored, a `.lua` file is always output, but the extension must be present in
the return value. If the function returns nil, the file is *not* compiled
*and* no futher patterns are checked, essentially the file will be ignored.

The function is called with each capture group in its associated pattern and
a final table containing helper functions.

Ex: (fn [root-path path-inside-health-dir {: join-path}
         (join-path some-dir :lua path-inside-health-dir))

Helpers: `join-path` joins all arguments with platform-specific separator.

You can provide any number of patterns function pairs. Patterns are checked
in the order given and match will stop future checks.

Usage example:

;; build all fnl files inside config dir
(build "/home/user/.config/.nvim"
      ;; config/fnl/*.fnl -> config/lua/*.lua
      "(~/%.config/nvim/)fnl/(.+)" (fn [root path]
                                       ;; ignore our own macro file
                                       (if (not (string.match path "my-macros%.fnl$"))
                                         (join-path root :lua path)))
      ;; config/ftplugins/*.fnl -> config/ftplugins/*.lua
      "(~/.config/nvim/ftplugins/.+)" (fn [whole-path] (values whole-path)))

Notes:

Each time you run your build function, the directory must be recursively
iterated for matching files. Configurations with thousands of files and
hundreds of match-function pairs may suffer negative performance impacts.

Even with no changes to the source files, the directory must be iterated and
*checked* for changes each time the build function is run. This check is
reasonably fast as we only have to check a few bytes of filesystem metadata
but it *is* a non-zero cost.

When in doubt, benchmark your build time and potentially limit its source
directory scope if searching is unreasonably long.

from hotpot.nvim.

rktjmp avatar rktjmp commented on May 27, 2024

Build support now in mainline, see COOKBOOK.md or :h hotpot.api.make.

from hotpot.nvim.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.