6868--- you run |vim.pack.update()|.
6969---
7070--- Freeze plugin from being updated:
71- ---- Update 'init.lua' for plugin to have `version` set to current commit hash.
72- --- You can get it by running `vim.pack.update({ 'plugin-name' })` and yanking
73- --- the word describing current state (looks like `abc12345`).
71+ ---- Update 'init.lua' for plugin to have `version` set to current revision.
72+ --- Get it with `:=vim.pack.get({ 'plug-name' })[1].rev` (looks like `abc12345`).
7473---- |:restart|.
7574---
7675--- Unfreeze plugin to start receiving updates:
@@ -148,13 +147,13 @@ local function git_clone(url, path)
148147end
149148
150149--- @async
151- --- @param rev string
150+ --- @param ref string
152151--- @param cwd string
153152--- @return string
154- local function git_get_hash (rev , cwd )
155- -- Using `rev-list -1` shows a commit of revision , while `rev-parse` shows
156- -- hash of revision . Those are different for annotated tags.
157- return git_cmd ({ ' rev-list' , ' -1' , ' --abbrev-commit' , rev }, cwd )
153+ local function git_get_hash (ref , cwd )
154+ -- Using `rev-list -1` shows a commit of reference , while `rev-parse` shows
155+ -- hash of reference . Those are different for annotated tags.
156+ return git_cmd ({ ' rev-list' , ' -1' , ' --abbrev-commit' , ref }, cwd )
158157end
159158
160159--- @async
@@ -169,11 +168,14 @@ end
169168--- @param cwd string
170169--- @return string[]
171170local function git_get_branches (cwd )
171+ local def_branch = git_get_default_branch (cwd )
172172 local cmd = { ' branch' , ' --remote' , ' --list' , ' --format=%(refname:short)' , ' --' , ' origin/**' }
173173 local stdout = git_cmd (cmd , cwd )
174174 local res = {} --- @type string[]
175175 for l in vim .gsplit (stdout , ' \n ' ) do
176- res [# res + 1 ] = l :match (' ^origin/(.+)$' )
176+ local branch = l :match (' ^origin/(.+)$' )
177+ local pos = branch == def_branch and 1 or (# res + 1 )
178+ table.insert (res , pos , branch )
177179 end
178180 return res
179181end
182184--- @param cwd string
183185--- @return string[]
184186local function git_get_tags (cwd )
185- local cmd = { ' tag' , ' --list' , ' --sort=-v:refname' }
186- return vim .split (git_cmd ( cmd , cwd ) , ' \n ' )
187+ local tags = git_cmd ( { ' tag' , ' --list' , ' --sort=-v:refname' }, cwd )
188+ return tags == ' ' and {} or vim .split (tags , ' \n ' )
187189end
188190
189191-- Plugin operations ----------------------------------------------------------
@@ -323,48 +325,30 @@ local function normalize_plugs(plugs)
323325 return res
324326end
325327
326- --- @param names string[] ?
328+ --- @param names ? string[]
327329--- @return vim.pack.Plug[]
328330local function plug_list_from_names (names )
329- local all_plugins = M .get ()
331+ local p_data_list = M .get (names , { info = false } )
330332 local plug_dir = get_plug_dir ()
331333 local plugs = {} --- @type vim.pack.Plug[]
332- local used_names = {} --- @type table<string,boolean>
333- -- Preserve plugin order; might be important during checkout or event trigger
334- for _ , p_data in ipairs (all_plugins ) do
334+ for _ , p_data in ipairs (p_data_list ) do
335335 -- NOTE: By default include only active plugins (and not all on disk). Using
336336 -- not active plugins might lead to a confusion as default `version` and
337337 -- user's desired one might mismatch.
338- -- TODO(echasnovski): Consider changing this if/when there is lockfile.
339- --- @cast names string[]
340- if (not names and p_data .active ) or vim .tbl_contains (names or {}, p_data .spec .name ) then
338+ -- TODO(echasnovski): Change this when there is lockfile.
339+ if names ~= nil or p_data .active then
341340 plugs [# plugs + 1 ] = new_plug (p_data .spec , plug_dir )
342- used_names [p_data .spec .name ] = true
343341 end
344342 end
345343
346- if vim .islist (names ) and # plugs ~= # names then
347- --- @param n string
348- local unused = vim .tbl_filter (function (n )
349- return not used_names [n ]
350- end , names )
351- error (' The following plugins are not installed: ' .. table.concat (unused , ' , ' ))
352- end
353-
354344 return plugs
355345end
356346
357347--- @param p vim.pack.Plug
358348--- @param event_name ' PackChangedPre' | ' PackChanged'
359349--- @param kind ' install' | ' update' | ' delete'
360350local function trigger_event (p , event_name , kind )
361- local spec = vim .deepcopy (p .spec )
362- -- Infer default branch for fuller `event-data` (if possible)
363- -- Doing it only on event trigger level allows keeping `spec` close to what
364- -- user supplied without performance issues during startup.
365- spec .version = spec .version or (uv .fs_stat (p .path ) and git_get_default_branch (p .path ))
366-
367- local data = { kind = kind , spec = spec , path = p .path }
351+ local data = { kind = kind , spec = vim .deepcopy (p .spec ), path = p .path }
368352 api .nvim_exec_autocmds (event_name , { pattern = p .path , data = data })
369353end
370354
463447--- @param p vim.pack.Plug
464448local function resolve_version (p )
465449 local function list_in_line (name , list )
466- return # list == 0 and ' ' or (' \n ' .. name .. ' : ' .. table.concat (list , ' , ' ))
450+ return (' \n %s: %s ' ): format ( name , table.concat (list , ' , ' ))
467451 end
468452
469453 -- Resolve only once
@@ -987,25 +971,57 @@ end
987971
988972--- @inlinedoc
989973--- @class vim.pack.PlugData
990- --- @field spec vim.pack.SpecResolved A | vim.pack.Spec | with defaults made explicit.
991- --- @field path string Plugin ' s path on disk.
992974--- @field active boolean Whether plugin was added via | vim.pack.add ()| to current session.
975+ --- @field branches ? string[] Available Git branches (first is default ). Missing if ` info=false` .
976+ --- @field path string Plugin ' s path on disk.
977+ --- @field rev ? string Current Git revision. Missing if ` info=false` .
978+ --- @field spec vim.pack.SpecResolved A | vim.pack.Spec | with resolved ` name` .
979+ --- @field tags ? string[] Available Git tags. Missing if ` info=false` .
980+
981+ --- @class vim.pack.keyset.get
982+ --- @inlinedoc
983+ --- @field info boolean Whether to include extra plugin info. Default ` true` .
993984
994- --- Get data about all plugins managed by |vim.pack|
985+ --- @param p_data_list vim.pack.PlugData[]
986+ local function add_p_data_info (p_data_list )
987+ local funs = {} --- @type (async fun ()) []
988+ for i , p_data in ipairs (p_data_list ) do
989+ local path = p_data .path
990+ --- @async
991+ funs [i ] = function ()
992+ p_data .branches = git_get_branches (path )
993+ p_data .rev = git_get_hash (' HEAD' , path )
994+ p_data .tags = git_get_tags (path )
995+ end
996+ end
997+ --- @async
998+ local function joined_f ()
999+ async .join (n_threads , funs )
1000+ end
1001+ async .run (joined_f ):wait ()
1002+ end
1003+
1004+ --- Gets |vim.pack| plugin info, optionally filtered by `names`.
1005+ --- @param names ? string[] List of plugin names. Default : all plugins managed by | vim.pack | .
1006+ --- @param opts ? vim.pack.keyset.get
9951007--- @return vim.pack.PlugData[]
996- function M .get ()
1008+ function M .get (names , opts )
1009+ vim .validate (' names' , names , vim .islist , true , ' list' )
1010+ opts = vim .tbl_extend (' force' , { info = true }, opts or {})
1011+
9971012 -- Process active plugins in order they were added. Take into account that
9981013 -- there might be "holes" after `vim.pack.del()`.
9991014 local active = {} --- @type table<integer,vim.pack.Plug ? >
10001015 for _ , p_active in pairs (active_plugins ) do
10011016 active [p_active .id ] = p_active .plug
10021017 end
10031018
1004- --- @type vim.pack.PlugData[]
1005- local res = {}
1019+ local res = {} --- @type vim.pack.PlugData[]
1020+ local used_names = {} --- @type table<string,boolean>
10061021 for i = 1 , n_active_plugins do
1007- if active [i ] then
1022+ if active [i ] and ( not names or vim . tbl_contains ( names , active [ i ]. spec . name )) then
10081023 res [# res + 1 ] = { spec = vim .deepcopy (active [i ].spec ), path = active [i ].path , active = true }
1024+ used_names [active [i ].spec .name ] = true
10091025 end
10101026 end
10111027
@@ -1015,20 +1031,33 @@ function M.get()
10151031 local plug_dir = get_plug_dir ()
10161032 for n , t in vim .fs .dir (plug_dir , { depth = 1 }) do
10171033 local path = vim .fs .joinpath (plug_dir , n )
1018- if t == ' directory' and not active_plugins [path ] then
1034+ local is_in_names = not names or vim .tbl_contains (names , n )
1035+ if t == ' directory' and not active_plugins [path ] and is_in_names then
10191036 local spec = { name = n , src = git_cmd ({ ' remote' , ' get-url' , ' origin' }, path ) }
10201037 res [# res + 1 ] = { spec = spec , path = path , active = false }
1038+ used_names [n ] = true
10211039 end
10221040 end
1041+ end
1042+ async .run (do_get ):wait ()
10231043
1024- -- Make default `version` explicit
1025- for _ , p_data in ipairs (res ) do
1026- if not p_data .spec .version then
1027- p_data .spec .version = git_get_default_branch (p_data .path )
1044+ if names ~= nil then
1045+ -- Align result with input
1046+ local names_order = {} --- @type table<string,integer>
1047+ for i , n in ipairs (names ) do
1048+ if not used_names [n ] then
1049+ error ((' Plugin `%s` is not installed' ):format (tostring (n )))
10281050 end
1051+ names_order [n ] = i
10291052 end
1053+ table.sort (res , function (a , b )
1054+ return names_order [a .spec .name ] < names_order [b .spec .name ]
1055+ end )
1056+ end
1057+
1058+ if opts .info then
1059+ add_p_data_info (res )
10301060 end
1031- async .run (do_get ):wait ()
10321061
10331062 return res
10341063end
0 commit comments