Merge #8247 'server: introduce --listen'

This commit is contained in:
Justin M. Keyes 2018-04-11 03:29:18 +02:00 committed by GitHub
commit f96d99ad11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 168 additions and 209 deletions

View File

@ -25,7 +25,7 @@ To enter commands in
type a colon
.Pq Sq \&:
which is also used in this manual to denote commands.
For more information, consult the on-line help system with the
For more information, consult the online help system with the
.Ic :help
command.
.Bl -tag -width Fl
@ -329,6 +329,9 @@ Implies
.Fl -headless .
.It Fl -headless
Do not start a user interface.
.Fl -listen .
.It Fl -listen Ar address
Start RPC server on this pipe or TCP socket.
.It Fl h , -help
Print usage information and exit.
.It Fl v , -version
@ -337,12 +340,12 @@ Print version information and exit.
.Sh ENVIRONMENT
.Bl -tag -width Fl
.It Ev VIM
Used to locate various user files, such as the user's init.vim.
Used to locate various user files, such as init.vim.
.It Ev VIMRUNTIME
Used to locate run time files, such as on-line documentation and
Used to locate runtime files, such as online documentation and
syntax highlighting definitions.
.It Ev XDG_CONFIG_HOME
Path to use for the user-local configuration directory, see
Path to the user-local configuration directory, see
.Sx FILES .
Defaults to
.Pa ~/.config
@ -356,7 +359,7 @@ Defaults to
.Pa ~/.local/share
if not set.
.It Ev VIMINIT
A string of Ex commands to be executed at startup.
Ex commands to be executed at startup.
For example, the command to quit is
.Ic :q ,
so to have
@ -375,41 +378,32 @@ command.
.Sh FILES
.Bl -tag -width "~/.config/nvim/init.vim"
.It Pa ~/.config/nvim/init.vim
The user-local
User-local
.Nm
configuration file.
See
.Ev XDG_CONFIG_HOME
above.
.It Pa ~/.config/nvim
The user-local
User-local
.Nm
configuration directory.
See
.Ev XDG_CONFIG_HOME
above.
See also
.Ev XDG_CONFIG_HOME .
.It Pa $VIM/sysinit.vim
The system-global
System-global
.Nm
configuration file.
.It Pa /usr/local/share/nvim
The system-global
System-global
.Nm
runtime directory.
.El
.Sh AUTHORS
.Nm
was started by
.An Thiago de Arruda ,
with a lot of help from others.
.Pp
Nvim was started by
.An Thiago de Arruda .
Most of Vim was written by
.An -nosplit
.An Bram Moolenaar ,
with a lot of help from others.
.An Bram Moolenaar .
See
.Ic :help credits .
.Pp
Vim is based on Stevie, worked on by
.An Tim Thompson ,
.An Tony Andrews ,

View File

@ -22,6 +22,10 @@ Commands ~
*:wv*
*:wviminfo* Deprecated alias to |:wshada| command.
Environment Variables ~
*$NVIM_LISTEN_ADDRESS* Deprecated in favor of |--listen|. If both are given,
$NVIM_LISTEN_ADDRESS is ignored.
Events ~
*EncodingChanged* Never fired; 'encoding' is always "utf-8".
*FileEncoding* Never fired; equivalent to |EncodingChanged|.

View File

