patch 9.1.1651: Cannot use clientserver over socket

Problem:  Cannot use clientserver over Unix domain socket
Solution: Implement socketserver functionality (Foxe Chen).

fixes: #3509
closes: #17839

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Foxe Chen
2025-08-18 21:40:40 +02:00
committed by Christian Brabandt
parent 31170af24a
commit 96b2154b73
34 changed files with 2562 additions and 68 deletions

View File

@ -85,6 +85,12 @@ jobs:
- features: normal
compiler: gcc
extra: [vimtags]
- features: huge
compiler: gcc
extra: [no_x11]
- features: huge
compiler: gcc
extra: [socketserver]
steps:
- name: Checkout repository from github
@ -220,7 +226,7 @@ jobs:
tiny)
echo "TEST=testtiny"
if ${{ contains(matrix.extra, 'nogui') }}; then
echo "CONFOPT=--disable-gui"
CONFOPT="--disable-gui"
fi
;;
normal)
@ -232,10 +238,16 @@ jobs:
PYTHON3_CONFOPT="--with-python3-stable-abi=3.8"
fi
# The ubuntu-24.04 CI runner does not provide a python2 package.
echo "CONFOPT=--enable-perlinterp=${INTERFACE} --enable-pythoninterp=no --enable-python3interp=${INTERFACE} --enable-rubyinterp=${INTERFACE} --enable-luainterp=${INTERFACE} --enable-tclinterp=${INTERFACE} ${PYTHON3_CONFOPT}"
CONFOPT="--enable-perlinterp=${INTERFACE} --enable-pythoninterp=no --enable-python3interp=${INTERFACE} --enable-rubyinterp=${INTERFACE} --enable-luainterp=${INTERFACE} --enable-tclinterp=${INTERFACE} ${PYTHON3_CONFOPT}"
;;
esac
if ${{ contains(matrix.extra, 'no_x11') }}; then
CONFOPT="${CONFOPT} --without-x --disable-gui"
fi
if ${{ contains(matrix.extra, 'socketserver') }}; then
CONFOPT="${CONFOPT} --enable-socketserver"
fi
if ${{ matrix.coverage == true }}; then
CFLAGS="${CFLAGS} --coverage -DUSE_GCOV_FLUSH"
echo "LDFLAGS=--coverage"
@ -259,6 +271,7 @@ jobs:
echo "TEST=-C runtime/doc vimtags VIMEXE=../../${SRCDIR}/vim"
fi
echo "CFLAGS=${CFLAGS}"
echo "CONFOPT=${CONFOPT}"
# Disables GTK attempt to integrate with the accessibility service that does run in CI.
echo "NO_AT_BRIDGE=1"
) >> $GITHUB_ENV

View File

@ -13046,6 +13046,7 @@ scrollbind Compiled with 'scrollbind' support. (always true)
showcmd Compiled with 'showcmd' support.
signs Compiled with |:sign| support.
smartindent Compiled with 'smartindent' support. (always true)
socketserver Compiled with socket server functionality. (Unix only)
sodium Compiled with libsodium for better crypt support
sound Compiled with sound support, e.g. `sound_playevent()`
spell Compiled with spell checking support |spell|.

View File

@ -1,4 +1,4 @@
*remote.txt* For Vim version 9.1. Last change: 2022 Feb 17
*remote.txt* For Vim version 9.1. Last change: 2025 Aug 18
VIM REFERENCE MANUAL by Bram Moolenaar
@ -61,7 +61,10 @@ The following command line arguments are available:
--servername {name} Become the server {name}. When used together
with one of the --remote commands: connect to
server {name} instead of the default (see
below). The name used will be uppercase.
below). The name used will be uppercase. If
using the socketserver, you can specify a
path, see |socketserver-name| for more
details.
*--remote-send*
--remote-send {keys} Send {keys} to server and exit. The {keys}
are not mapped. Special key names are
@ -72,6 +75,12 @@ The following command line arguments are available:
on stdout.
*--serverlist*
--serverlist Output a list of server names.
*--clientserver*
--clientserver {method} Use the specified method {method} as the
backend for clientserver functionality. Can
either be "socket" or "x11".
{only available when Vim is compiled with both
|+X11| and |+socketserver| features}
Examples ~
@ -105,7 +114,8 @@ specified name is not available, a postfix is applied until a free name is
encountered, i.e. "gvim1" for the second invocation of gvim on a particular
X-server. The resulting name is available in the servername builtin variable
|v:servername|. The case of the server name is ignored, thus "gvim" and
"GVIM" are considered equal.
"GVIM" are considered equal. Note if a socket server is being used, there are
some differences, see |socketserver-differences|.
When Vim is invoked with --remote, --remote-wait or --remote-send it will try
to locate the server name determined by the invocation name and --servername
@ -119,7 +129,8 @@ itself. This way it is not necessary to know whether gvim is already started
when sending command to it.
The --serverlist argument will cause Vim to print a list of registered command
servers on the standard output (stdout) and exit.
servers on the standard output (stdout) and exit. If a socket server is being
used, there are caveats, see |socketserver-differences|.
*{server}*
The {server} argument is used by several functions. When this is an empty
string then on Unix the default server name is used, which is "GVIM". On
@ -206,4 +217,64 @@ When using gvim, the --remote-wait only works properly this way: >
start /w gvim --remote-wait file.txt
<
==============================================================================
3. Socket server specific items *socketserver-clientserver*
*E1563* *E1564* *E1565* *E1566* *E1567*
The communication between client and server is done using Unix domain sockets.
These sockets are either placed in these directories in the following order of
availability:
1. "$XDG_RUTIME_DIR/vim" if $XDG_RUNTIME_DIR is set in the environment.
2. "$TMPDIR/vim-[uid]", where "[uid]" is the uid of the user. This
directory will have the access permissions set to 700 so only the user
can read or write from/to it. If $TMPDIR is not set, "/tmp" is used.
*socketserver-name*
When specifying the server id/name, it can be taken as a generic name or an
absolute or relative path. If the server id starts with either a "/"
(absolute) or "./" | "../" (relative), then it is taken as path to the socket.
Otherwise the server id will be the filename of the socket which will be
placed in the above common directories. Note that a server id/name can only
contain slashes "/" if it is taken as a path, so names such as "abc/dir" will
be invalid.
Socket server functionality is available in both GTK GUI and terminal versions of
Vim. Unless Vim is compiled with |+autoservername| feature, the socket server
will have to started explicitly, just like X11, even in the GUI.
If Vim crashes or does not exit cleanly, the socket server will not remove the
socket file and it will be left around. This is generally not a problem,
because if a socket name is taken, Vim checks if the socket in its place is
dead (not attached to any process), and can replace it instead of finding a
new name.
To send commands to a Vim socket server from another application, read the
source file src/os_unix.c, there is detailed description of the protocol used.
*socketserver-differences*
Most of the functionality is the same as X11, however unlike X11, where the
client does not need to be a server in order to communicate with another
server, the socket server requires the server to be running even as a client.
The exception is |serverlist()| or the |--serverlist| argument, which does not
require the server to be running.
Additionally, the server id or client id will not be a number like X11 or
MS-Windows (shown in hex representation), instead it is the absolute path to
the socket. This can be seen via the |v:servername| variable.
The |--serverlist| argument will act just like X11, however it only checks the
given common directories above. If a custom path is used for a socket, it
will not be detected, such as a path either not in $XDG_RUNTIME_DIR or
<$TMPDIR or /tmp>/vim of the |--serverlist| Vim process.
If you have both |+socketserver| and |+X11| compiled, you will need to add
|--clientserver| set to "socket" in combination with |--serverlist| to list
the available servers. You cannot list both types of backends in one command.
*socketserver-x11*
If Vim is compiled with both |+X11| and |+socketserver|, then deciding which
backend to use is done at startup time, via the |--clientserver| argument. By
default if it is not specified, then X11 will be used. A Vim instance using a
socket server cannot communicate with one using X11.
vim:tw=78:sw=4:ts=8:noet:ft=help:norl:

