What Emacs' package.el can learn from Neovim's packer.nvim

Mehrad Mahmoudian published on
9 min, 1700 words


I used vim and Neovim for about a decade, and then during the past two years I have switched to Emacs (initially Doom Emacs, and now my own config). There are perhaps some things that I have brought with myself subconsciously from [neo]vim to my Emacs config. But in this article I want to briefly touch upon the thing that I wish Emacs, and especially package.el can adopt/learn from Neovim's Packer.

There are some good things about package.el, but the following are what I can think of while writing this article:

  1. It does the damn job! We all [hopefully] know that package manager's work is not easy. It should take care of installing and updating packages, and it should also allow the user to handle the orphan packages, and it should massively assist about dependency conflicts. I don't know how either of Packer or package.el are handling these cases under the hood though.
  2. It is fast and easy to use. I don't want to bring negative examples, but package.el is very out of the way of the user and it does what it does efficiently. It also provides sufficient amount of information about the packages states right from Emacs.
  3. It is shipped with Emacs.

All these said, there are few things that feels archaic about package.el, from how it looks, to how it behaves. Here are the things that I don't like about package.el:

  1. It is not by default asynchronous. This would make Emacs unresponsive and the whole update process time-consuming.
  2. It does not show what are the changes. Almost always each package update is based on some VCS (e.g git) commits, and some might also come with some change-log. It would be good to allow user to read these before attempting to update a package.
  3. It does not help the user to shortlist things. What I mean by this is that there are times that you are installing, removing, or updating packages, and more than one package is marked for that action. There is no particular view mode in package.el to just list these in a short-list and let the user ponder on them. The only time [that I know of] that the user is provided with a list is after (package-menu-mark-upgrade) and then (package-menu-execute), and that is only to ask for a single yes or no!

Out of these, the 1st and 2nd items are those that Packer does much better than package.el. Packer performs the update in an asynchronous way, and shows the process of updating. It also shows the changes directly from the commit messages. As for the 3rd item, Packer (unlike package.el) does not provide a catalogue of packages that user can read and choose from. Therefore it does not necessarily "need" to have such feature, but the package.el actually do provide a catalogue, and user can mark multiple packages and then perform some operations on them. For this reason it is imho essential to have a view mode that looks like something like dashboard.el or magit to show the user what is going to be updated, what removed and what installed, and let the user remove items from the list before performing the operation. Of course I'm not saying that this should be the default behavior, but it should be a feature for the sake of user-friendliness and usability.

The following is the output of one of updates with Packer:

               packer.nvim - finished in 3.313s
  Updated nvim-lualine/lualine.nvim: e80465d..c28a742
  URL: https://github.com/nvim-lualine/lualine.nvim
    c28a742 Fix: `searchcount` error (#1004) (#1005) (2 days ago)
    9170434 added colours to qf extension to distinguish quickfix and location list (#933) (5 days ago)
    0ddacf0 feat: allow specifying theme as function (#998) (5 days ago)
    4bfc6bc chore: autogen (vimdocs+formating) (5 days ago)
    44a0fba feat: add name+parent path option for component(filename) (#945) (5 days ago)

  Updated kyazdani42/nvim-tree.lua: 45400cd..d1410cb
  URL: https://github.com/kyazdani42/nvim-tree.lua
    d1410cb docs: :help for api.node (#2106) (31 hours ago)
    94e3b09 chore(deps): bump amannn/action-semantic-pull-request (#2107) (2 days ago)
    0ef3d46 feat(#1974): experimental.git.async see https://github.com/nvim-tree/nvim-tree.lua/issues/2104 (#2094) (2 days ago)
    7ad1c20 fix: `api.node.open.preview` should toggle directories (#2099) (2 days ago)
    0c9bfe7 feat(#2092): add api.node.navigate.open.next, prev (#2093) (2 days ago)
    7cd722f ci: ensure PR subjects follow semantic commit spec (#2096) (2 days ago)
    3e9509e fix(#2088): actions change dir enable false does not update tree root (#2095) (2 days ago)

  Updated kyazdani42/nvim-web-devicons: 267af2d..0568104
  URL: https://github.com/kyazdani42/nvim-web-devicons
    0568104 fix: use official icon for `nim` (#246) (3 days ago)
    aa962cd ci: ensure PR subjects follow semantic commit spec (#243) (3 days ago)
    d92b3f4 chore: update pre-commit hooks (#244) (5 days ago)

  Updated nvim-treesitter/nvim-treesitter: 0927565..411e771
  URL: https://github.com/nvim-treesitter/nvim-treesitter
    411e771 Update parsers: sql (6 hours ago)
    871f566 Update parsers: svelte, swift, vimdoc (30 hours ago)
    0a20d1b feat(ecma): use lua-match for jsdoc injections (2 days ago)
    9711139 docs: add a small note about local queries (2 days ago)
    6f5a4f6 feat: use `-bundle` to build parsers on macOS (2 days ago)
    cbfa7ca Update parsers: awk, tiger (3 days ago)
    06075ec fix(scala): Add missing locals definitions for scala (4 days ago)
    ee1d618 Update parsers: vhs (4 days ago)
    93fa5df chore(help)!: renamed to vimdoc (4 days ago)
    2f3113f fix(ruby): then blocks should not be `@conditional`, just "then" (5 days ago)
    d546bcd highlights(sql): add `full`, `any` and `some` keywords (5 days ago)

  Updated neovim/nvim-lspconfig: c345220..7179a46
  URL: https://github.com/neovim/nvim-lspconfig
    7179a46 docs: update server_configurations.md skip-checks: true (4 hours ago)
    ae3debc fix(sqls): deprecate sqls suggest sqlls instead (#2544) (4 hours ago)
    8dc63a4 docs: update server_configurations.md skip-checks: true (7 hours ago)
    de114a6 fix(dafny): add default cmd for Dafny 4 (#2541) (7 hours ago)
    0bc0c38 docs: update server_configurations.md skip-checks: true (4 days ago)
    623be4b docs(nil_ls): add a link to an example (#2540) (4 days ago)
    8cbfc30 docs: fix typo (4 days ago)
    94291c9 refactor: fix stylua lint error (#2535) (6 days ago)
    6125158 ci: removed unused packages (#2534) (6 days ago)

 Press 'q' to quit
 Press '<CR>' to show more info
 Press 'd' to show the diff
 Press 'r' to revert an update

As you can see, Packer's output is very lean, clean, and clear, and it contains almost all the information the user might need. I personally would have liked to see a box dedicated to each package, so that the package dev could use to inform the user about a breaking change. But even in the current state it is good enough.

I do acknowledge that in Emacs-land, packages don't usually break your workflow (at least haven't happened to me yet), but like any other software out there, the possibility is there, and it feels like living on a thin sheet of ice when updating packages on my work machine. Additionally, such view would show the user how active the package developers are and the users can start caring more about the FLOSS devs and try to support them in various ways.