@ -1788,9 +1788,9 @@ v:scrollstart String describing the script or function that caused the
hit-enter prompt.
*v:servername* *servername-variable*
*$NVIM_LISTEN_ADDRESS*
v:servername Default Nvim server address. Equivalent to
|$NVIM_LISTEN_ADDRESS| on startup. |serverstop()|
v:servername Primary listen-address of the current Nvim instance, the first
item returned by |serverlist()|. Can be set by |--listen| or
|$NVIM_LISTEN_ADDRESS| at startup. |serverstart()| |serverstop()|
Read-only.
@ -6638,15 +6638,11 @@ server2client({clientid}, {string}) *server2client()*
:echo server2client(expand("<client>"), "HELLO")
<
serverlist() *serverlist()*
Returns a list of available server names in a list.
When there are no servers an empty string is returned.
Returns a list of server addresses, or empty if all servers
were stopped. |serverstart()| |serverstop()|
Example: >
:echo serverlist()
< {Nvim} *--serverlist*
The Vim command-line option `--serverlist` was removed from
Nvim, but it can be imitated: >
nvim --cmd "echo serverlist()" --cmd "q"
<
serverstart([{address}]) *serverstart()*
Opens a socket or named pipe at {address} and listens for
|RPC| messages. Clients can send |API| commands to the address
@ -6674,13 +6670,9 @@ serverstart([{address}]) *serverstart()*
< |$NVIM_LISTEN_ADDRESS| is set to {address} if not already set.
*--servername*
The Vim command-line option `--servername` can be imitated: >
nvim --cmd "let g:server_addr = serverstart('foo')"
<
serverstop({address}) *serverstop()*
Closes the pipe or socket at {address}. Does nothing if
{address} is empty or invalid.
Closes the pipe or socket at {address}.
Returns TRUE if {address} is valid, else FALSE.
If |$NVIM_LISTEN_ADDRESS| is stopped it is unset.
If |v:servername| is stopped it is set to the next available
address returned by |serverlist()|.

View File

@ -70,9 +70,8 @@ An rpc socket is automatically created with each instance. The socket
location is stored in |v:servername|. By default this is a named pipe
with an automatically generated address. See |XXX|.
To make Nvim listen on a TCP/IP socket instead, set the
|$NVIM_LISTEN_ADDRESS| environment variable before starting Nvim: >
NVIM_LISTEN_ADDRESS=127.0.0.1:6666 nvim
To make Nvim listen on a TCP/IP socket instead, specify |--listen|: >
nvim --listen 127.0.0.1:6666
<Also, more sockets and named pipes can be listened on using |serverstart()|.
Note that localhost TCP sockets are generally less secure than named pipes,

View File

@ -355,6 +355,10 @@ argument.
instead.
See also |silent-mode|, which does start a (limited) UI.
--listen {addr} *--listen*
Start |RPC| server on socket or TCP address {addr}. Sets the
primary listen address |v:servername| to {addr}. |serverstart()|
==============================================================================
2. Initialization *initialization* *startup*

View File

@ -1,50 +0,0 @@
#!/usr/bin/env expect
if {$argc < 2} {
puts "Need commands for running the tests and for starting nvim"
exit 1
}
set timeout 60
set run_tests [split [lindex $argv 0] " "]
set run_nvim [split [lindex $argv 1] " "]
# don't echo to stdout
log_user 0
# set NVIM_LISTEN_ADDRESS, so nvim will listen on a known socket
set env(NVIM_LISTEN_ADDRESS) "/tmp/nvim-[exec date +%s%N].sock"
# start nvim
spawn {*}$run_nvim
# save the job descriptor
set nvim_id $spawn_id
# Reset function that can be invoked by test runners to put nvim in a cleaner
# state
send {
:echo "read"."y"
}
# wait until nvim is ready
expect "ready"
# run tests
spawn {*}$run_tests
set tests_id $spawn_id
set status 1
# listen for test output in the background
expect_background {
* {
# show test output to the user
send_user -- $expect_out(buffer)
}
eof {
# collect the exit status code
set spawn_id $tests_id
catch wait result
set status [lindex $result 3]
set spawn_id $nvim_id
# quit nvim
send ":qa!\r"
}
}
# switch back nvim and wait until it exits
set spawn_id $nvim_id
expect eof
exit $status

View File

@ -14403,8 +14403,11 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
if (argvars[0].vval.v_string) {
server_stop((char *) argvars[0].vval.v_string);
bool rv = server_stop((char *)argvars[0].vval.v_string);
rettv->vval.v_number = (rv ? 1 : 0);
}
}

View File