View File

@ -1497,6 +1497,7 @@ $quote eval.txt /*$quote*
+scrollbind various.txt /*+scrollbind*
+signs various.txt /*+signs*
+smartindent various.txt /*+smartindent*
+socketserver various.txt /*+socketserver*
+sodium various.txt /*+sodium*
+sound various.txt /*+sound*
+spell various.txt /*+spell*
@ -1557,6 +1558,7 @@ $quote eval.txt /*$quote*
-- starting.txt /*--*
--- starting.txt /*---*
--clean starting.txt /*--clean*
--clientserver remote.txt /*--clientserver*
--cmd starting.txt /*--cmd*
--echo-wid starting.txt /*--echo-wid*
--gui-dialog-file starting.txt /*--gui-dialog-file*
@ -4697,6 +4699,11 @@ E156 sign.txt /*E156*
E1560 vim9.txt /*E1560*
E1561 vim9.txt /*E1561*
E1562 options.txt /*E1562*
E1563 remote.txt /*E1563*
E1564 remote.txt /*E1564*
E1565 remote.txt /*E1565*
E1566 remote.txt /*E1566*
E1567 remote.txt /*E1567*
E157 sign.txt /*E157*
E158 sign.txt /*E158*
E159 sign.txt /*E159*
@ -10225,6 +10232,10 @@ slow-fast-terminal term.txt /*slow-fast-terminal*
slow-start starting.txt /*slow-start*
slow-terminal term.txt /*slow-terminal*
socket-interface channel.txt /*socket-interface*
socketserver-clientserver remote.txt /*socketserver-clientserver*
socketserver-differences remote.txt /*socketserver-differences*
socketserver-name remote.txt /*socketserver-name*
socketserver-x11 remote.txt /*socketserver-x11*
sort() builtin.txt /*sort()*
sorting change.txt /*sorting*
sound-functions usr_41.txt /*sound-functions*

View File

@ -1,4 +1,4 @@
*various.txt* For Vim version 9.1. Last change: 2025 Aug 06
*various.txt* For Vim version 9.1. Last change: 2025 Aug 18
VIM REFERENCE MANUAL by Bram Moolenaar
@ -487,6 +487,8 @@ m *+ruby/dyn* Ruby interface |ruby-dynamic| |/dyn|
T *+scrollbind* 'scrollbind'
N *+signs* |:sign|
T *+smartindent* 'smartindent'
N *+socketserver* Unix only: socket server backend for clientserver
functionality
H *+sodium* compiled with libsodium for better encryption support
H *+sound* |sound_playevent()|, |sound_playfile()| functions, etc.
N *+spell* spell checking support, see |spell|

View File

@ -41745,6 +41745,10 @@ Others: ~
Unicode 16.
- Two additional digraphs have been added: LEFT ANGLE BRACKET "<[" and RIGHT
ANGLE BRACKET "]>".
- Support for Unix domain sockets have been added for the clientserver
feature, see |socketserver-clientserver|.
Platform specific ~
- MS-Winodws: Paths like "\Windows" and "/Windows" are now considered to be
absolute paths (to the current drive) and no longer relative.
@ -41849,6 +41853,7 @@ Options: ~
Vim Arguments: ~
|-Y| Do not connect to the Wayland compositor.
|--clientserver| Specify backend for clientserver functionality.
==============================================================================

View File

@ -499,7 +499,14 @@ List the names of all Vim servers that can be found.
.TP
\-\-servername {name}
Use {name} as the server name. Used for the current Vim, unless used with a
\-\-remote argument, then it's the name of the server to connect to.
\-\-remote argument, then it's the name of the server to connect to. If the
socketserver backend is being used, if the name starts with "/", "./", or "../",
it is taken as either an absolute, relative or relative path to the socket.
.TP
\-\-clientserver {backend}
Use {backend} as the backend for clientserver functionality, either "socket" or
"x11" respectively. Only available when compiled with both socketserver and X11
features present
.TP
\-\-socketid {id}
GTK GUI only: Use the GtkPlug mechanism to run gVim in another window.

View File

@ -378,7 +378,16 @@ OPTIONS
--servername {name}
Use {name} as the server name. Used for the current Vim,
unless used with a --remote argument, then it's the name of
the server to connect to.
the server to connect to. If the socketserver backend is
being used, if the name starts with "/", "./", or "../", it
is taken as either an absolute, relative or relative path
to the socket.
--clientserver {backend}
Use {backend} as the backend for clientserver functional
ity, either "socket" or "x11" respectively. Only available
when compiled with both socketserver and X11 features
present
--socketid {id}
GTK GUI only: Use the GtkPlug mechanism to run gVim in an

View File

@ -16,9 +16,10 @@ function SetupRemoteReplies()
let max = argc()
let id = expand("<client>")
if id == 0
if (type(id) == v:t_number && id == 0) || (type(id) == v:t_string && id == '')
return
endif
while cnt < max
" Handle same file from more clients and file being more than once
" on the command line by encoding this stuff in the group name

View File

@ -2,7 +2,7 @@
" Language: Vim script
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
" Doug Kearns <dougkearns@gmail.com>
" Last Change: 2025 Aug 16
" Last Change: 2025 Aug 18
" Former Maintainer: Charles E. Campbell
" DO NOT CHANGE DIRECTLY.

36
src/auto/configure vendored
View File

@ -854,6 +854,7 @@ enable_netbeans
enable_channel
enable_terminal
enable_autoservername
enable_socketserver
enable_multibyte
enable_rightleft
enable_arabic
@ -1534,6 +1535,7 @@ Optional Features:
--disable-channel Disable process communication support.
--enable-terminal Enable terminal emulation support.
--enable-autoservername Automatically define servername at vim startup.
--enable-socketserver Use sockets for clientserver communication.
--enable-multibyte Include multibyte editing support.
--disable-rightleft Do not include Right-to-Left language support.
--disable-arabic Do not include Arabic language support.
@ -9084,6 +9086,40 @@ if test "$enable_autoservername" = "yes"; then
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-socketserver argument" >&5
printf %s "checking --enable-socketserver argument... " >&6; }
# Check whether --enable-socketserver was given.
if test ${enable_socketserver+y}
then :
enableval=$enable_socketserver; enable_socketserver=$enableval
else case e in #(
e) if test "x$features" = xtiny
then :
enable_socketserver=no_auto
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cannot use socketserver with tiny features" >&5
printf "%s\n" "cannot use socketserver with tiny features" >&6; }
else case e in #(
e) enable_socketserver=auto ;;
esac
fi ;;
esac
fi
if test "$enable_socketserver" = "yes"; then
printf "%s\n" "#define WANT_SOCKETSERVER 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
elif test "$enable_socketserver" = "auto"; then
printf "%s\n" "#define MAYBE_SOCKETSERVER 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: auto" >&5
printf "%s\n" "auto" >&6; }
elif test "$enable_socketserver" = "no"; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-multibyte argument" >&5
printf %s "checking --enable-multibyte argument... " >&6; }
# Check whether --enable-multibyte was given.

View File

@ -15,6 +15,11 @@
#if defined(FEAT_CLIENTSERVER) || defined(PROTO)
#ifdef FEAT_SOCKETSERVER
# include <sys/socket.h>
# include "sys/un.h"
#endif
static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u **serverStr);
static char_u *serverMakeName(char_u *arg, char *cmd);
@ -191,6 +196,8 @@ static char_u *build_drop_cmd(int filec, char **filev, int tabs, int sendReply);
void
exec_on_server(mparm_T *parmp)
{
int made_name = FALSE;
if (parmp->serverName_arg != NULL && *parmp->serverName_arg == NUL)
return;
@ -199,6 +206,23 @@ exec_on_server(mparm_T *parmp)
serverInitMessaging();
# endif
#ifdef FEAT_SOCKETSERVER
// If servername is specified and we are using sockets, always init the
// sockt server. We may need to receive replies back to us. If --serverlist
// is passed, the socket server will be uninitialized before listing
// sockets then initialized after. This is so we don't add our own socket
// in the list. This does not happen in serverlist().
if ((parmp->serverArg || parmp->serverName_arg != NULL) &&
clientserver_method == CLIENTSERVER_METHOD_SOCKET)
{
parmp->servername = serverMakeName(parmp->serverName_arg,
parmp->argv[0]);
if (socket_server_init(parmp->servername) == OK)
TIME_MSG("initialize socket server");
made_name = TRUE;
}
#endif
/*
* When a command server argument was found, execute it. This may
* exit Vim when it was successful. Otherwise it's executed further
@ -214,8 +238,9 @@ exec_on_server(mparm_T *parmp)
// If we're still running, get the name to register ourselves.
// On Win32 can register right now, for X11 need to setup the
// clipboard first, it's further down.
parmp->servername = serverMakeName(parmp->serverName_arg,
parmp->argv[0]);
if (!made_name && parmp->servername == NULL)
parmp->servername = serverMakeName(parmp->serverName_arg,
parmp->argv[0]);
# ifdef MSWIN
if (parmp->servername != NULL)
{
@ -224,14 +249,13 @@ exec_on_server(mparm_T *parmp)
}
# endif
}
/*
* Prepare for running as a Vim server.
*/
void
prepare_server(mparm_T *parmp)
{
# if defined(FEAT_X11)
# if defined(FEAT_X11) || defined(FEAT_SOCKETSERVER)
/*
* Register for remote command execution with :serversend and --remote
* unless there was a -X or a --servername '' on the command line.
@ -239,7 +263,13 @@ prepare_server(mparm_T *parmp)
* or when compiling with autoservername.
* When running as root --servername is also required.
*/
if (X_DISPLAY != NULL && parmp->servername != NULL && (
if (
# ifdef FEAT_X11
X_DISPLAY != NULL &&
# endif
parmp->servername != NULL && (
# if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI)
(
# if defined(FEAT_AUTOSERVERNAME)
@ -254,12 +284,26 @@ prepare_server(mparm_T *parmp)
# endif
parmp->serverName_arg != NULL))
{
(void)serverRegisterName(X_DISPLAY, parmp->servername);
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
{
if (socket_server_init(parmp->servername) == OK)
TIME_MSG("initialize socket server");
}
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
{
(void)serverRegisterName(X_DISPLAY, parmp->servername);
TIME_MSG("register x11 server name");
}
# endif
vim_free(parmp->servername);
TIME_MSG("register server name");
}
#ifdef FEAT_X11
else
serverDelayedStartName = parmp->servername;
#endif
# endif
/*
@ -299,9 +343,12 @@ cmdsrv_main(
#define ARGTYPE_SEND 3
int silent = FALSE;
int tabs = FALSE;
# ifndef FEAT_X11
#ifdef FEAT_SOCKETSERVER
char_u *receiver;
#endif
# ifdef MSWIN
HWND srv;
# else
# elif defined(FEAT_X11)
Window srv;
setup_term_clip();
@ -384,16 +431,27 @@ cmdsrv_main(
}
Argc = i;
}
# ifdef FEAT_X11
if (xterm_dpy == NULL)
#ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
ret = socket_server_send(
sname, *serverStr, NULL, &receiver,
0, -1, silent);
#endif
#ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
{
mch_errmsg(_("No display"));
ret = -1;
if (xterm_dpy == NULL)
{
mch_errmsg(_("No display"));
ret = -1;
}
else
ret = serverSendToVim(xterm_dpy, sname, *serverStr,
NULL, &srv, 0, 0, 0, silent);
}
else
ret = serverSendToVim(xterm_dpy, sname, *serverStr,
NULL, &srv, 0, 0, 0, silent);
# else
#endif
#ifdef MSWIN
// Win32 always works?
ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent);
# endif
@ -452,14 +510,24 @@ cmdsrv_main(
vim_memset(done, 0, numFiles);
while (memchr(done, 0, numFiles) != NULL)
{
char_u *p;
char_u *p = NULL;
int j;
# ifdef MSWIN
p = serverGetReply(srv, NULL, TRUE, TRUE, 0);
if (p == NULL)
break;
# else
if (serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0)
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET
&& socket_server_read_reply(receiver, &p, -1) == FAIL)
break;
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11
&& serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0)
break;
# endif
if (p == NULL)
break;
# endif
j = atoi((char *)p);
@ -490,12 +558,34 @@ cmdsrv_main(
if (serverSendToVim(sname, (char_u *)argv[i + 1],
&res, NULL, 1, 0, FALSE) < 0)
# else
if (xterm_dpy == NULL)
mch_errmsg(_("No display: Send expression failed.\n"));
else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1],
&res, NULL, 1, 0, 1, FALSE) < 0)
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
{
if (!socket_server_valid())
mch_errmsg(_("Socket server not online:"
"Send expression failed"));
else if (socket_server_send(sname, (char_u *)argv[i + 1],
&res, NULL, 1, 0, FALSE) < 0)
goto expr_fail;
}
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
{
if (xterm_dpy == NULL)
mch_errmsg(_("No display: Send expression failed.\n"));
else if (serverSendToVim(xterm_dpy, sname,
(char_u *)argv[i + 1], &res,
NULL, 1, 0, 1, FALSE) < 0)
goto expr_fail;
}
# endif
if (FALSE)
# endif
{
# if !defined(MSWIN)
expr_fail:
# endif
if (res != NULL && *res != NUL)
{
// Output error from remote
@ -511,8 +601,25 @@ cmdsrv_main(
// Win32 always works?
res = serverGetVimNames();
# else
if (xterm_dpy != NULL)
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
{
int was_init = socket_server_valid();
// Don't want to add ourselves to the list. So shutdown the
// server before listing then startup back again.
socket_server_uninit();
res = socket_server_list_sockets();
if (was_init)
socket_server_init(NULL);
}
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
xterm_dpy != NULL)
res = serverGetVimNames(xterm_dpy);
# endif
# endif
if (did_emsg)
mch_errmsg("\n");
@ -541,6 +648,9 @@ cmdsrv_main(
if (didone)
{
#ifdef FEAT_SOCKETSERVER
socket_server_uninit();
#endif
display_errors(); // display any collected messages
exit(exiterr); // Mission accomplished - get out
}
@ -694,7 +804,24 @@ serverMakeName(char_u *arg, char *cmd)
char_u *p;
if (arg != NULL && *arg != NUL)
{
#ifdef FEAT_SOCKETSERVER
// If we are using a socket server, we want to preserve the original
// name if it is a path, else uppercase it if its just a generic name.
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
{
if (arg[0] == '/' || STRNCMP(arg, "./", 2) == 0 ||
STRNCMP(arg, "../", 3) == 0)
p = vim_strsave(arg);
else
p = vim_strsave_up(arg);
}
else
p = vim_strsave_up(arg);
#else
p = vim_strsave_up(arg);
#endif
}
else
{
p = vim_strsave_up(gettail((char_u *)cmd));
@ -747,7 +874,12 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
# ifdef MSWIN
HWND w;
# else
#ifdef FEAT_X11
Window w;
#endif
#ifdef FEAT_SOCKETSERVER
char_u *client = NULL;
#endif
# endif
if (check_restricted() || check_secure())
@ -768,14 +900,33 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
# ifdef MSWIN
if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0)
# else
if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
0, TRUE) < 0)
#ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
if (socket_server_send(server_name, keys, &r, &client, expr,
timeout * 1000, TRUE) < 0)
goto stuff;
#endif
#ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
0, TRUE) < 0)
goto stuff;
#endif
# endif
#if !defined(MSWIN)
if (FALSE)
{
stuff:
#else
{
#endif
if (r != NULL)
{
emsg((char *)r); // sending worked but evaluation failed
vim_free(r);
#ifdef FEAT_SOCKETSERVER
vim_free(client);
#endif
}
else
semsg(_(e_unable_to_send_to_str), server_name);
@ -787,19 +938,39 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
if (argvars[2].v_type != VAR_UNKNOWN)
{
dictitem_T v;
#if defined(FEAT_SOCKETSERVER)
struct sockaddr_un addr;
char_u str[sizeof(addr.sun_path)];
#else
char_u str[30];
#endif
char_u *idvar;
idvar = tv_get_string_chk(&argvars[2]);
if (idvar != NULL && *idvar != NUL)
{
#ifdef MSWIN
sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
#else
#ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
#endif
#ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
vim_snprintf((char *)str, sizeof(addr.sun_path),
"%s", client);
#endif
#endif
v.di_tv.v_type = VAR_STRING;
v.di_tv.vval.v_string = vim_strsave(str);
set_var(idvar, &v.di_tv, FALSE);
vim_free(v.di_tv.vval.v_string);
}
}
#ifdef FEAT_SOCKETSERVER
vim_free(client);
#endif
}
#endif
@ -890,11 +1061,20 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
rettv->vval.v_number = (s != NULL);
}
# else
if (check_connection() == FAIL)
return;
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
rettv->vval.v_number = socket_server_peek_reply(serverid, &s);
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
{
if (check_connection() == FAIL)
return;
rettv->vval.v_number = serverPeekReply(X_DISPLAY,
serverStrToWin(serverid), &s);
rettv->vval.v_number = serverPeekReply(X_DISPLAY,
serverStrToWin(serverid), &s);
}
# endif
# endif
if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
@ -943,12 +1123,21 @@ f_remote_read(typval_T *argvars UNUSED, typval_T *rettv)
if (n != 0)
r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
if (r == NULL)
# else
if (check_connection() == FAIL
|| serverReadReply(X_DISPLAY, serverStrToWin(serverid),
&r, FALSE, timeout) < 0)
# endif
emsg(_(e_unable_to_read_server_reply));
# else
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
socket_server_read_reply(serverid, &r, timeout * 1000) == FAIL)
emsg(_(e_unable_to_read_server_reply));
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
(check_connection() == FAIL
|| serverReadReply(X_DISPLAY, serverStrToWin(serverid),
&r, FALSE, timeout) < 0))
emsg(_(e_unable_to_read_server_reply));
# endif
# endif
}
#endif
rettv->v_type = VAR_STRING;
@ -992,11 +1181,18 @@ f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
}
char_u *server = tv_get_string_chk(&argvars[0]);
# ifdef FEAT_X11
if (check_connection() == OK)
serverRegisterName(X_DISPLAY, server);
# else
# ifdef MSWIN
serverSetName(server);
# else
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
socket_server_init(server);
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
check_connection() == OK)
serverRegisterName(X_DISPLAY, server);
# endif
# endif
#else
@ -1026,13 +1222,30 @@ f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
if (server == NULL || reply == NULL)
return;
# ifdef FEAT_X11
if (check_connection() == FAIL)
return;
# endif
#ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
socket_server_send_reply(server, reply) == FAIL)
goto fail;
#endif
#ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
check_connection() == FAIL)
return;
if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
serverSendReply(server, reply) < 0)
#endif
#ifdef MSWIN
if (serverSendReply(server, reply) < 0)
#endif
#if defined(FEAT_SOCKETSERVER) && !defined(FEAT_X11) && !defined(MSWIN)
if (FALSE)
#endif
{
#ifdef FEAT_SOCKETSERVER
fail:
#endif
emsg(_(e_unable_to_send_to_client));
return;
}
@ -1051,9 +1264,18 @@ f_serverlist(typval_T *argvars UNUSED, typval_T *rettv)
# ifdef MSWIN
r = serverGetVimNames();
# else
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
r = socket_server_list_sockets();
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
{
make_connection();
if (X_DISPLAY != NULL)
r = serverGetVimNames(X_DISPLAY);
}
# endif
# endif
#endif
rettv->v_type = VAR_STRING;

View File

@ -406,6 +406,12 @@
/* Define if you want to always define a server name at vim startup. */
#undef FEAT_AUTOSERVERNAME
/* Define if you want to use sockets for clientserver communication. */
#undef WANT_SOCKETSERVER
/* Define if you want to use sockets for clientserver communication if it makes sense. */
#undef MAYBE_SOCKETSERVER
/* Define if you want to include fontset support. */
#undef FEAT_XFONTSET

View File

@ -2340,6 +2340,24 @@ if test "$enable_autoservername" = "yes"; then
AC_DEFINE(FEAT_AUTOSERVERNAME)
fi
AC_MSG_CHECKING(--enable-socketserver argument)
AC_ARG_ENABLE(socketserver,
[ --enable-socketserver Use sockets for clientserver communication.],
[enable_socketserver=$enableval],
AS_IF([test "x$features" = xtiny],
[enable_socketserver=no_auto
AC_MSG_RESULT([cannot use socketserver with tiny features])],
[enable_socketserver=auto]))
if test "$enable_socketserver" = "yes"; then
AC_DEFINE(WANT_SOCKETSERVER)
AC_MSG_RESULT([yes])
elif test "$enable_socketserver" = "auto"; then
AC_DEFINE(MAYBE_SOCKETSERVER)
AC_MSG_RESULT([auto])
elif test "$enable_socketserver" = "no"; then
AC_MSG_RESULT([no])
fi
AC_MSG_CHECKING(--enable-multibyte argument)
AC_ARG_ENABLE(multibyte,
[ --enable-multibyte Include multibyte editing support.], ,

View File

@ -3782,3 +3782,15 @@ EXTERN char e_duplicate_type_var_name_str[]
EXTERN char e_diff_anchors_with_hidden_windows[]
INIT(= N_("E1562: Diff anchors cannot be used with hidden diff windows"));
#endif
#ifdef FEAT_SOCKETSERVER
EXTERN char e_socket_path_too_big[]
INIT(= N_("E1563: Socket path is too big"));
EXTERN char e_socket_name_no_slashes[]
INIT(= N_("E1564: Socket name cannot have slashes in it without being a path"));
EXTERN char e_socket_server_not_online[]
INIT(= N_("E1565: Socket server is not online, call remote_startserver() first"));
EXTERN char e_socket_server_failed_connecting[]
INIT(= N_("E1566: Failed connecting to socket %s: %s"));
EXTERN char e_socket_server_unavailable[]
INIT(= N_("E1567: Cannot start socket server, socket path is unavailable"));
#endif

View File

@ -6765,6 +6765,13 @@ f_has(typval_T *argvars, typval_T *rettv)
1
#else
0
#endif
},
{"socketserver",
#ifdef FEAT_SOCKETSERVER
1
#else
0
#endif
},
{"balloon_eval",

View File

@ -10039,9 +10039,29 @@ eval_vars(
#ifdef FEAT_CLIENTSERVER
case SPEC_CLIENT: // Source of last submitted input
#ifdef MSWIN
sprintf((char *)strbuf, PRINTF_HEX_LONG_U,
(long_u)clientWindow);
result = strbuf;
#else
# ifdef FEAT_SOCKETSERVER
if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
{
if (client_socket == NULL)
result = (char_u *)"";
else
result = client_socket;
}
# endif
# ifdef FEAT_X11
if (clientserver_method == CLIENTSERVER_METHOD_X11)
{
sprintf((char *)strbuf, PRINTF_HEX_LONG_U,
(long_u)clientWindow);
result = strbuf;
}
# endif
#endif
break;
#endif

View File

@ -945,11 +945,20 @@
# define FIND_REPLACE_DIALOG 1
#endif
/*
* +socketserver Use UNIX domain sockets for clientserver communication
*/
#if defined(UNIX) && (defined(WANT_SOCKETSERVER) || \
(defined(MAYBE_SOCKETSERVER) && !defined(HAVE_X11)))
#define FEAT_SOCKETSERVER
#endif
/*
* +clientserver Remote control via the remote_send() function
* and the --remote argument
*/
#if (defined(MSWIN) || defined(FEAT_XCLIPBOARD)) && defined(FEAT_EVAL)
#if (defined(MSWIN) || defined(FEAT_XCLIPBOARD) || defined(FEAT_SOCKETSERVER)) \
&& defined(FEAT_EVAL)
# define FEAT_CLIENTSERVER
#endif

View File

@ -1868,7 +1868,7 @@ EXTERN Window commWindow INIT(= None);
EXTERN Window clientWindow INIT(= None);
EXTERN Atom commProperty INIT(= None);
EXTERN char_u *serverDelayedStartName INIT(= NULL);
# else
# elif defined(MSWIN)
# ifdef PROTO
typedef int HWND;
# endif
@ -2090,3 +2090,35 @@ EXTERN char *wayland_display_name INIT(= NULL);
EXTERN int wayland_display_fd;
#endif
#if defined(FEAT_CLIENTSERVER) && !defined(MSWIN)
// Backend for clientserver functionality
typedef enum {
CLIENTSERVER_METHOD_NONE,
CLIENTSERVER_METHOD_X11,
CLIENTSERVER_METHOD_SOCKET
} clientserver_method_T;
// Default to X11 if compiled with support for it, else use socket server.
# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
EXTERN clientserver_method_T clientserver_method
# else
// Since we aren't going to be changing clientserver_method, make it constant to
// allow compiler optimizations.
EXTERN const clientserver_method_T clientserver_method
# endif
# ifdef FEAT_X11
INIT(= CLIENTSERVER_METHOD_X11);
# elif defined(FEAT_SOCKETSERVER)
INIT(= CLIENTSERVER_METHOD_SOCKET);
# else
INIT(= CLIENTSERVER_METHOD_NONE);
# endif
#endif
#ifdef FEAT_SOCKETSERVER
// Path to socket of last client that communicated with us
EXTERN char_u *client_socket INIT(= NULL);
#endif

View File

@ -150,6 +150,12 @@ gui_start(char_u *arg UNUSED)
// Reset clipmethod to CLIPMETHOD_NONE
choose_clipmethod();
#ifdef FEAT_SOCKETSERVER
// Install socket server listening socket if we are running it
if (socket_server_valid())
gui_gtk_init_socket_server();
#endif
vim_free(old_term);
// If the GUI started successfully, trigger the GUIEnter event, otherwise

View File

@ -99,6 +99,13 @@ extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockI
# include <X11/Sunkeysym.h>
#endif
#ifdef FEAT_SOCKETSERVER
# include <glib-unix.h>
// Used to track the source for the listening socket
static uint socket_server_source_id = 0;
#endif
/*
* Easy-to-use macro for multihead support.
*/
@ -2688,6 +2695,53 @@ global_event_filter(GdkXEvent *xev,
}
#endif // !USE_GNOME_SESSION
#ifdef FEAT_SOCKETSERVER
/*
* Callback for new events from the socket server listening socket
*/
static int
socket_server_poll_in(int fd UNUSED, GIOCondition cond, void *user_data UNUSED)
{
if (cond & G_IO_IN)
socket_server_accept_client();
else if (cond & (G_IO_ERR | G_IO_HUP))
{
socket_server_uninit();
return FALSE;
}
return TRUE;
}
/*
* Initialize socket server for use in the GUI (does not actually initialize the
* socket server, only attaches a source).
*/
void
gui_gtk_init_socket_server(void)
{
if (socket_server_source_id > 0)
return;
// Register source for file descriptor to global default context
socket_server_source_id = g_unix_fd_add(socket_server_get_fd(),
G_IO_IN | G_IO_ERR | G_IO_HUP, socket_server_poll_in, NULL);
}
/*
* Remove the source for the socket server listening socket.
*/
void
gui_gtk_uninit_socket_server(void)
{
if (socket_server_source_id > 0)
{
g_source_remove(socket_server_source_id);
socket_server_source_id = 0;
}
}
#endif
/*
* Setup the window icon & xcmdsrv comm after the main window has been realized.
@ -2754,7 +2808,7 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
setup_save_yourself();
#ifdef FEAT_CLIENTSERVER
if (gui_mch_get_display())
if (clientserver_method == CLIENTSERVER_METHOD_X11 && gui_mch_get_display())
{
if (serverName == NULL && serverDelayedStartName != NULL)
{

View File

@ -14,7 +14,7 @@
#include "vim.h"
#include "version.h"
#if defined(FEAT_CLIENTSERVER) || defined(PROTO)
#if (defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)) || defined(PROTO)
# ifdef FEAT_X11
# include <X11/Intrinsic.h>

View File

@ -1855,7 +1855,8 @@ getout(int exitval)
* Get the name of the display, before gui_prepare() removes it from
* argv[]. Used for the xterm-clipboard display.
*
* Also find the --server... arguments and --socketid and --windowid
* Also find the --server, --clientserver... arguments and --socketid and
* --windowid
*/
static void
early_arg_scan(mparm_T *parmp UNUSED)
@ -1900,6 +1901,22 @@ early_arg_scan(mparm_T *parmp UNUSED)
gui.dofork = FALSE;
# endif
}
# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
else if (STRNICMP(argv[i], "--clientserver", 14) == 0)
{
char_u *arg;
if (i == argc - 1)
mainerr_arg_missing((char_u *)argv[i]);
arg = (char_u *)argv[++i];
if (STRICMP(arg, "socket") == 0)
clientserver_method = CLIENTSERVER_METHOD_SOCKET;
else if (STRICMP(arg, "x11") == 0)
clientserver_method = CLIENTSERVER_METHOD_X11;
else
mainerr(ME_UNKNOWN_OPTION, arg);
}
# endif
# endif
# if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN)
@ -2220,7 +2237,11 @@ command_line_scan(mparm_T *parmp)
else if (STRNICMP(argv[0] + argv_idx, "serverlist", 10) == 0)
; // already processed -- no arg
else if (STRNICMP(argv[0] + argv_idx, "servername", 10) == 0
|| STRNICMP(argv[0] + argv_idx, "serversend", 10) == 0)
|| STRNICMP(argv[0] + argv_idx, "serversend", 10) == 0
# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
|| STRNICMP(argv[0] + argv_idx, "clientserver", 12) == 0
# endif
)
{
// already processed -- snatch the following arg
if (argc > 1)
@ -3712,6 +3733,9 @@ usage(void)
main_msg(_("-Y\t\t\tDo not connect to Wayland compositor"));
#endif
#ifdef FEAT_CLIENTSERVER
# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
main_msg(_("--clientserver <socket|x11> Backend for clientserver communication"));
# endif
main_msg(_("--remote <files>\tEdit <files> in a Vim server if possible"));
main_msg(_("--remote-silent <files> Same, don't complain if there is no server"));
main_msg(_("--remote-wait <files> As --remote but wait for files to have been edited"));

File diff suppressed because it is too large Load Diff

28
src/po/vim.pot generated
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim\n"
"Report-Msgid-Bugs-To: vim-dev@vim.org\n"
"POT-Creation-Date: 2025-08-16 17:57+0200\n"
"POT-Creation-Date: 2025-08-18 21:30+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -254,6 +254,9 @@ msgstr ""
msgid "%d of %d edited"
msgstr ""
msgid "Socket server not online:Send expression failed"
msgstr ""
msgid "No display: Send expression failed.\n"
msgstr ""
@ -1711,6 +1714,9 @@ msgstr ""
msgid "-Y\t\t\tDo not connect to Wayland compositor"
msgstr ""
msgid "--clientserver <socket|x11> Backend for clientserver communication"
msgstr ""
msgid "--remote <files>\tEdit <files> in a Vim server if possible"
msgstr ""
@ -2507,6 +2513,10 @@ msgstr ""
msgid "XSMP SmcOpenConnection failed: %s"
msgstr ""
#, c-format
msgid "Failed creating socket directory: %s"
msgstr ""
msgid "At line"
msgstr ""
@ -8806,6 +8816,22 @@ msgstr ""
msgid "E1562: Diff anchors cannot be used with hidden diff windows"
msgstr ""
msgid "E1563: Socket path is too big"
msgstr ""
msgid "E1564: Socket name cannot have slashes in it without being a path"
msgstr ""
msgid "E1565: Socket server is not online, call remote_startserver() first"
msgstr ""
#, c-format
msgid "E1566: Failed connecting to socket %s: %s"
msgstr ""
msgid "E1567: Cannot start socket server, socket path is unavailable"
msgstr ""
#. type of cmdline window or 0
#. result of cmdline window or 0
#. buffer of cmdline window or NULL

View File

@ -8,6 +8,8 @@ void gui_mch_stop_blink(int may_call_gui_update_cursor);
void gui_mch_start_blink(void);
int gui_mch_early_init_check(int give_message);
int gui_mch_init_check(void);
void gui_gtk_init_socket_server(void);
void gui_gtk_uninit_socket_server(void);
void gui_mch_set_dark_theme(int dark);
void gui_mch_show_tabline(int showit);
int gui_mch_showing_tabline(void);

View File

@ -68,6 +68,7 @@ char *did_set_shellslash(optset_T *args);
char *did_set_shiftwidth_tabstop(optset_T *args);
char *did_set_showtabline(optset_T *args);
char *did_set_smoothscroll(optset_T *args);
char *did_set_socktimeoutlen(optset_T *args);
char *did_set_spell(optset_T *args);
char *did_set_swapfile(optset_T *args);
char *did_set_termguicolors(optset_T *args);

View File

@ -34,6 +34,8 @@ int expand_set_casemap(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_clipboard(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_clipmethod(optset_T *args);
int expand_set_clipmethod(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_clientserver(optset_T *args UNUSED);
int expand_set_clientserver(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_chars_option(optset_T *args);
int expand_set_chars_option(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_cinoptions(optset_T *args);

View File

@ -94,4 +94,15 @@ void stop_timeout(void);
volatile sig_atomic_t *start_timeout(long msec);
void delete_timer(void);
int mch_create_anon_file(void);
int socket_server_init(char_u *sock_path);
void socket_server_uninit(void);
char_u *socket_server_list_sockets(void);
void socket_server_accept_client(void);
int socket_server_valid(void);
int socket_server_send(char_u *sock_path, char_u *cmd, char_u **result, char_u **receiver, int is_expr, int timeout, int silent);
int socket_server_read_reply(char_u *sender, char_u **str, int timeout);
int socket_server_peek_reply(char_u *sender, char_u **str);
int socket_server_send_reply(char_u *client, char_u *str);
int socket_server_get_fd(void);
int socket_server_waiting_accept(void);
/* vim: set ft=c : */

View File

@ -10,6 +10,12 @@ CheckFeature clientserver
source util/shared.vim
" Unlike X11, we need the socket server running if we want to send commands to
" a server via sockets.
if v:servername == ""
call remote_startserver('VIMSOCKETSERVERTEST')
endif
func Check_X11_Connection()
if has('x11')
CheckX11
@ -184,10 +190,18 @@ func Test_client_server()
call assert_fails('call remote_startserver("")', 'E1175:')
call assert_fails('call remote_startserver([])', 'E1174:')
call assert_fails("let x = remote_peek([])", 'E730:')
call assert_fails("let x = remote_read('vim10')",
\ has('unix') ? ['E573:.*vim10'] : 'E277:')
call assert_fails("call server2client('abc', 'xyz')",
\ has('unix') ? ['E573:.*abc'] : 'E258:')
" When using socket server, server id is not a number, but the path to the
" socket.
if has('socketserver') && !has('X11')
call assert_fails("let x = remote_read('vim/10')", ['E573:.*vim/10'])
call assert_fails("call server2client('a/b/c', 'xyz')", ['E573:.*a/b/c'])
else
call assert_fails("let x = remote_read('vim10')",
\ has('unix') ? ['E573:.*vim10'] : 'E277:')
call assert_fails("call server2client('abc', 'xyz')",
\ has('unix') ? ['E573:.*abc'] : 'E258:')
endif
endfunc
func Test_client_server_stopinsert()
@ -231,6 +245,121 @@ func Test_client_server_stopinsert()
endtry
endfunc
" Test if socket server and X11 backends can be chosen and work properly.
func Test_client_server_x11_and_socket_server()
CheckNotMSWindows
CheckFeature socketserver
CheckFeature x11
let g:test_is_flaky = 1
let cmd = GetVimCommand()
if cmd == ''
throw 'GetVimCommand() failed'
endif
call Check_X11_Connection()
let types = ['socket', 'x11']
for type in types
let name = 'VIMTEST_' .. toupper(type)
let actual_cmd = cmd .. ' --clientserver ' .. type
let actual_cmd .= ' --servername ' .. name
let job = job_start(actual_cmd, {'stoponexit': 'kill', 'out_io': 'null'})
call WaitForAssert({-> assert_equal("run", job_status(job))})
call WaitForAssert({-> assert_match(name, system(cmd .. ' --clientserver ' .. type .. ' --serverlist'))})
call assert_match(name, system(actual_cmd .. ' --remote-expr "v:servername"'))
call system(actual_cmd .. " --remote-expr 'execute(\"qa!\")'")
try
call WaitForAssert({-> assert_equal("dead", job_status(job))})
finally
if job_status(job) != 'dead'
call assert_report('Server did not exit')
call job_stop(job, 'kill')
endif
endtry
endfor
endfunc
" Test if socket server works in the GUI
func Test_client_socket_server_server_gui()
CheckNotMSWindows
CheckFeature socketserver
CheckFeature gui_gtk
let g:test_is_flaky = 1
let cmd = GetVimCommand()
if cmd == ''
throw 'GetVimCommand() failed'
endif
call Check_X11_Connection()
let name = 'VIMTESTSOCKET'
let cmd .= ' --clientserver socket'
let cmd .= ' --servername ' .. name
let job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
call WaitForAssert({-> assert_equal("run", job_status(job))})
call WaitForAssert({-> assert_match(name, system(cmd .. ' --serverlist'))})
call system(cmd .. " --remote-expr 'execute(\"gui\")'")
call assert_match('1', system(cmd .. " --remote-expr 'has(\"gui_running\")'"))
call assert_match(name, system(cmd .. ' --remote-expr "v:servername"'))
call system(cmd .. " --remote-expr 'execute(\"qa!\")'")
try
call WaitForAssert({-> assert_equal("dead", job_status(job))})
finally
if job_status(job) != 'dead'
call assert_report('Server did not exit')
call job_stop(job, 'kill')
endif
endtry
endfunc
" Test if custom paths work for socketserver
func Test_client_socket_server_custom_path()
CheckNotMSWindows
CheckFeature socketserver
CheckNotFeature x11
let g:test_is_flaky = 1
let cmd = GetVimCommand()
if cmd == ''
throw 'GetVimCommand() failed'
endif
let name = 'VIMTESTSOCKET2'
let paths = ['./' .. name, '../testdir/' .. name, getcwd(-1) .. '/' .. name]
for path in paths
let actual = cmd .. ' --servername ' .. path
let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'})
call WaitForAssert({-> assert_equal("run", job_status(job))})
call WaitForAssert({-> assert_equal(path, glob(path))})
call system(actual .. " --remote-expr 'execute(\"qa!\")'")
try
call WaitForAssert({-> assert_equal("dead", job_status(job))})
finally
if job_status(job) != 'dead'
call assert_report('Server did not exit')
call job_stop(job, 'kill')
endif
endtry
endfor
endfunc
" Uncomment this line to get a debugging log
" call ch_logfile('channellog', 'w')

View File

@ -3,6 +3,11 @@
source util/screendump.vim
import './util/vim9.vim' as v9
" Socket backend for remote functions require the socket server to be running
if v:servername == ""
call remote_startserver('VIMSOCKETSERVERTEST')
endif
" Test for passing too many or too few arguments to builtin functions
func Test_internalfunc_arg_error()
let l =<< trim END

View File

@ -79,6 +79,10 @@ func Test_wayland_startup()
call s:PreTest()
call s:CheckXConnection()
if v:servername == ""
call remote_startserver('VIMSOCKETSERVER')
endif
let l:name = 'WLVIMTEST'
let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
@ -372,6 +376,10 @@ func Test_wayland_autoselect_works()
call writefile(l:lines, 'Wltester', 'D')
if v:servername == ""
call remote_startserver('VIMSOCKETSERVER')
endif
let l:name = 'WLVIMTEST'
let l:cmd = GetVimCommand() .. ' -S Wltester --servername ' .. l:name
let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
@ -415,6 +423,10 @@ func Test_no_wayland_connect_cmd_flag()
call s:PreTest()
call s:CheckXConnection()
if v:servername == ""
call remote_startserver('VIMSOCKETSERVER')
endif
let l:name = 'WLFLAGVIMTEST'
let l:cmd = GetVimCommand() .. ' -Y --servername ' .. l:name
let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
@ -453,6 +465,10 @@ func Test_wayland_become_inactive()
call s:PreTest()
call s:CheckXConnection()
if v:servername == ""
call remote_startserver('VIMSOCKETSERVER')
endif
let l:name = 'WLLOSEVIMTEST'
let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
let l:job = job_start(cmd, {
@ -544,6 +560,10 @@ func Test_wayland_bad_environment()
let l:old = $XDG_RUNTIME_DIR
unlet $XDG_RUNTIME_DIR
if v:servername == ""
call remote_startserver('VIMSOCKETSERVER')
endif
let l:name = 'WLVIMTEST'
let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
let l:job = job_start(cmd, {

View File

@ -407,7 +407,19 @@ inchar_loop(
if ((resize_func != NULL && resize_func(TRUE))
#if defined(FEAT_CLIENTSERVER) && defined(UNIX)
|| server_waiting()
|| (
# ifdef FEAT_X11
(clientserver_method == CLIENTSERVER_METHOD_X11 &&
server_waiting())
# endif
# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
||
# endif
# ifdef FEAT_SOCKETSERVER
(clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
socket_server_waiting_accept())
# endif
)
#endif
#ifdef MESSAGE_QUEUE
|| interrupted

View File

@ -516,6 +516,11 @@ static char *(features[]) =
"-signs",
#endif
"+smartindent",
#ifdef FEAT_SOCKETSERVER
"+socketserver",
#else
"-socketserver",
#endif
#ifdef FEAT_SODIUM
# ifdef DYNAMIC_SODIUM
"+sodium/dyn",
@ -719,6 +724,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1651,
/**/
1650,
/**/