mirror of
https://github.com/neovim/neovim.git
synced 2026-07-14 05:10:36 +00:00
feat(server): add v:useractive, use it in serverlist(info=true) #39423
Problem:
When showing the :connect menu, it is useful to know which servers
are most-recently active. But we don't have a good way to detect that.
Solution:
- Introduce `v:useractive`.
- Include this timestamp in `serverlist({info=true})`.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Signed-off-by: Szymon Wilczek <swilczek.lx@gmail.com>
This commit is contained in:
@@ -241,6 +241,7 @@ VIMSCRIPT
|
||||
|
||||
• |v:exitreason| is set before |QuitPre|.
|
||||
• |v:starttime| is the process start time (nanoseconds since UNIX epoch).
|
||||
• |v:useractive| timestamp of the most recent user activity.
|
||||
• |serverlist()| with `info=true` returns details for each server (own + peers).
|
||||
|
||||
==============================================================================
|
||||
|
||||
@@ -9142,12 +9142,11 @@ serverlist([{opts}]) *serverlist()*
|
||||
info : If |TRUE|, return a list of Dicts with detailed info
|
||||
instead of addresses. Implies `peer=true`. Each Dict
|
||||
has the following items:
|
||||
addr (string) Server address.
|
||||
pid (number?) PID of the Nvim process owning the
|
||||
server, or |v:null| if the peer is
|
||||
unreachable.
|
||||
own (bool) Whether this server belongs to the
|
||||
current Nvim instance.
|
||||
addr (string) Server address.
|
||||
pid (number) PID of the Nvim process.
|
||||
own (bool) Whether this server belongs to
|
||||
the current Nvim instance.
|
||||
active (number) |v:useractive| of the server.
|
||||
(default: |FALSE|)
|
||||
|
||||
Example: >vim
|
||||
|
||||
@@ -754,6 +754,14 @@ v:true
|
||||
operator) and to one when used as a Number (e.g. in |expr5| or
|
||||
|expr7| when used with numeric operators). Read-only.
|
||||
|
||||
*v:useractive* *useractive-variable*
|
||||
v:useractive
|
||||
Timestamp indicating the most recent user activity.
|
||||
Updated every time a key is received from a UI
|
||||
(TUI keystrokes or RPC |nvim_input()|).
|
||||
Initialized to 0 (no user activity since startup).
|
||||
Read-only.
|
||||
|
||||
*v:val* *val-variable*
|
||||
v:val
|
||||
Value of the current item of a |List| or |Dictionary|. Only
|
||||
|
||||
@@ -6,8 +6,9 @@ local M = {}
|
||||
---
|
||||
---@class vim.ServerInfo
|
||||
---@field addr string Server address (socket path, named pipe, or TCP host:port).
|
||||
---@field pid integer? PID of the Nvim process owning this server (nil if unreachable).
|
||||
---@field pid integer PID of the Nvim process.
|
||||
---@field own boolean True if this server belongs to the current Nvim instance.
|
||||
---@field active integer Timestamp of last user activity.
|
||||
|
||||
--- @param opts? table Options:
|
||||
--- - opts.peer (boolean): If true, also discover peer servers.
|
||||
@@ -42,6 +43,7 @@ function M.serverlist(opts, addrs)
|
||||
end, { path = root, type = 'socket', limit = math.huge })
|
||||
|
||||
local peer_pids = {} ---@type table<string, integer>
|
||||
local peer_active = {} ---@type table<string, integer>
|
||||
|
||||
for _, socket in ipairs(socket_paths) do
|
||||
if not own_set[socket] and not vim.list_contains(addrs, socket) then
|
||||
@@ -57,6 +59,10 @@ function M.serverlist(opts, addrs)
|
||||
if ok_pid and type(pid) == 'number' then
|
||||
peer_pids[socket] = pid
|
||||
end
|
||||
local ok_la, la = pcall(vim.fn.rpcrequest, chan, 'nvim_get_vvar', 'useractive')
|
||||
if ok_la and type(la) == 'number' then
|
||||
peer_active[socket] = la
|
||||
end
|
||||
end
|
||||
end
|
||||
pcall(vim.fn.chanclose, chan)
|
||||
@@ -69,6 +75,7 @@ function M.serverlist(opts, addrs)
|
||||
end
|
||||
|
||||
local self_pid = vim.fn.getpid()
|
||||
local self_active = vim.v.useractive
|
||||
local result = {} ---@type vim.ServerInfo[]
|
||||
for _, addr in ipairs(addrs) do
|
||||
local own = own_set[addr] == true
|
||||
@@ -76,6 +83,7 @@ function M.serverlist(opts, addrs)
|
||||
addr = addr,
|
||||
pid = own and self_pid or peer_pids[addr],
|
||||
own = own,
|
||||
active = own and self_active or peer_active[addr],
|
||||
})
|
||||
end
|
||||
return result
|
||||
|
||||
11
runtime/lua/vim/_meta/vimfn.gen.lua
generated
11
runtime/lua/vim/_meta/vimfn.gen.lua
generated
@@ -8292,12 +8292,11 @@ function vim.fn.searchpos(pattern, flags, stopline, timeout, skip) end
|
||||
--- info : If |TRUE|, return a list of Dicts with detailed info
|
||||
--- instead of addresses. Implies `peer=true`. Each Dict
|
||||
--- has the following items:
|
||||
--- addr (string) Server address.
|
||||
--- pid (number?) PID of the Nvim process owning the
|
||||
--- server, or |v:null| if the peer is
|
||||
--- unreachable.
|
||||
--- own (bool) Whether this server belongs to the
|
||||
--- current Nvim instance.
|
||||
--- addr (string) Server address.
|
||||
--- pid (number) PID of the Nvim process.
|
||||
--- own (bool) Whether this server belongs to
|
||||
--- the current Nvim instance.
|
||||
--- active (number) |v:useractive| of the server.
|
||||
--- (default: |FALSE|)
|
||||
---
|
||||
--- Example: >vim
|
||||
|
||||
8
runtime/lua/vim/_meta/vvars.gen.lua
generated
8
runtime/lua/vim/_meta/vvars.gen.lua
generated
@@ -805,6 +805,14 @@ vim.v.throwpoint = ...
|
||||
--- @type boolean
|
||||
vim.v['true'] = ...
|
||||
|
||||
--- Timestamp indicating the most recent user activity.
|
||||
--- Updated every time a key is received from a UI
|
||||
--- (TUI keystrokes or RPC `nvim_input()`).
|
||||
--- Initialized to 0 (no user activity since startup).
|
||||
--- Read-only.
|
||||
--- @type integer
|
||||
vim.v.useractive = ...
|
||||
|
||||
--- Value of the current item of a `List` or `Dictionary`. Only
|
||||
--- valid while evaluating the expression used with `map()` and
|
||||
--- `filter()`. Read-only.
|
||||
|
||||
@@ -9974,12 +9974,11 @@ M.funcs = {
|
||||
info : If |TRUE|, return a list of Dicts with detailed info
|
||||
instead of addresses. Implies `peer=true`. Each Dict
|
||||
has the following items:
|
||||
addr (string) Server address.
|
||||
pid (number?) PID of the Nvim process owning the
|
||||
server, or |v:null| if the peer is
|
||||
unreachable.
|
||||
own (bool) Whether this server belongs to the
|
||||
current Nvim instance.
|
||||
addr (string) Server address.
|
||||
pid (number) PID of the Nvim process.
|
||||
own (bool) Whether this server belongs to
|
||||
the current Nvim instance.
|
||||
active (number) |v:useractive| of the server.
|
||||
(default: |FALSE|)
|
||||
|
||||
Example: >vim
|
||||
|
||||
@@ -217,6 +217,7 @@ static struct vimvar {
|
||||
VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO),
|
||||
VV(VV_STARTTIME, "starttime", VAR_NUMBER, VV_RO),
|
||||
VV(VV_EXITREASON, "exitreason", VAR_STRING, VV_RO),
|
||||
VV(VV_USERACTIVE, "useractive", VAR_NUMBER, VV_RO),
|
||||
};
|
||||
#undef VV
|
||||
|
||||
|
||||
@@ -137,4 +137,5 @@ typedef enum {
|
||||
VV_VIRTNUM,
|
||||
VV_STARTTIME,
|
||||
VV_EXITREASON,
|
||||
VV_USERACTIVE,
|
||||
} VimVarIndex;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "nvim/autocmd.h"
|
||||
#include "nvim/autocmd_defs.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/eval_defs.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
@@ -276,6 +280,10 @@ size_t input_enqueue(uint64_t chan_id, String keys)
|
||||
{
|
||||
current_ui = chan_id;
|
||||
|
||||
if (keys.size > 0) {
|
||||
set_vim_var_nr(VV_USERACTIVE, (varnumber_T)os_realtime());
|
||||
}
|
||||
|
||||
const char *ptr = keys.data;
|
||||
const char *end = ptr + keys.size;
|
||||
|
||||
|
||||
@@ -447,6 +447,16 @@ M.vars = {
|
||||
encoding. See |multi-lang|.
|
||||
]=],
|
||||
},
|
||||
useractive = {
|
||||
type = 'integer',
|
||||
desc = [=[
|
||||
Timestamp indicating the most recent user activity.
|
||||
Updated every time a key is received from a UI
|
||||
(TUI keystrokes or RPC |nvim_input()|).
|
||||
Initialized to 0 (no user activity since startup).
|
||||
Read-only.
|
||||
]=],
|
||||
},
|
||||
lc_time = {
|
||||
type = 'string',
|
||||
desc = [=[
|
||||
|
||||
@@ -231,25 +231,44 @@ describe('server', function()
|
||||
by_addr[entry.addr] = entry
|
||||
end
|
||||
|
||||
-- own server: own=true, pid matches
|
||||
-- own server: own=true, pid matches, active present
|
||||
local own_entry = by_addr[own_addr]
|
||||
eq(true, own_entry ~= nil)
|
||||
eq(true, own_entry.own)
|
||||
eq(self_pid, own_entry.pid)
|
||||
eq('number', type(own_entry.active))
|
||||
eq(eval('v:useractive'), own_entry.active)
|
||||
|
||||
-- peer server: own=false, pid is from peer process
|
||||
-- peer server: own=false, pid + active from peer process
|
||||
local peer_entry = by_addr[peer_addr]
|
||||
eq(true, peer_entry ~= nil)
|
||||
eq(false, peer_entry.own)
|
||||
eq('number', type(peer_entry.pid))
|
||||
eq('number', type(peer_entry.active))
|
||||
n.set_session(client)
|
||||
local peer_pid = fn.getpid()
|
||||
local peer_active = eval('v:useractive')
|
||||
n.set_session(current_server)
|
||||
eq(peer_pid, peer_entry.pid)
|
||||
eq(peer_active, peer_entry.active)
|
||||
|
||||
client:close()
|
||||
end)
|
||||
|
||||
it('v:useractive advances on UI input', function()
|
||||
clear()
|
||||
local before = eval('v:useractive')
|
||||
eq('number', type(before))
|
||||
n.feed('ix<Esc>')
|
||||
local after = eval('v:useractive')
|
||||
eq(true, after > before)
|
||||
end)
|
||||
|
||||
it('v:useractive is read-only', function()
|
||||
clear()
|
||||
matches('E46:', pcall_err(n.command, 'let v:useractive = 0'))
|
||||
end)
|
||||
|
||||
it('removes stale socket files automatically #36581', function()
|
||||
-- Windows named pipes are ephemeral kernel objects that are automatically
|
||||
-- cleaned up when the process terminates. Unix domain sockets persist as
|
||||
|
||||
Reference in New Issue
Block a user