@ -6975,12 +6975,10 @@ do_exedit (
ex_no_reprint = TRUE;
}
/*
* ":gui" and ":gvim" when there is no GUI.
*/
/// ":gui" and ":gvim" when there is no GUI.
static void ex_nogui(exarg_T *eap)
{
eap->errmsg = e_nogvim;
eap->errmsg = (char_u *)N_("E25: Nvim does not have a built-in GUI");
}

View File

@ -1070,7 +1070,6 @@ EXTERN char_u e_nesting[] INIT(= N_("E22: Scripts nested too deep"));
EXTERN char_u e_noalt[] INIT(= N_("E23: No alternate file"));
EXTERN char_u e_noabbr[] INIT(= N_("E24: No such abbreviation"));
EXTERN char_u e_nobang[] INIT(= N_("E477: No ! allowed"));
EXTERN char_u e_nogvim[] INIT(= N_("E25: Nvim does not have a built-in GUI"));
EXTERN char_u e_nogroup[] INIT(= N_("E28: No such highlight group name: %s"));
EXTERN char_u e_noinstext[] INIT(= N_("E29: No inserted text yet"));
EXTERN char_u e_nolastcmd[] INIT(= N_("E30: No previous command line"));

View File

@ -72,30 +72,30 @@
# include "nvim/os/pty_process_unix.h"
#endif
/* Maximum number of commands from + or -c arguments. */
// Maximum number of commands from + or -c arguments.
#define MAX_ARG_CMDS 10
/* values for "window_layout" */
#define WIN_HOR 1 /* "-o" horizontally split windows */
#define WIN_VER 2 /* "-O" vertically split windows */
#define WIN_TABS 3 /* "-p" windows on tab pages */
// values for "window_layout"
#define WIN_HOR 1 // "-o" horizontally split windows
#define WIN_VER 2 // "-O" vertically split windows
#define WIN_TABS 3 // "-p" windows on tab pages
/* Struct for various parameters passed between main() and other functions. */
// Struct for various parameters passed between main() and other functions.
typedef struct {
int argc;
char **argv;
char *use_vimrc; // vimrc from -u argument
int n_commands; /* no. of commands from + or -c */
int n_commands; // no. of commands from + or -c
char *commands[MAX_ARG_CMDS]; // commands from + or -c arg
char_u cmds_tofree[MAX_ARG_CMDS]; /* commands that need free() */
int n_pre_commands; /* no. of commands from --cmd */
char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free()
int n_pre_commands; // no. of commands from --cmd
char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument
int edit_type; /* type of editing to do */
char_u *tagname; /* tag from -t argument */
char_u *use_ef; /* 'errorfile' from -q argument */
int edit_type; // type of editing to do
char_u *tagname; // tag from -t argument
char_u *use_ef; // 'errorfile' from -q argument
int want_full_screen;
bool input_isatty; // stdin is a terminal
@ -103,13 +103,15 @@ typedef struct {
bool err_isatty; // stderr is a terminal
int no_swap_file; // "-n" argument used
int use_debug_break_level;
int window_count; /* number of windows to use */
int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */
int window_count; // number of windows to use
int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS
#if !defined(UNIX)
int literal; /* don't expand file names */
int literal; // don't expand file names
#endif
int diff_mode; /* start with 'diff' set */
int diff_mode; // start with 'diff' set
char *listen_addr; // --listen {address}
} mparm_T;
/* Values for edit_type. */
@ -150,7 +152,6 @@ void event_init(void)
signal_init();
// finish mspgack-rpc initialization
channel_init();
server_init();
terminal_init();
}
@ -241,9 +242,8 @@ int main(int argc, char **argv)
char_u *cwd = NULL; // current workding dir on startup
time_init();
/* Many variables are in "params" so that we can pass them to invoked
* functions without a lot of arguments. "argc" and "argv" are also
* copied, so that they can be changed. */
// Many variables are in `params` so that we can pass them around easily.
// `argc` and `argv` are also copied, so that they can be changed.
init_params(&params, argc, argv);
init_startuptime(&params);
@ -254,11 +254,10 @@ int main(int argc, char **argv)
check_and_set_isatty(&params);
event_init();
/*
* Process the command line arguments. File names are put in the global
* argument list "global_alist".
*/
// Process the command line arguments. File names are put in the global
// argument list "global_alist".
command_line_scan(&params);
server_init(params.listen_addr);
if (GARGCOUNT > 0) {
fname = get_fname(&params, cwd);
@ -819,6 +818,9 @@ static void command_line_scan(mparm_T *parmp)
if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
abort();
}
} else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) {
want_argument = true;
argv_idx += 6;
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
#if !defined(UNIX)
parmp->literal = TRUE;
@ -864,10 +866,6 @@ static void command_line_scan(mparm_T *parmp)
case 'f': /* "-f" GUI: run in foreground. */
break;
case 'g': /* "-g" start GUI */
main_start_gui();
break;
case 'F': { // "-F" start in Farsi mode: rl + fkmap set.
p_fkmap = true;
set_option_value("rl", 1L, NULL, 0);
@ -906,18 +904,8 @@ static void command_line_scan(mparm_T *parmp)
parmp->no_swap_file = TRUE;
break;
case 'p': /* "-p[N]" open N tab pages */
#ifdef TARGET_API_MAC_OSX
/* For some reason on MacOS X, an argument like:
-psn_0_10223617 is passed in when invoke from Finder
or with the 'open' command */
if (argv[0][argv_idx] == 's') {
argv_idx = -1; /* bypass full -psn */
main_start_gui();
break;
}
#endif
/* default is 0: open window for each file */
case 'p': // "-p[N]" open N tab pages
// default is 0: open window for each file
parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
parmp->window_layout = WIN_TABS;
break;
@ -1030,15 +1018,12 @@ static void command_line_scan(mparm_T *parmp)
mainerr(err_opt_unknown, argv[0]);
}
/*
* Handle option arguments with argument.
*/
// Handle option arguments with argument.
if (want_argument) {
/*
* Check for garbage immediately after the option letter.
*/
if (argv[0][argv_idx] != NUL)
// Check for garbage immediately after the option letter.
if (argv[0][argv_idx] != NUL) {
mainerr(err_opt_garbage, argv[0]);
}
--argc;
if (argc < 1 && c != 'S') /* -S has an optional argument */
@ -1077,13 +1062,17 @@ static void command_line_scan(mparm_T *parmp)
break;
case '-':
if (argv[-1][2] == 'c') {
/* "--cmd {command}" execute command */
if (parmp->n_pre_commands >= MAX_ARG_CMDS)
if (strequal(argv[-1], "--cmd")) {
// "--cmd {command}" execute command
if (parmp->n_pre_commands >= MAX_ARG_CMDS) {
mainerr(err_extra_cmd, NULL);
}
parmp->pre_commands[parmp->n_pre_commands++] = argv[0];
} else if (strequal(argv[-1], "--listen")) {
// "--listen {address}"
parmp->listen_addr = argv[0];
}
/* "--startuptime <file>" already handled */
// "--startuptime <file>" already handled
break;
case 'q': /* "-q {errorfile}" QuickFix mode */
@ -1224,11 +1213,10 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
paramp->want_full_screen = true;
paramp->use_debug_break_level = -1;
paramp->window_count = -1;
paramp->listen_addr = NULL;
}
/*
* Initialize global startuptime file if "--startuptime" passed as an argument.
*/
/// Initialize global startuptime file if "--startuptime" passed as an argument.
static void init_startuptime(mparm_T *paramp)
{
for (int i = 1; i < paramp->argc; i++) {
@ -1834,17 +1822,6 @@ static void source_startup_scripts(const mparm_T *const parmp)
TIME_MSG("sourcing vimrc file(s)");
}
/*
* Setup to start using the GUI. Exit with an error when not available.
*/
static void main_start_gui(void)
{
mch_errmsg(_(e_nogvim));
mch_errmsg("\n");
mch_exit(2);
}
/// Get an environment variable, and execute it as Ex commands.
///
/// @param env environment variable to execute
@ -1968,6 +1945,7 @@ static void usage(void)
mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" --listen <address> Start RPC server at this address\n"));
#if !defined(UNIX)
mch_msg(_(" --literal Don't expand wildcards\n"));
#endif

View File

@ -32,26 +32,27 @@ static garray_T watchers = GA_EMPTY_INIT_VALUE;
#endif
/// Initializes the module
bool server_init(void)
bool server_init(const char *listen_addr)
{
ga_init(&watchers, sizeof(SocketWatcher *), 1);
bool must_free = false;
const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR);
if (listen_address == NULL) {
must_free = true;
listen_address = server_address_new();
// $NVIM_LISTEN_ADDRESS
const char *env_addr = os_getenv(LISTEN_ADDRESS_ENV_VAR);
int rv = listen_addr == NULL ? 1 : server_start(listen_addr);
if (0 != rv) {
rv = env_addr == NULL ? 1 : server_start(env_addr);
if (0 != rv) {
listen_addr = server_address_new();
if (listen_addr == NULL) {
return false;
}
rv = server_start(listen_addr);
xfree((char *)listen_addr);
}
}
if (!listen_address) {
return false;
}
bool ok = (server_start(listen_address) == 0);
if (must_free) {
xfree((char *) listen_address);
}
return ok;
return rv == 0;
}
/// Teardown a single server
@ -120,8 +121,8 @@ bool server_owns_pipe_address(const char *path)
/// @param endpoint Address of the server. Either a 'ip:[port]' string or an
/// arbitrary identifier (trimmed to 256 bytes) for the Unix
/// socket or named pipe.
/// @returns 0 on success, 1 on a regular error, and negative errno
/// on failure to bind or listen.
/// @returns 0: success, 1: validation error, 2: already listening,
/// -errno: failed to bind or listen.
int server_start(const char *endpoint)
{
if (endpoint == NULL || endpoint[0] == '\0') {
@ -145,7 +146,7 @@ int server_start(const char *endpoint)
uv_freeaddrinfo(watcher->uv.tcp.addrinfo);
}
socket_watcher_close(watcher, free_server);
return 1;
return 2;
}
}
@ -177,7 +178,7 @@ int server_start(const char *endpoint)
/// Stops listening on the address specified by `endpoint`.
///
/// @param endpoint Address of the server.
void server_stop(char *endpoint)
bool server_stop(char *endpoint)
{
SocketWatcher *watcher;
bool watcher_found = false;
@ -196,8 +197,8 @@ void server_stop(char *endpoint)
}
if (!watcher_found) {
ELOG("Not listening on %s", addr);
return;
WLOG("Not listening on %s", addr);
return false;
}
// Unset $NVIM_LISTEN_ADDRESS if it is the stopped address.
@ -219,6 +220,8 @@ void server_stop(char *endpoint)
if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) {
set_vservername(&watchers);
}
return true;
}
/// Returns an allocated array of server addresses.

