Skip to content

Commit

Permalink
console: configure continuation
Browse files Browse the repository at this point in the history
Continuation marker can be set up with `\set continuation` command.
Works on both server and client side in any language.

Closes tarantool#4317
Requires tarantool#7357

@TarantoolBot document
Title: introduce line carrying slash

Now we can use multiline commands with lines ending by configuring
continuation symbol. Works only when there is no set delimiter.
Consider the example where the marker is set, used and removed:
```
tarantool> \set continuation on
---
- true
...

tarantool> a = 10\
         > + 12
---
...

tarantool> \set continuation off
---
- true
...

tarantool> a = 10\
---
- error: '[string "a = 10\"]:1: unexpected symbol near ''\'''
...

tarantool>

```
  • Loading branch information
Lord-KA committed Mar 7, 2023
1 parent 56ec2cc commit 4fd9975
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## feature/console

* Now multiline commands can be used by setting up continuation marker
with `\set continuation` command.
82 changes: 71 additions & 11 deletions src/box/lua/console.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ local output_eos = { ["yaml"] = '\n...\n', ["lua"] = ';' }

local default_local_eos = ''

-- Each but the last string of multiline command should end with this string.
local continuation_symbol = '\\'

output_handlers["yaml"] = function(status, _opts, ...)
local err, ok, res
-- Using pcall, because serializer can raise an exception
Expand Down Expand Up @@ -294,6 +297,14 @@ local function set_output(storage, value)
return true
end

local function set_continuation(storage, value)
if value ~= 'on' and value ~= 'off' then
return error('usage: \\set continuation on|off')
end
storage.continuation_on = value == 'on'
return true
end

local param_handlers = {
language = set_language,
lang = set_language,
Expand All @@ -302,7 +313,8 @@ local param_handlers = {
o = set_output,
delimiter = set_delimiter,
delim = set_delimiter,
d = set_delimiter
d = set_delimiter,
continuation = set_continuation,
}

local function set_param(storage, _func, param, value)
Expand Down Expand Up @@ -497,6 +509,14 @@ local text_connection_mt = {
end
end
end
if operators[items[1]] == set_param and
param_handlers[items[2]] == set_continuation then

local continuation = items[3]
if (continuation == 'on' or continuation == 'off') then
self.continuation_on = continuation == 'on'
end
end
return text
end,
--
Expand Down Expand Up @@ -573,7 +593,8 @@ local function wrap_text_socket(connection, url, print_f)
print_f = print_f,
eos = current_eos(),
fmt = current_output()["fmt"],
local_eos = default_local_eos
local_eos = default_local_eos,
continuation_on = false,
}, text_connection_mt)
--
-- Prepare the connection: setup EOS symbol
Expand Down Expand Up @@ -649,19 +670,29 @@ local function local_read(self)
break
end
if delim == "" then
local lang = self.language
if not lang or lang == 'lua' then
-- stop once a complete Lua statement is entered
if local_check_lua(buf) then
break
end
local continuation_on
if self.remote ~= nil then
continuation_on = self.remote.continuation_on
else
break
continuation_on = self.continuation_on
end
local lang = self.language or 'lua'
-- Interpret continuation_symbol as a line continuation marker.
if continuation_on and line:endswith(continuation_symbol) then
buf = buf:sub(1, -1 - #continuation_symbol)
goto continue
end
-- Continue reading if Lua input seems incomplete according to
-- internal heuristic.
if lang == 'lua' and not local_check_lua(buf) then
goto continue
end
break
elseif #buf >= #delim and buf:sub(#buf - #delim + 1) == delim then
buf = buf:sub(0, #buf - #delim)
break
end
::continue::
buf = buf.."\n"
prompt = string.rep(' ', #self.prompt)
end
Expand Down Expand Up @@ -689,9 +720,12 @@ local function local_print(self, output)
end

--
-- Read command from connected client console.listen()
-- Read a line or a chunk from connected client console.listen().
--
local function client_read(self)
-- The value is returned without delimiter (if any) and trailing
-- newline character.
--
local function client_read_line(self)
--
-- Byte sequences that come over the network and come from
-- the local client console may have a different terminal
Expand All @@ -712,6 +746,31 @@ local function client_read(self)
return buf:sub(1, -#self.delimiter-2)
end

--
-- Read a command from connected client console.listen().
--
local function client_read(self)
local buf = ''
local continuation_on = self.continuation_on
while true do
local line = client_read_line(self)
if line == nil then
-- EOF or error.
--
-- Note: Even if `buf` is not empty, skip unfinished
-- command.
return nil
end
buf = buf .. line
if continuation_on and line:endswith(continuation_symbol) then
buf = buf:sub(1, -1 - #continuation_symbol)
else
break
end
end
return buf
end

--
-- Print result to connected client from console.listen()
--
Expand All @@ -733,6 +792,7 @@ local repl_mt = {
completion = internal.completion_handler;
ac = true;
local_eos = default_local_eos;
continuation_on = false;
};
}

Expand Down
63 changes: 63 additions & 0 deletions test/app-luatest/gh_4317_console_backslash_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
local t = require('luatest')
local g = t.group()

local result_str = [[tarantool> \set continuation on
---
- true
...
tarantool> local a = 0\
> for i = 1, 10 do
> a = a + i
> end\
> print(a)
55
---
...
tarantool> ]]

local TARANTOOL_PATH = arg[-1]

g.test_using_backslash_on_local_console = function()
local tarantool_command = [[\\set continuation on\nlocal a = 0\\\n]] ..
[[for i = 1, 10 do\na = a + i\nend\\\n]] ..
[[print(a)\n]]

local cmd = "printf '%s' | %s -i 2>/dev/null"
cmd = (cmd):format(tarantool_command, TARANTOOL_PATH)
local fh = io.popen(cmd, 'r')

-- XXX: Readline on CentOS 7 produces \e[?1034h escape sequence before
-- tarantool> prompt, remove it.
local result = fh:read('*a'):gsub('\x1b%[%?1034h', '')

fh:close()
t.assert_equals(result, result_str)
end

g.test_using_backslash_on_remote_console = function()
local console = require('console')
local socket = require('socket')
local log = require('log')

-- Suppress console log messages.
log.level(4)
local CONSOLE_SOCKET = '/tmp/tarantool-test-gh-4317.sock'
local EOL = '\n...\n'
console.listen(CONSOLE_SOCKET)
socket.tcp_connect('unix/', CONSOLE_SOCKET)
local tarantool_command = '\\set continuation on\nlocal a = 0 \\\n' ..
'for i = 1, 10 do\\\n a = a + i \\\n end \\\n' ..
'return a'

local client = socket.tcp_connect('unix/', CONSOLE_SOCKET)
client:write(('%s\n'):format(tarantool_command))
-- Read 'true' after '\set continuation on' and then read result.
client:read(EOL)
local result = client:read(EOL)
t.assert_str_contains(result, '- 55')
t.assert_not_str_contains(result, 'error')
client:close()
os.remove(CONSOLE_SOCKET)
end

0 comments on commit 4fd9975

Please sign in to comment.