datwaft / nvim.conf

datwaft's neovim configuration
114 stars 6 forks source link

Question about package macros #18

Closed shaunsingh closed 2 years ago

shaunsingh commented 2 years ago

Hi! I was just wondering why

  1. choose to use a global to manage your list of plugins/rocks
  2. manage package/packages at compile-time instead of runtime

It would make sense to use a global if you were to part the pack! statements to different files and then unpack them at once, though thats much harder to do compile-time, and vice versa (as you have all the plugins in one file a local would work as well)

I was working on a "module system" (where sets of plugins and configurations are defined in a module thats loaded via a macro ala doomemacs) and was trying to part out the package declarations into their own files, but currently that means having to include them which gets messy very quickly

datwaft commented 2 years ago

The idea behind the macros that I use to manage packages was to reduce the nesting required.

For example, without macros you would need to do something like this:

(local packer (require "packer"))

(packer.startup
  (fn [use]
    (use "wbthomason/packer.nvim")
    (use {0 "tpope/vim-dispatch" :opt true :cmd ["Dispatch" "Make" "Focus" "Start"]})))

With my current macros you would something like:

(pack! "wbthomason/packer.nvim")
(pack! "tpope/vim-dispatch" {:opt true :cmd ["Dispatch" "Make" "Focus" "Start"]})

(unpack!)

As you can see it streamlines the process of declaring packages, making each line/statement a declaration of a package, without needing nesting at all.

This approach also has some weaknesses, I haven't tested recently if you can have multiple package definitions in multiple files, but a few months ago there was an issue with hotpot in which the global variables were reset each time the macros were imported, and so you couldn't define some packages in some file and after that add more packages in another file. (See https://github.com/rktjmp/hotpot.nvim/issues/38, it is fixed right now).

Additionally, to my understanding the use of global variables in compile-time is not well defined in Fennel, so the functionality could be broken in future updates.

choose to use a global to manage your list of plugins/rocks

This is mainly a limitation of the approach I used. To let every package declaration be at root level I needed a way to save which packages had been declared, to later on expand that list of declarations into the code that the package manager requires to function. Without a global I cannot make macros communicate between each other to save the state, and therefore I wouldn't be able to have them at root level.

If you find a better solution let me know, as I also don't really like having macros do side-effects.

manage package/packages at compile-time instead of runtime

Technically the packages are not being managed at compile time, the macros don't execute any code to interface with Packer at compile-time, only expand to code that later on Neovim will use to interface with Packer at run-time.

The result of the previous example compiled to Lua would be:

local function _1_(use)
  use({"wbthomason/packer.nvim"})
  return use({opt = true, cmd = {"Dispatch", "Make", "Focus", "Start"}, "tpope/vim-dispatch"})
end
return (require("packer")).startup(_1_)
datwaft commented 2 years ago

I just did a quick test to see if the macros work between multiple files, and it seems they do, the only limitation is that you need to use include instead of require.

Here are the source files:

;; modules1.fnl
(import-macros {: pack!} :themis.pack)

(pack! "username1/package1")
(pack! "username1/package2")

;; modules2.fnl
(import-macros {: pack!} :themis.pack)

(pack! "username2/package1")
(pack! "username2/package2")

;; modules.fnl
(import-macros {: pack!
                : unpack!} :themis.pack)

(include "modules1")
(include "modules2")

(pack! "username/package1")
(pack! "username/package2")

(unpack!)

Here is the output:

package.preload["modules2"] = package.preload["modules2"] or function(...)
  return nil
end
package.preload["modules1"] = package.preload["modules1"] or function(...)
  return nil
end
require("modules1")
require("modules2")
local function _1_(use)
  use({"username1/package1"})
  use({"username1/package2"})
  use({"username2/package1"})
  use({"username2/package2"})
  use({"username/package1"})
  return use({"username/package2"})
end
return (require("packer")).startup(_1_)
shaunsingh commented 2 years ago

Thanks for the help and explanations, the biggest issue I had with using include was that relative include wasn't working with hotpot, but that may be something on my end.