View File

@ -1,31 +1,40 @@
local helpers = require('test.functional.helpers')(after_each)
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
local command = helpers.command
local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
local os_name = helpers.os_name
local iswin = helpers.iswin
local ok = helpers.ok
local matches = helpers.matches
local function clear_serverlist()
for _, server in pairs(funcs.serverlist()) do
funcs.serverstop(server)
end
for _, server in pairs(funcs.serverlist()) do
funcs.serverstop(server)
end
end
describe('serverstart(), serverstop()', function()
describe('server', function()
before_each(clear)
it('sets $NVIM_LISTEN_ADDRESS on first invocation', function()
it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function()
-- Unset $NVIM_LISTEN_ADDRESS
command('let $NVIM_LISTEN_ADDRESS = ""')
local s = eval('serverstart()')
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
eq(s, eval('$NVIM_LISTEN_ADDRESS'))
command("call serverstop('"..s.."')")
eq(1, eval("serverstop('"..s.."')"))
eq('', eval('$NVIM_LISTEN_ADDRESS'))
end)
it('sets v:servername _only_ on nvim startup unless all servers are stopped',
it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function()
clear({env={NVIM_LISTEN_ADDRESS='.'}})
eq('.', eval('$NVIM_LISTEN_ADDRESS'))
local servers = funcs.serverlist()
eq(1, #servers)
ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\…
end)
it('sets v:servername at startup or if all servers were stopped',
function()
local initial_server = meths.get_vvar('servername')
assert(initial_server ~= nil and initial_server:len() > 0,
@ -38,24 +47,23 @@ describe('serverstart(), serverstop()', function()
neq(initial_server, s)
-- serverstop() does _not_ modify v:servername...
funcs.serverstop(s)
eq(1, funcs.serverstop(s))
eq(initial_server, meths.get_vvar('servername'))
-- ...unless we stop _all_ servers.
funcs.serverstop(funcs.serverlist()[1])
eq(1, funcs.serverstop(funcs.serverlist()[1]))
eq('', meths.get_vvar('servername'))
-- v:servername will take the next available server.
local servername = (os_name() == 'windows'
and [[\\.\pipe\Xtest-functional-server-pipe]]
or 'Xtest-functional-server-socket')
local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]]
or 'Xtest-functional-server-socket')
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
end)
it('serverstop() ignores invalid input', function()
command("call serverstop('')")
command("call serverstop('bogus-socket-name')")
it('serverstop() returns false for invalid input', function()
eq(0, eval("serverstop('')"))
eq(0, eval("serverstop('bogus-socket-name')"))
end)
it('parses endpoints correctly', function()
@ -96,17 +104,13 @@ describe('serverstart(), serverstop()', function()
funcs.serverstart('127.0.0.1:65536') -- invalid port
eq({}, funcs.serverlist())
end)
end)
describe('serverlist()', function()
before_each(clear)
it('returns the list of servers', function()
it('serverlist() returns the list of servers', function()
-- There should already be at least one server.
local n = eval('len(serverlist())')
-- Add a few
local servs = (os_name() == 'windows'
-- Add some servers.
local servs = (iswin()
and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] })
for _, s in ipairs(servs) do
@ -120,9 +124,31 @@ describe('serverlist()', function()
-- The new servers should be at the end of the list.
for i = 1, #servs do
eq(servs[i], new_servs[i + n])
command("call serverstop('"..servs[i].."')")
eq(1, eval("serverstop('"..servs[i].."')"))
end
-- After serverstop() the servers should NOT be in the list.
eq(n, eval('len(serverlist())'))
end)
end)
describe('startup --listen', function()
it('validates', function()
clear()
local cmd = { unpack(helpers.nvim_argv) }
table.insert(cmd, '--listen')
matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd))
cmd = { unpack(helpers.nvim_argv) }
table.insert(cmd, '--listen2')
matches('nvim.*: Garbage after option argument: "%-%-listen2"', funcs.system(cmd))
end)
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]]
or 'Xtest-listen-pipe')
clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' },
args={ '--listen', addr } })
eq(addr, meths.get_vvar('servername'))
end)
end)

View File

@ -16,6 +16,7 @@ local neq = global_helpers.neq
local eq = global_helpers.eq
local ok = global_helpers.ok
local map = global_helpers.map
local matches = global_helpers.matches
local filter = global_helpers.filter
local dedent = global_helpers.dedent
local table_flatten = global_helpers.table_flatten
@ -747,6 +748,7 @@ local module = {
insert = insert,
iswin = iswin,
map = map,
matches = matches,
merge_args = merge_args,
meth_pcall = meth_pcall,
meths = meths,

View File

@ -16,6 +16,12 @@ end
local function ok(res)
return assert.is_true(res)
end
local function matches(pat, actual)
if nil ~= string.match(actual, pat) then
return true
end
error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual))
end
-- initial_path: directory to recurse into
-- re: include pattern (string)
@ -572,6 +578,7 @@ return {
hasenv = hasenv,
intchar2lua = intchar2lua,
map = map,
matches = matches,
mergedicts_copy = mergedicts_copy,
neq = neq,
ok = ok,