wbthomason / packer.nvim

A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config
MIT License
7.87k stars 267 forks source link

feat(lazy): add lazy loader #1072

Closed akinsho closed 1 year ago

akinsho commented 2 years ago

This PR adds in a lazy loading module for packer which is entirely based on TJ's project https://github.com/tjdevries/lazy.nvim. It also adds in @module syntax for the lazy loaded modules so that whilst using sumneko lsp these imports still have the correct types.

It should reduce the defer the cost of requiring modules till they are actually used which hopefully will improve startup times for packer.

Ideally all imports should now use this and can then be added at the top of the file rather than several adhoc requires inside of functions, the added value of which is that you can see at a glance what modules a module depends on

MurdeRM3L0DY commented 2 years ago

Helo. I thought I would share the lazy module I use:

local lazy = {}

lazy.require = function(modname)
  local ref = nil
  local init = false

  local init_ref = function()
    if init then
      return
    end

    ref = require(modname)
    init = true
  end

  return setmetatable({}, {
    __call = function(_, ...)
      init_ref()

      return ref(...)
    end,

    __index = function(_, k)
      init_ref()

      return ref[k]
    end,

    __newindex = function(_, k, v)
      init_ref()

      ref[k] = v
    end,
  })
end

lazy.access = function(table_or_modname, key)
  local ref = type(table_or_modname) == 'string' and lazy.require(table_or_modname)
    or table_or_modname

  return setmetatable({}, {
    __call = function(_, ...)
      return ref[key](...)
    end,

    __index = function(_, k)
      return ref[key][k]
    end,

    __newindex = function(_, k, v)
      ref[key][k] = v
    end,
  })
end

return lazy

The advantage with this is you can use lazy.access at the top level without requiring the underlying module. For instance:

local lazy = require 'packer.lazy'

local a = lazy.require 'packer.async'
local async = lazy.access(a, 'sync')
local await = lazy.access(a, 'wait')
folke commented 2 years ago

@akinsho I found an easier way to get proper type annotations in those files.

Use lazy.require as:

local require = require 'packer.lazy'.require

local foo = require("foo")

Making it a local won't mess with the global require, you can keep any require statements as is and sumneko will still give annoations as if it was the global require that was used. This way you don't need to add the ---@mod annotations anywhere.