Skip to content

Instantly share code, notes, and snippets.

@ekickx
Last active January 10, 2022 01:56
Show Gist options
  • Save ekickx/22a1ee5735aa80aca9a908121a04945f to your computer and use it in GitHub Desktop.
Save ekickx/22a1ee5735aa80aca9a908121a04945f to your computer and use it in GitHub Desktop.
Deferred/Lazy Load Neovim Plugin with Dep

The idea of deferred load is we priotize to instantly load plugin that we need on startup. And load plugin that we don't need later. So yeah it's like what zinit does with their turbo mode. In Neovim, we can achive this with vim.defer_fn

In case you don't know, dep is a neovim plugin manager. The reason I choose it instead of packer is its ability to load plugins in consistent order.

Since you already got the gist of what will we do, let's jump to the code.


I will dump all of the code in init.lua to make it simpler.

Bootstrap

-- Install dep if it's not exist
local dep_bootstrap = function()
  local path = vim.fn.stdpath('data') .. '/site/pack/deps/opt/dep'

  if vim.fn.empty(vim.fn.glob(path)) > 0 then
    vim.fn.system {
      'git', 'clone', '--depth=1', 'https://github.com/chiyadev/dep', path
    }
  end

  vim.cmd 'packadd dep'
end
 
local dep_load = function(plugins)
  dep_bootstrap()
  require 'dep'(plugins)
end

dep_load {
  {
    'NTBBloodbath/doom-one.nvim',
    function()
      vim.cmd 'colorscheme doom-one'
    end
  }
}

We will load doom-one colorscheme instantly on startup

Defer Load

local dep_load = function(plugins)
  dep_bootstrap()
  require 'dep'(plugins)
end

-- Run dep_load `wait_time`ms later after we call it
local dep_defer_load = function(plugins, wait_time)
  wait_time = wait_time or 0
  vim.defer_fn(function()
    dep_load(plugins)
  end, wait_time)
end

dep_load {
  {
    'NTBBloodbath/doom-one.nvim',
    function()
      vim.cmd 'colorscheme doom-one'
    end
  }
}

dep_defer_load({
  {
    'lewis6991/gitsigns.nvim',
    function()
      require 'gitsigns'.setup()
    end,
    requires = 'nvim-lua/plenary.nvim',
  }
}, 700)

That means dep will load gitsigns 700ms or 0.7s later after doom-one loaded

Okay that's basically how we do deferred load…

…But wait

Let's look back at the code above. It implies:

🧑‍💻: "Hey Dep, please load `doom-one` on startup for me. Oh and 0.7s later also load `gitsigns`"
📦: "Ok"

*On startup*
📦: *Load `doom-one`*

*0.7s later*
📦: *Load `gitsigns`*
📦: *Remove `doom-one`*

🧑‍💻: "Wait.. wait.. Why did you remove `doom-one`?"
📦: "Well you told me to load `gitsigns` and since you don't need `doom-one`, I removed it for you"

See what's wrong there? Dep has a nice feature that will auto-remove plugin that we don't initialize. It is logical since if we don't want to use a certain plugin we will not initialize it in require 'dep(<plugins_table>)'.If we don't use a certain plugin but still want to keep it, dep has disabled field for it. The point is we still need to initialize it.

So the code above should be:

dep_load {
  {
    'NTBBloodbath/doom-one.nvim',
    function()
      vim.cmd 'colorscheme doom-one'
    end
  }
}

dep_defer_load({
  {
    'NTBBloodbath/doom-one.nvim',
    function()
      vim.cmd 'colorscheme doom-one'
    end
  },
  {
    'lewis6991/gitsigns.nvim',
    function()
      require 'gitsigns'.setup()
    end,
    requires = 'nvim-lua/plenary.nvim',
  }
}, 700)

But it will be a hassle if we should manually re-initialize every plugin that has been initialized before. Therefore, we are going to make function that automate the re-initialization. The gist is after we initialize plugin/s, we can save it to a table for future use, let's call it future_plugins. Next if we're going to load another plugin/s, we will append list of plugin/s that we want to load to future_plugins. Now future_plugins contain our current plugins and can be used on the future. And so we load current future_plugins. Zamn that's how we do re-initialization.

Next off let's write the actual code:

-- Create global table
_G.Dep_future_plugins = {}

-- Append 2nd table to 1st table
local tbl_append = function(tbl_1, tbl_2)
  local result = {unpack(tbl_1)}
  table.move(tbl_2, 1, #tbl_2, #result + 1, result)
  return result
end

local dep_load = function(plugins)
  _G.Dep_future_plugins = tbl_append(_G.Dep_future_plugins, plugins)
  dep_bootstrap()
  require 'dep'(_G.Dep_future_plugins)
end

Here you go, now here is the demo (If it looks laggy it is because of the video recorder. It didn't even show me pressing :q):

Demo

Look at first doom-one loaded first and 0.7s later doom-one and gitsigns loaded

_G.Dep_future_plugins = {}
-- Append 2nd table to 1st table
local tbl_append = function(tbl_1, tbl_2)
local result = {unpack(tbl_1)}
table.move(tbl_2, 1, #tbl_2, #result + 1, result)
return result
end
-- Install dep if it's not exist
local dep_bootstrap = function()
local path = vim.fn.stdpath('data') .. '/site/pack/deps/opt/dep'
if vim.fn.empty(vim.fn.glob(path)) > 0 then
vim.fn.system {
'git', 'clone', '--depth=1', 'https://github.com/chiyadev/dep', path
}
end
vim.cmd 'packadd dep'
end
local dep_load = function(plugins)
_G.Dep_future_plugins = tbl_append(_G.Dep_future_plugins, plugins)
dep_bootstrap()
require 'dep'(_G.Dep_future_plugins)
end
-- Run dep_load `wait_time`ms later after we call it
local dep_defer_load = function(plugins, wait_time)
wait_time = wait_time or 0
vim.defer_fn(function()
dep_load(plugins)
end, wait_time)
end
dep_load {
{
'NTBBloodbath/doom-one.nvim',
function()
vim.cmd 'colorscheme doom-one'
end
}
}
dep_defer_load({
{
'lewis6991/gitsigns.nvim',
function()
require 'gitsigns'.setup()
end,
requires = 'nvim-lua/plenary.nvim',
}
}, 700)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment