mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 12:45:17 -07:00
7a367c6967
In order to run unittests with a release build, we need the test functions to be accessible when NDEBUG is defined. Moving the functions into the test fixture ensures they are available and only available for use by the unit tests.
3596 lines
98 KiB
Lua
3596 lines
98 KiB
Lua
local t = require('test.unit.testutil')
|
||
local itp = t.gen_itp(it)
|
||
local bit = require('bit')
|
||
|
||
--- @class vterm
|
||
--- @field ENC_UTF8 integer
|
||
--- @field VTERM_ATTR_BLINK integer
|
||
--- @field VTERM_ATTR_BOLD integer
|
||
--- @field VTERM_ATTR_FONT integer
|
||
--- @field VTERM_ATTR_ITALIC integer
|
||
--- @field VTERM_ATTR_REVERSE integer
|
||
--- @field VTERM_ATTR_UNDERLINE integer
|
||
--- @field VTERM_BASELINE_RAISE integer
|
||
--- @field VTERM_KEY_ENTER integer
|
||
--- @field VTERM_KEY_FUNCTION_0 integer
|
||
--- @field VTERM_KEY_KP_0 integer
|
||
--- @field VTERM_KEY_NONE integer
|
||
--- @field VTERM_KEY_TAB integer
|
||
--- @field VTERM_KEY_UP integer
|
||
--- @field VTERM_MAX_CHARS_PER_CELL integer
|
||
--- @field VTERM_MOD_ALT integer
|
||
--- @field VTERM_MOD_CTRL integer
|
||
--- @field VTERM_MOD_SHIFT integer
|
||
--- @field parser_apc function
|
||
--- @field parser_csi function
|
||
--- @field parser_dcs function
|
||
--- @field parser_osc function
|
||
--- @field parser_pm function
|
||
--- @field parser_sos function
|
||
--- @field parser_text function
|
||
--- @field print_color function
|
||
--- @field screen_sb_clear function
|
||
--- @field screen_sb_popline function
|
||
--- @field screen_sb_pushline function
|
||
--- @field selection_query function
|
||
--- @field selection_set function
|
||
--- @field state_erase function
|
||
--- @field state_movecursor function
|
||
--- @field state_moverect function
|
||
--- @field state_pos function
|
||
--- @field state_putglyph function
|
||
--- @field state_sb_clear function
|
||
--- @field state_scrollrect function
|
||
--- @field state_setpenattr function
|
||
--- @field state_settermprop function
|
||
--- @field term_output function
|
||
--- @field vterm_input_write function
|
||
--- @field vterm_keyboard_end_paste function
|
||
--- @field vterm_keyboard_key function
|
||
--- @field vterm_keyboard_start_paste function
|
||
--- @field vterm_keyboard_unichar function
|
||
--- @field vterm_lookup_encoding fun(any, any):any
|
||
--- @field vterm_mouse_button function
|
||
--- @field vterm_mouse_move function
|
||
--- @field vterm_new fun(any, any):any
|
||
--- @field vterm_obtain_screen fun(any):any
|
||
--- @field vterm_obtain_state fun(any): any
|
||
--- @field vterm_output_set_callback function
|
||
--- @field vterm_parser_set_callbacks fun(any, any, any):any
|
||
--- @field vterm_screen_convert_color_to_rgb function
|
||
--- @field vterm_screen_enable_altscreen function
|
||
--- @field vterm_screen_enable_reflow function
|
||
--- @field vterm_screen_get_attrs_extent function
|
||
--- @field vterm_screen_get_cell function
|
||
--- @field vterm_screen_get_chars fun(any, any, any, any):any
|
||
--- @field vterm_screen_get_text fun(any, any, any, any):any
|
||
--- @field vterm_screen_is_eol fun(any, any):any
|
||
--- @field vterm_screen_reset function
|
||
--- @field vterm_screen_set_callbacks function
|
||
--- @field vterm_set_size function
|
||
--- @field vterm_set_utf8 fun(any, any, any):any
|
||
--- @field vterm_state_focus_in function
|
||
--- @field vterm_state_focus_out function
|
||
--- @field vterm_state_get_cursorpos fun(any, any)
|
||
--- @field vterm_state_get_lineinfo fun(any, any):any
|
||
--- @field vterm_state_get_penattr function
|
||
--- @field vterm_state_reset function
|
||
--- @field vterm_state_set_bold_highbright function
|
||
--- @field vterm_state_set_callbacks function
|
||
--- @field vterm_state_set_selection_callbacks function
|
||
--- @field vterm_state_set_unrecognised_fallbacks function
|
||
local vterm = t.cimport(
|
||
'./src/vterm/vterm.h',
|
||
'./src/vterm/vterm_internal.h',
|
||
'./test/unit/fixtures/vterm_test.h'
|
||
)
|
||
|
||
--- @return string
|
||
local function read_rm()
|
||
local f = assert(io.open(t.paths.vterm_test_file, 'rb'))
|
||
local text = f:read('*a')
|
||
f:close()
|
||
vim.fs.rm(t.paths.vterm_test_file, { force = true })
|
||
return text
|
||
end
|
||
|
||
local function append(str)
|
||
local f = assert(io.open(t.paths.vterm_test_file, 'a'))
|
||
f:write(str)
|
||
f:close()
|
||
return 1
|
||
end
|
||
|
||
local function parser_control(control)
|
||
return append(string.format('control %02x\n', control))
|
||
end
|
||
|
||
local function parser_escape(bytes)
|
||
return append(string.format('escape %s\n', t.ffi.string(bytes)))
|
||
end
|
||
|
||
local function wantparser(vt)
|
||
assert(vt)
|
||
|
||
local parser_cbs = t.ffi.new('VTermParserCallbacks')
|
||
parser_cbs['text'] = vterm.parser_text
|
||
parser_cbs['control'] = parser_control
|
||
parser_cbs['escape'] = parser_escape
|
||
parser_cbs['csi'] = vterm.parser_csi
|
||
parser_cbs['osc'] = vterm.parser_osc
|
||
parser_cbs['dcs'] = vterm.parser_dcs
|
||
parser_cbs['apc'] = vterm.parser_apc
|
||
parser_cbs['pm'] = vterm.parser_pm
|
||
parser_cbs['sos'] = vterm.parser_sos
|
||
|
||
vterm.vterm_parser_set_callbacks(vt, parser_cbs, nil)
|
||
end
|
||
|
||
--- @return any
|
||
local function init()
|
||
local vt = vterm.vterm_new(25, 80)
|
||
vterm.vterm_output_set_callback(vt, vterm.term_output, nil)
|
||
vterm.vterm_set_utf8(vt, true)
|
||
return vt
|
||
end
|
||
|
||
local function state_setlineinfo()
|
||
return 1
|
||
end
|
||
|
||
--- @return any
|
||
local function wantstate(vt, opts)
|
||
opts = opts or {}
|
||
assert(vt)
|
||
local state = vterm.vterm_obtain_state(vt)
|
||
|
||
local state_cbs = t.ffi.new('VTermStateCallbacks')
|
||
state_cbs['putglyph'] = vterm.state_putglyph
|
||
state_cbs['movecursor'] = vterm.state_movecursor
|
||
state_cbs['scrollrect'] = vterm.state_scrollrect
|
||
state_cbs['moverect'] = vterm.state_moverect
|
||
state_cbs['erase'] = vterm.state_erase
|
||
state_cbs['setpenattr'] = vterm.state_setpenattr
|
||
state_cbs['settermprop'] = vterm.state_settermprop
|
||
state_cbs['setlineinfo'] = state_setlineinfo
|
||
state_cbs['sb_clear'] = vterm.state_sb_clear
|
||
|
||
local selection_cbs = t.ffi.new('VTermSelectionCallbacks')
|
||
selection_cbs['set'] = vterm.selection_set
|
||
selection_cbs['query'] = vterm.selection_query
|
||
|
||
vterm.vterm_state_set_callbacks(state, state_cbs, nil)
|
||
|
||
-- In some tests we want to check the behaviour of overflowing the buffer, so make it nicely small
|
||
vterm.vterm_state_set_selection_callbacks(state, selection_cbs, nil, nil, 16)
|
||
vterm.vterm_state_set_bold_highbright(state, 1)
|
||
vterm.vterm_state_reset(state, 1)
|
||
|
||
local fallbacks = t.ffi.new('VTermStateFallbacks')
|
||
fallbacks['control'] = parser_control
|
||
fallbacks['csi'] = vterm.parser_csi
|
||
fallbacks['osc'] = vterm.parser_osc
|
||
fallbacks['dcs'] = vterm.parser_dcs
|
||
fallbacks['apc'] = vterm.parser_apc
|
||
fallbacks['pm'] = vterm.parser_pm
|
||
fallbacks['sos'] = vterm.parser_sos
|
||
|
||
vterm.want_state_scrollback = opts.b or false
|
||
vterm.want_state_erase = opts.e or false
|
||
vterm.vterm_state_set_unrecognised_fallbacks(state, opts.f and fallbacks or nil, nil)
|
||
vterm.want_state_putglyph = opts.g or false
|
||
vterm.want_state_moverect = opts.m or false
|
||
vterm.want_state_settermprop = opts.p or false
|
||
vterm.want_state_scrollrect = opts.s or false
|
||
|
||
return state
|
||
end
|
||
|
||
--- @return any
|
||
local function wantscreen(vt, opts)
|
||
opts = opts or {}
|
||
local screen = vterm.vterm_obtain_screen(vt)
|
||
local screen_cbs = t.ffi.new('VTermScreenCallbacks')
|
||
|
||
-- TODO(dundargoc): fix
|
||
-- screen_cbs['damage'] = vterm.screen_damage
|
||
screen_cbs['moverect'] = vterm.state_moverect
|
||
screen_cbs['movecursor'] = vterm.state_movecursor
|
||
screen_cbs['settermprop'] = vterm.state_settermprop
|
||
screen_cbs['sb_pushline'] = vterm.screen_sb_pushline
|
||
screen_cbs['sb_popline'] = vterm.screen_sb_popline
|
||
screen_cbs['sb_clear'] = vterm.screen_sb_clear
|
||
|
||
vterm.vterm_screen_set_callbacks(screen, screen_cbs, nil)
|
||
|
||
if opts.a then
|
||
vterm.vterm_screen_enable_altscreen(screen, 1)
|
||
end
|
||
vterm.want_screen_scrollback = opts.b or false
|
||
vterm.want_state_movecursor = opts.c or false
|
||
-- TODO(dundargoc): fix
|
||
-- vterm.want_screen_damage = opts.d or opts.D or false
|
||
-- vterm.want_screen_cells = opts.D or false
|
||
vterm.want_state_moverect = opts.m or false
|
||
vterm.want_state_settermprop = opts.p or false
|
||
if opts.r then
|
||
vterm.vterm_screen_enable_reflow(screen, true)
|
||
end
|
||
|
||
return screen
|
||
end
|
||
|
||
local function reset(state, screen)
|
||
if state then
|
||
vterm.vterm_state_reset(state, 1)
|
||
vterm.vterm_state_get_cursorpos(state, vterm.state_pos)
|
||
end
|
||
if screen then
|
||
vterm.vterm_screen_reset(screen, 1)
|
||
end
|
||
end
|
||
|
||
local function push(input, vt)
|
||
vterm.vterm_input_write(vt, input, string.len(input))
|
||
end
|
||
|
||
local function expect(expected)
|
||
local actual = read_rm()
|
||
t.eq(expected .. '\n', actual)
|
||
end
|
||
|
||
local function expect_output(expected_preformat)
|
||
local actual = read_rm()
|
||
local expected = 'output '
|
||
|
||
for c in string.gmatch(expected_preformat, '.') do
|
||
if expected ~= 'output ' then
|
||
expected = expected .. ','
|
||
end
|
||
expected = string.format('%s%x', expected, string.byte(c))
|
||
end
|
||
|
||
t.eq(expected .. '\n', actual)
|
||
end
|
||
|
||
local function cursor(row, col, state)
|
||
local pos = t.ffi.new('VTermPos') --- @type {row: integer, col: integer}
|
||
vterm.vterm_state_get_cursorpos(state, pos)
|
||
t.eq(row, pos.row)
|
||
t.eq(col, pos.col)
|
||
end
|
||
|
||
local function lineinfo(row, expected, state)
|
||
local info = vterm.vterm_state_get_lineinfo(state, row)
|
||
local dwl = info.doublewidth == 1
|
||
local dhl = info.doubleheight == 1
|
||
local cont = info.continuation == 1
|
||
|
||
t.eq(dwl, expected.dwl or false)
|
||
t.eq(dhl, expected.dhl or false)
|
||
t.eq(cont, expected.cont or false)
|
||
end
|
||
|
||
local function pen(attribute, expected, state)
|
||
local is_bool = { bold = true, italic = true, blink = true, reverse = true }
|
||
local vterm_attribute = {
|
||
bold = vterm.VTERM_ATTR_BOLD,
|
||
underline = vterm.VTERM_ATTR_UNDERLINE,
|
||
italic = vterm.VTERM_ATTR_ITALIC,
|
||
blink = vterm.VTERM_ATTR_BLINK,
|
||
reverse = vterm.VTERM_ATTR_REVERSE,
|
||
font = vterm.VTERM_ATTR_FONT,
|
||
}
|
||
|
||
local val = t.ffi.new('VTermValue') --- @type {boolean: integer}
|
||
vterm.vterm_state_get_penattr(state, vterm_attribute[attribute], val)
|
||
local actual = val.boolean --- @type integer|boolean
|
||
if is_bool[attribute] then
|
||
actual = val.boolean == 1
|
||
end
|
||
t.eq(expected, actual)
|
||
end
|
||
|
||
local function resize(rows, cols, vt)
|
||
vterm.vterm_set_size(vt, rows, cols)
|
||
end
|
||
|
||
local function screen_chars(start_row, start_col, end_row, end_col, expected, screen)
|
||
local rect = t.ffi.new('VTermRect')
|
||
rect['start_row'] = start_row
|
||
rect['start_col'] = start_col
|
||
rect['end_row'] = end_row
|
||
rect['end_col'] = end_col
|
||
|
||
local len = vterm.vterm_screen_get_chars(screen, nil, 0, rect)
|
||
|
||
local chars = t.ffi.new('uint32_t[?]', len)
|
||
vterm.vterm_screen_get_chars(screen, chars, len, rect)
|
||
|
||
local actual = ''
|
||
for i = 0, tonumber(len) - 1 do
|
||
actual = actual .. string.char(chars[i])
|
||
end
|
||
|
||
t.eq(expected, actual)
|
||
end
|
||
|
||
local function screen_text(start_row, start_col, end_row, end_col, expected, screen)
|
||
local rect = t.ffi.new('VTermRect')
|
||
rect['start_row'] = start_row
|
||
rect['start_col'] = start_col
|
||
rect['end_row'] = end_row
|
||
rect['end_col'] = end_col
|
||
|
||
local len = vterm.vterm_screen_get_text(screen, nil, 0, rect)
|
||
|
||
local text = t.ffi.new('unsigned char[?]', len)
|
||
vterm.vterm_screen_get_text(screen, text, len, rect)
|
||
|
||
local actual = ''
|
||
for i = 0, tonumber(len) - 1 do
|
||
actual = string.format('%s%02x,', actual, text[i])
|
||
end
|
||
actual = actual:sub(1, -2)
|
||
|
||
t.eq(expected, actual)
|
||
end
|
||
|
||
--- @param row integer
|
||
local function screen_row(row, expected, screen, end_col)
|
||
local rect = t.ffi.new('VTermRect')
|
||
rect['start_row'] = row
|
||
rect['start_col'] = 0
|
||
rect['end_row'] = row + 1
|
||
rect['end_col'] = end_col or 80
|
||
|
||
local len = vterm.vterm_screen_get_text(screen, nil, 0, rect)
|
||
|
||
local text = t.ffi.new('unsigned char[?]', len)
|
||
vterm.vterm_screen_get_text(screen, text, len, rect)
|
||
|
||
t.eq(expected, t.ffi.string(text))
|
||
end
|
||
|
||
local function screen_cell(row, col, expected, screen)
|
||
local pos = t.ffi.new('VTermPos')
|
||
pos['row'] = row
|
||
pos['col'] = col
|
||
|
||
local cell = t.ffi.new('VTermScreenCell')
|
||
vterm.vterm_screen_get_cell(screen, pos, cell)
|
||
|
||
local actual = '{'
|
||
for i = 0, vterm.VTERM_MAX_CHARS_PER_CELL - 1 do
|
||
if cell['chars'][i] ~= 0 then
|
||
if i > 0 then
|
||
actual = actual .. ','
|
||
end
|
||
actual = string.format('%s%02x', actual, cell['chars'][i])
|
||
end
|
||
end
|
||
actual = string.format('%s} width=%d attrs={', actual, cell['width'])
|
||
actual = actual .. (cell['attrs'].bold ~= 0 and 'B' or '')
|
||
actual = actual
|
||
.. (cell['attrs'].underline ~= 0 and string.format('U%d', cell['attrs'].underline) or '')
|
||
actual = actual .. (cell['attrs'].italic ~= 0 and 'I' or '')
|
||
actual = actual .. (cell['attrs'].blink ~= 0 and 'K' or '')
|
||
actual = actual .. (cell['attrs'].reverse ~= 0 and 'R' or '')
|
||
actual = actual .. (cell['attrs'].font ~= 0 and string.format('F%d', cell['attrs'].font) or '')
|
||
actual = actual .. (cell['attrs'].small ~= 0 and 'S' or '')
|
||
if cell['attrs'].baseline ~= 0 then
|
||
actual = actual .. (cell['attrs'].baseline == vterm.VTERM_BASELINE_RAISE and '^' or '_')
|
||
end
|
||
actual = actual .. '} '
|
||
|
||
actual = actual .. (cell['attrs'].dwl ~= 0 and 'dwl ' or '')
|
||
if cell['attrs'].dhl ~= 0 then
|
||
actual = actual .. string.format('dhl-%s ', cell['attrs'].dhl == 2 and 'bottom' or 'top')
|
||
end
|
||
|
||
actual = string.format('%sfg=', actual)
|
||
vterm.vterm_screen_convert_color_to_rgb(screen, cell['fg'])
|
||
vterm.print_color(cell['fg'])
|
||
|
||
actual = actual .. read_rm()
|
||
actual = actual .. ' bg='
|
||
|
||
vterm.vterm_screen_convert_color_to_rgb(screen, cell['bg'])
|
||
vterm.print_color(cell['bg'])
|
||
|
||
actual = actual .. read_rm()
|
||
|
||
t.eq(expected, actual)
|
||
end
|
||
|
||
local function screen_eol(row, col, expected, screen)
|
||
local pos = t.ffi.new('VTermPos')
|
||
pos['row'] = row
|
||
pos['col'] = col
|
||
|
||
local is_eol = vterm.vterm_screen_is_eol(screen, pos)
|
||
t.eq(expected, is_eol)
|
||
end
|
||
|
||
local function screen_attrs_extent(row, col, expected, screen)
|
||
local pos = t.ffi.new('VTermPos')
|
||
pos['row'] = row
|
||
pos['col'] = col
|
||
|
||
local rect = t.ffi.new('VTermRect')
|
||
rect['start_col'] = 0
|
||
rect['end_col'] = -1
|
||
vterm.vterm_screen_get_attrs_extent(screen, rect, pos, 1)
|
||
|
||
local actual = string.format(
|
||
'%d,%d-%d,%d',
|
||
rect['start_row'],
|
||
rect['start_col'],
|
||
rect['end_row'],
|
||
rect['end_col']
|
||
)
|
||
|
||
t.eq(expected, actual)
|
||
end
|
||
|
||
local function wantencoding()
|
||
local encoding = t.ffi.new('VTermEncodingInstance')
|
||
encoding['enc'] = vterm.vterm_lookup_encoding(vterm.ENC_UTF8, string.byte('u'))
|
||
if encoding.enc.init then
|
||
encoding.enc.init(encoding.enc, encoding['data'])
|
||
end
|
||
return encoding
|
||
end
|
||
|
||
local function encin(input, encoding)
|
||
local len = string.len(input)
|
||
|
||
local cp = t.ffi.new('uint32_t[?]', len)
|
||
local cpi = t.ffi.new('int[1]')
|
||
local pos = t.ffi.new('size_t[1]', 0)
|
||
|
||
encoding.enc.decode(encoding.enc, encoding.data, cp, cpi, len, input, pos, len)
|
||
|
||
local f = assert(io.open(t.paths.vterm_test_file, 'w'))
|
||
if tonumber(cpi[0]) > 0 then
|
||
f:write('encout ')
|
||
for i = 0, cpi[0] - 1 do
|
||
if i == 0 then
|
||
f:write(string.format('%x', cp[i]))
|
||
else
|
||
f:write(string.format(',%x', cp[i]))
|
||
end
|
||
end
|
||
f:write('\n')
|
||
end
|
||
f:close()
|
||
end
|
||
|
||
local function strpe_modifiers(input_mod)
|
||
local mod = t.ffi.new('VTermModifier') ---@type any
|
||
if input_mod.C then
|
||
mod = bit.bor(mod, vterm.VTERM_MOD_CTRL)
|
||
end
|
||
if input_mod.S then
|
||
mod = bit.bor(mod, vterm.VTERM_MOD_SHIFT)
|
||
end
|
||
if input_mod.A then
|
||
mod = bit.bor(mod, vterm.VTERM_MOD_ALT)
|
||
end
|
||
return mod
|
||
end
|
||
|
||
local function strp_key(input_key)
|
||
if input_key == 'up' then
|
||
return vterm.VTERM_KEY_UP
|
||
end
|
||
|
||
if input_key == 'tab' then
|
||
return vterm.VTERM_KEY_TAB
|
||
end
|
||
|
||
if input_key == 'enter' then
|
||
return vterm.VTERM_KEY_ENTER
|
||
end
|
||
|
||
if input_key == 'f1' then
|
||
return vterm.VTERM_KEY_FUNCTION_0 + 1
|
||
end
|
||
|
||
if input_key == 'kp0' then
|
||
return vterm.VTERM_KEY_KP_0
|
||
end
|
||
|
||
return vterm.VTERM_KEY_NONE
|
||
end
|
||
|
||
local function mousemove(row, col, vt, input_mod)
|
||
input_mod = input_mod or {}
|
||
local mod = strpe_modifiers(input_mod)
|
||
vterm.vterm_mouse_move(vt, row, col, mod)
|
||
end
|
||
|
||
local function mousebtn(press, button, vt, input_mod)
|
||
input_mod = input_mod or {}
|
||
local mod = strpe_modifiers(input_mod)
|
||
local flag = press == 'd' or press == 'D'
|
||
vterm.vterm_mouse_button(vt, button, flag, mod)
|
||
end
|
||
|
||
local function inchar(c, vt, input_mod)
|
||
input_mod = input_mod or {}
|
||
local mod = strpe_modifiers(input_mod)
|
||
vterm.vterm_keyboard_unichar(vt, c, mod)
|
||
end
|
||
|
||
local function inkey(input_key, vt, input_mod)
|
||
input_mod = input_mod or {}
|
||
local mod = strpe_modifiers(input_mod)
|
||
local key = strp_key(input_key)
|
||
vterm.vterm_keyboard_key(vt, key, mod)
|
||
end
|
||
|
||
before_each(function()
|
||
vim.fs.rm(t.paths.vterm_test_file, { force = true })
|
||
end)
|
||
|
||
describe('vterm', function()
|
||
itp('02parser', function()
|
||
local vt = init()
|
||
vterm.vterm_set_utf8(vt, false)
|
||
wantparser(vt)
|
||
|
||
-- Basic text
|
||
push('hello', vt)
|
||
expect('text 68,65,6c,6c,6f')
|
||
|
||
-- C0
|
||
push('\x03', vt)
|
||
expect('control 03')
|
||
push('\x1f', vt)
|
||
expect('control 1f')
|
||
|
||
-- C1 8bit
|
||
push('\x83', vt)
|
||
expect('control 83')
|
||
push('\x99', vt)
|
||
expect('control 99')
|
||
|
||
-- C1 7bit
|
||
push('\x1b\x43', vt)
|
||
expect('control 83')
|
||
push('\x1b\x59', vt)
|
||
expect('control 99')
|
||
|
||
-- High bytes
|
||
push('\xa0\xcc\xfe', vt)
|
||
expect('text a0,cc,fe')
|
||
|
||
-- Mixed
|
||
push('1\n2', vt)
|
||
expect('text 31\ncontrol 0a\ntext 32')
|
||
|
||
-- Escape
|
||
push('\x1b=', vt)
|
||
expect('escape =')
|
||
|
||
-- Escape 2-byte
|
||
push('\x1b(X', vt)
|
||
expect('escape (X')
|
||
|
||
-- Split write Escape
|
||
push('\x1b(', vt)
|
||
push('Y', vt)
|
||
expect('escape (Y')
|
||
|
||
-- Escape cancels Escape, starts another
|
||
push('\x1b(\x1b)Z', vt)
|
||
expect('escape )Z')
|
||
|
||
-- CAN cancels Escape, returns to normal mode
|
||
push('\x1b(\x18AB', vt)
|
||
expect('text 41,42')
|
||
|
||
-- C0 in Escape interrupts and continues
|
||
push('\x1b(\nX', vt)
|
||
expect('control 0a\nescape (X')
|
||
|
||
-- CSI 0 args
|
||
push('\x1b[a', vt)
|
||
expect('csi 61 *')
|
||
|
||
-- CSI 1 arg
|
||
push('\x1b[9b', vt)
|
||
expect('csi 62 9')
|
||
|
||
-- CSI 2 args
|
||
push('\x1b[3;4c', vt)
|
||
expect('csi 63 3,4')
|
||
|
||
-- CSI 1 arg 1 sub
|
||
push('\x1b[1:2c', vt)
|
||
expect('csi 63 1+,2')
|
||
|
||
-- CSI many digits
|
||
push('\x1b[678d', vt)
|
||
expect('csi 64 678')
|
||
|
||
-- CSI leading zero
|
||
push('\x1b[007e', vt)
|
||
expect('csi 65 7')
|
||
|
||
-- CSI qmark
|
||
push('\x1b[?2;7f', vt)
|
||
expect('csi 66 L=3f 2,7')
|
||
|
||
-- CSI greater
|
||
push('\x1b[>c', vt)
|
||
expect('csi 63 L=3e *')
|
||
|
||
-- CSI SP
|
||
push('\x1b[12 q', vt)
|
||
expect('csi 71 12 I=20')
|
||
|
||
-- Mixed CSI
|
||
push('A\x1b[8mB', vt)
|
||
expect('text 41\ncsi 6d 8\ntext 42')
|
||
|
||
-- Split write
|
||
push('\x1b', vt)
|
||
push('[a', vt)
|
||
expect('csi 61 *')
|
||
push('foo\x1b[', vt)
|
||
expect('text 66,6f,6f')
|
||
push('4b', vt)
|
||
expect('csi 62 4')
|
||
push('\x1b[12;', vt)
|
||
push('3c', vt)
|
||
expect('csi 63 12,3')
|
||
|
||
-- Escape cancels CSI, starts Escape
|
||
push('\x1b[123\x1b9', vt)
|
||
expect('escape 9')
|
||
|
||
-- CAN cancels CSI, returns to normal mode
|
||
push('\x1b[12\x18AB', vt)
|
||
expect('text 41,42')
|
||
|
||
-- C0 in Escape interrupts and continues
|
||
push('\x1b(\nX', vt)
|
||
expect('control 0a\nescape (X')
|
||
|
||
-- OSC BEL
|
||
push('\x1b]1;Hello\x07', vt)
|
||
expect('osc [1;Hello]')
|
||
|
||
-- OSC ST (7bit)
|
||
push('\x1b]1;Hello\x1b\\', vt)
|
||
expect('osc [1;Hello]')
|
||
|
||
-- OSC ST (8bit)
|
||
push('\x9d1;Hello\x9c', vt)
|
||
expect('osc [1;Hello]')
|
||
|
||
-- OSC in parts
|
||
push('\x1b]52;abc', vt)
|
||
expect('osc [52;abc')
|
||
push('def', vt)
|
||
expect('osc def')
|
||
push('ghi\x1b\\', vt)
|
||
expect('osc ghi]')
|
||
|
||
-- OSC BEL without semicolon
|
||
push('\x1b]1234\x07', vt)
|
||
expect('osc [1234;]')
|
||
|
||
-- OSC ST without semicolon
|
||
push('\x1b]1234\x1b\\', vt)
|
||
expect('osc [1234;]')
|
||
|
||
-- Escape cancels OSC, starts Escape
|
||
push('\x1b]Something\x1b9', vt)
|
||
expect('escape 9')
|
||
|
||
-- CAN cancels OSC, returns to normal mode
|
||
push('\x1b]12\x18AB', vt)
|
||
expect('text 41,42')
|
||
|
||
-- C0 in OSC interrupts and continues
|
||
push('\x1b]2;\nBye\x07', vt)
|
||
expect('osc [2;\ncontrol 0a\nosc Bye]')
|
||
|
||
-- DCS BEL
|
||
push('\x1bPHello\x07', vt)
|
||
expect('dcs [Hello]')
|
||
|
||
-- DCS ST (7bit)
|
||
push('\x1bPHello\x1b\\', vt)
|
||
expect('dcs [Hello]')
|
||
|
||
-- DCS ST (8bit)
|
||
push('\x90Hello\x9c', vt)
|
||
expect('dcs [Hello]')
|
||
|
||
-- Split write of 7bit ST
|
||
push('\x1bPABC\x1b', vt)
|
||
expect('dcs [ABC')
|
||
push('\\', vt)
|
||
expect('dcs ]')
|
||
|
||
-- Escape cancels DCS, starts Escape
|
||
push('\x1bPSomething\x1b9', vt)
|
||
expect('escape 9')
|
||
|
||
-- CAN cancels DCS, returns to normal mode
|
||
push('\x1bP12\x18AB', vt)
|
||
expect('text 41,42')
|
||
|
||
-- C0 in OSC interrupts and continues
|
||
push('\x1bPBy\ne\x07', vt)
|
||
expect('dcs [By\ncontrol 0a\ndcs e]')
|
||
|
||
-- APC BEL
|
||
push('\x1b_Hello\x07', vt)
|
||
expect('apc [Hello]')
|
||
|
||
-- APC ST (7bit)
|
||
push('\x1b_Hello\x1b\\', vt)
|
||
expect('apc [Hello]')
|
||
|
||
-- APC ST (8bit)
|
||
push('\x9fHello\x9c', vt)
|
||
expect('apc [Hello]')
|
||
|
||
-- PM BEL
|
||
push('\x1b^Hello\x07', vt)
|
||
expect('pm [Hello]')
|
||
|
||
-- PM ST (7bit)
|
||
push('\x1b^Hello\x1b\\', vt)
|
||
expect('pm [Hello]')
|
||
|
||
-- PM ST (8bit)
|
||
push('\x9eHello\x9c', vt)
|
||
expect('pm [Hello]')
|
||
|
||
-- SOS BEL
|
||
push('\x1bXHello\x07', vt)
|
||
expect('sos [Hello]')
|
||
|
||
-- SOS ST (7bit)
|
||
push('\x1bXHello\x1b\\', vt)
|
||
expect('sos [Hello]')
|
||
|
||
-- SOS ST (8bit)
|
||
push('\x98Hello\x9c', vt)
|
||
expect('sos [Hello]')
|
||
|
||
push('\x1bXABC\x01DEF\x1b\\', vt)
|
||
expect('sos [ABC\x01DEF]')
|
||
push('\x1bXABC\x99DEF\x1b\\', vt)
|
||
expect('sos [ABC\x99DEF]')
|
||
|
||
-- NUL ignored
|
||
push('\x00', vt)
|
||
|
||
-- NUL ignored within CSI
|
||
push('\x1b[12\x003m', vt)
|
||
expect('csi 6d 123')
|
||
|
||
-- DEL ignored
|
||
push('\x7f', vt)
|
||
|
||
-- DEL ignored within CSI
|
||
push('\x1b[12\x7f3m', vt)
|
||
expect('csi 6d 123')
|
||
|
||
-- DEL inside text"
|
||
push('AB\x7fC', vt)
|
||
expect('text 41,42\ntext 43')
|
||
end)
|
||
|
||
itp('03encoding_utf8', function()
|
||
local encoding = wantencoding()
|
||
|
||
-- Low
|
||
encin('123', encoding)
|
||
expect('encout 31,32,33')
|
||
|
||
-- We want to prove the UTF-8 parser correctly handles all the sequences.
|
||
-- Easy way to do this is to check it does low/high boundary cases, as that
|
||
-- leaves only two for each sequence length
|
||
--
|
||
-- These ranges are therefore:
|
||
--
|
||
-- Two bytes:
|
||
-- U+0080 = 000 10000000 => 00010 000000
|
||
-- => 11000010 10000000 = C2 80
|
||
-- U+07FF = 111 11111111 => 11111 111111
|
||
-- => 11011111 10111111 = DF BF
|
||
--
|
||
-- Three bytes:
|
||
-- U+0800 = 00001000 00000000 => 0000 100000 000000
|
||
-- => 11100000 10100000 10000000 = E0 A0 80
|
||
-- U+FFFD = 11111111 11111101 => 1111 111111 111101
|
||
-- => 11101111 10111111 10111101 = EF BF BD
|
||
-- (We avoid U+FFFE and U+FFFF as they're invalid codepoints)
|
||
--
|
||
-- Four bytes:
|
||
-- U+10000 = 00001 00000000 00000000 => 000 010000 000000 000000
|
||
-- => 11110000 10010000 10000000 10000000 = F0 90 80 80
|
||
-- U+1FFFFF = 11111 11111111 11111111 => 111 111111 111111 111111
|
||
-- => 11110111 10111111 10111111 10111111 = F7 BF BF BF
|
||
|
||
-- 2 byte
|
||
encin('\xC2\x80\xDF\xBF', encoding)
|
||
expect('encout 80,7ff')
|
||
|
||
-- 3 byte
|
||
encin('\xE0\xA0\x80\xEF\xBF\xBD', encoding)
|
||
expect('encout 800,fffd')
|
||
|
||
-- 4 byte
|
||
encin('\xF0\x90\x80\x80\xF7\xBF\xBF\xBF', encoding)
|
||
expect('encout 10000,1fffff')
|
||
|
||
-- Next up, we check some invalid sequences
|
||
-- + Early termination (back to low bytes too soon)
|
||
-- + Early restart (another sequence introduction before the previous one was finished)
|
||
|
||
-- Early termination
|
||
encin('\xC2!', encoding)
|
||
expect('encout fffd,21')
|
||
|
||
encin('\xE0!\xE0\xA0!', encoding)
|
||
expect('encout fffd,21,fffd,21')
|
||
|
||
encin('\xF0!\xF0\x90!\xF0\x90\x80!', encoding)
|
||
expect('encout fffd,21,fffd,21,fffd,21')
|
||
|
||
-- Early restart
|
||
encin('\xC2\xC2\x90', encoding)
|
||
expect('encout fffd,90')
|
||
|
||
encin('\xE0\xC2\x90\xE0\xA0\xC2\x90', encoding)
|
||
expect('encout fffd,90,fffd,90')
|
||
|
||
encin('\xF0\xC2\x90\xF0\x90\xC2\x90\xF0\x90\x80\xC2\x90', encoding)
|
||
expect('encout fffd,90,fffd,90,fffd,90')
|
||
|
||
-- Test the overlong sequences by giving an overlong encoding of U+0000 and
|
||
-- an encoding of the highest codepoint still too short
|
||
--
|
||
-- Two bytes:
|
||
-- U+0000 = C0 80
|
||
-- U+007F = 000 01111111 => 00001 111111 =>
|
||
-- => 11000001 10111111 => C1 BF
|
||
--
|
||
-- Three bytes:
|
||
-- U+0000 = E0 80 80
|
||
-- U+07FF = 00000111 11111111 => 0000 011111 111111
|
||
-- => 11100000 10011111 10111111 = E0 9F BF
|
||
--
|
||
-- Four bytes:
|
||
-- U+0000 = F0 80 80 80
|
||
-- U+FFFF = 11111111 11111111 => 000 001111 111111 111111
|
||
-- => 11110000 10001111 10111111 10111111 = F0 8F BF BF
|
||
|
||
-- Overlong
|
||
encin('\xC0\x80\xC1\xBF', encoding)
|
||
expect('encout fffd,fffd')
|
||
|
||
encin('\xE0\x80\x80\xE0\x9F\xBF', encoding)
|
||
expect('encout fffd,fffd')
|
||
|
||
encin('\xF0\x80\x80\x80\xF0\x8F\xBF\xBF', encoding)
|
||
expect('encout fffd,fffd')
|
||
|
||
-- UTF-16 surrogates U+D800 and U+DFFF
|
||
-- UTF-16 Surrogates
|
||
encin('\xED\xA0\x80\xED\xBF\xBF', encoding)
|
||
expect('encout fffd,fffd')
|
||
|
||
-- Split write
|
||
encin('\xC2', encoding)
|
||
encin('\xA0', encoding)
|
||
expect('encout a0')
|
||
|
||
encin('\xE0', encoding)
|
||
encin('\xA0\x80', encoding)
|
||
expect('encout 800')
|
||
encin('\xE0\xA0', encoding)
|
||
encin('\x80', encoding)
|
||
expect('encout 800')
|
||
|
||
encin('\xF0', encoding)
|
||
encin('\x90\x80\x80', encoding)
|
||
expect('encout 10000')
|
||
encin('\xF0\x90', encoding)
|
||
encin('\x80\x80', encoding)
|
||
expect('encout 10000')
|
||
encin('\xF0\x90\x80', encoding)
|
||
encin('\x80', encoding)
|
||
expect('encout 10000')
|
||
end)
|
||
|
||
itp('10state_putglyph', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true })
|
||
|
||
-- Low
|
||
reset(state, nil)
|
||
push('ABC', vt)
|
||
expect('putglyph 41 1 0,0\nputglyph 42 1 0,1\nputglyph 43 1 0,2')
|
||
|
||
-- UTF-8 1 char
|
||
-- U+00C1 = 0xC3 0x81 name: LATIN CAPITAL LETTER A WITH ACUTE
|
||
-- U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE
|
||
reset(state, nil)
|
||
push('\xC3\x81\xC3\xA9', vt)
|
||
expect('putglyph c1 1 0,0\nputglyph e9 1 0,1')
|
||
|
||
-- UTF-8 split writes
|
||
reset(state, nil)
|
||
push('\xC3', vt)
|
||
push('\x81', vt)
|
||
expect('putglyph c1 1 0,0')
|
||
|
||
-- UTF-8 wide char
|
||
-- U+FF10 = EF BC 90 name: FULLWIDTH DIGIT ZERO
|
||
reset(state, nil)
|
||
push('\xEF\xBC\x90 ', vt)
|
||
expect('putglyph ff10 2 0,0\nputglyph 20 1 0,2')
|
||
|
||
-- UTF-8 emoji wide char
|
||
-- U+1F600 = F0 9F 98 80 name: GRINNING FACE
|
||
reset(state, nil)
|
||
push('\xF0\x9F\x98\x80 ', vt)
|
||
expect('putglyph 1f600 2 0,0\nputglyph 20 1 0,2')
|
||
|
||
-- UTF-8 combining chars
|
||
-- U+0301 = CC 81 name: COMBINING ACUTE
|
||
reset(state, nil)
|
||
push('e\xCC\x81Z', vt)
|
||
expect('putglyph 65,301 1 0,0\nputglyph 5a 1 0,1')
|
||
|
||
-- Combining across buffers
|
||
reset(state, nil)
|
||
push('e', vt)
|
||
expect('putglyph 65 1 0,0')
|
||
push('\xCC\x81Z', vt)
|
||
expect('putglyph 65,301 1 0,0\nputglyph 5a 1 0,1')
|
||
|
||
-- Spare combining chars get truncated
|
||
reset(state, nil)
|
||
push('e' .. string.rep('\xCC\x81', 10), vt)
|
||
expect('putglyph 65,301,301,301,301,301 1 0,0') -- and nothing more
|
||
|
||
reset(state, nil)
|
||
push('e', vt)
|
||
expect('putglyph 65 1 0,0')
|
||
push('\xCC\x81', vt)
|
||
expect('putglyph 65,301 1 0,0')
|
||
push('\xCC\x82', vt)
|
||
expect('putglyph 65,301,302 1 0,0')
|
||
|
||
-- DECSCA protected
|
||
reset(state, nil)
|
||
push('A\x1b[1"qB\x1b[2"qC', vt)
|
||
expect('putglyph 41 1 0,0\nputglyph 42 1 0,1 prot\nputglyph 43 1 0,2')
|
||
end)
|
||
|
||
itp('11state_movecursor', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
|
||
-- Implicit
|
||
push('ABC', vt)
|
||
cursor(0, 3, state)
|
||
|
||
-- Backspace
|
||
push('\b', vt)
|
||
cursor(0, 2, state)
|
||
-- Horizontal Tab
|
||
push('\t', vt)
|
||
cursor(0, 8, state)
|
||
-- Carriage Return
|
||
push('\r', vt)
|
||
cursor(0, 0, state)
|
||
-- Linefeed
|
||
push('\n', vt)
|
||
cursor(1, 0, state)
|
||
|
||
-- Backspace bounded by lefthand edge
|
||
push('\x1b[4;2H', vt)
|
||
cursor(3, 1, state)
|
||
push('\b', vt)
|
||
cursor(3, 0, state)
|
||
push('\b', vt)
|
||
cursor(3, 0, state)
|
||
|
||
-- Backspace cancels phantom
|
||
push('\x1b[4;80H', vt)
|
||
cursor(3, 79, state)
|
||
push('X', vt)
|
||
cursor(3, 79, state)
|
||
push('\b', vt)
|
||
cursor(3, 78, state)
|
||
|
||
-- HT bounded by righthand edge
|
||
push('\x1b[1;78H', vt)
|
||
cursor(0, 77, state)
|
||
push('\t', vt)
|
||
cursor(0, 79, state)
|
||
push('\t', vt)
|
||
cursor(0, 79, state)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Index
|
||
push('ABC\x1bD', vt)
|
||
cursor(1, 3, state)
|
||
-- Reverse Index
|
||
push('\x1bM', vt)
|
||
cursor(0, 3, state)
|
||
-- Newline
|
||
push('\x1bE', vt)
|
||
cursor(1, 0, state)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Cursor Forward
|
||
push('\x1b[B', vt)
|
||
cursor(1, 0, state)
|
||
push('\x1b[3B', vt)
|
||
cursor(4, 0, state)
|
||
push('\x1b[0B', vt)
|
||
cursor(5, 0, state)
|
||
|
||
-- Cursor Down
|
||
push('\x1b[C', vt)
|
||
cursor(5, 1, state)
|
||
push('\x1b[3C', vt)
|
||
cursor(5, 4, state)
|
||
push('\x1b[0C', vt)
|
||
cursor(5, 5, state)
|
||
|
||
-- Cursor Up
|
||
push('\x1b[A', vt)
|
||
cursor(4, 5, state)
|
||
push('\x1b[3A', vt)
|
||
cursor(1, 5, state)
|
||
push('\x1b[0A', vt)
|
||
cursor(0, 5, state)
|
||
|
||
-- Cursor Backward
|
||
push('\x1b[D', vt)
|
||
cursor(0, 4, state)
|
||
push('\x1b[3D', vt)
|
||
cursor(0, 1, state)
|
||
push('\x1b[0D', vt)
|
||
cursor(0, 0, state)
|
||
|
||
-- Cursor Next Line
|
||
push(' ', vt)
|
||
cursor(0, 3, state)
|
||
push('\x1b[E', vt)
|
||
cursor(1, 0, state)
|
||
push(' ', vt)
|
||
cursor(1, 3, state)
|
||
push('\x1b[2E', vt)
|
||
cursor(3, 0, state)
|
||
push('\x1b[0E', vt)
|
||
cursor(4, 0, state)
|
||
|
||
-- Cursor Previous Line
|
||
push(' ', vt)
|
||
cursor(4, 3, state)
|
||
push('\x1b[F', vt)
|
||
cursor(3, 0, state)
|
||
push(' ', vt)
|
||
cursor(3, 3, state)
|
||
push('\x1b[2F', vt)
|
||
cursor(1, 0, state)
|
||
push('\x1b[0F', vt)
|
||
cursor(0, 0, state)
|
||
|
||
-- Cursor Horizonal Absolute
|
||
push('\n', vt)
|
||
cursor(1, 0, state)
|
||
push('\x1b[20G', vt)
|
||
cursor(1, 19, state)
|
||
push('\x1b[G', vt)
|
||
cursor(1, 0, state)
|
||
|
||
-- Cursor Position
|
||
push('\x1b[10;5H', vt)
|
||
cursor(9, 4, state)
|
||
push('\x1b[8H', vt)
|
||
cursor(7, 0, state)
|
||
push('\x1b[H', vt)
|
||
cursor(0, 0, state)
|
||
|
||
-- Cursor Position cancels phantom
|
||
push('\x1b[10;78H', vt)
|
||
cursor(9, 77, state)
|
||
push('ABC', vt)
|
||
cursor(9, 79, state)
|
||
push('\x1b[10;80H', vt)
|
||
push('C', vt)
|
||
cursor(9, 79, state)
|
||
push('X', vt)
|
||
cursor(10, 1, state)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Bounds Checking
|
||
push('\x1b[A', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[D', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[25;80H', vt)
|
||
cursor(24, 79, state)
|
||
push('\x1b[B', vt)
|
||
cursor(24, 79, state)
|
||
push('\x1b[C', vt)
|
||
cursor(24, 79, state)
|
||
push('\x1b[E', vt)
|
||
cursor(24, 0, state)
|
||
push('\x1b[H', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[F', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[999G', vt)
|
||
cursor(0, 79, state)
|
||
push('\x1b[99;99H', vt)
|
||
cursor(24, 79, state)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Horizontal Position Absolute
|
||
push('\x1b[5`', vt)
|
||
cursor(0, 4, state)
|
||
|
||
-- Horizontal Position Relative
|
||
push('\x1b[3a', vt)
|
||
cursor(0, 7, state)
|
||
|
||
-- Horizontal Position Backward
|
||
push('\x1b[3j', vt)
|
||
cursor(0, 4, state)
|
||
|
||
-- Horizontal and Vertical Position
|
||
push('\x1b[3;3f', vt)
|
||
cursor(2, 2, state)
|
||
|
||
-- Vertical Position Absolute
|
||
push('\x1b[5d', vt)
|
||
cursor(4, 2, state)
|
||
|
||
-- Vertical Position Relative
|
||
push('\x1b[2e', vt)
|
||
cursor(6, 2, state)
|
||
|
||
-- Vertical Position Backward
|
||
push('\x1b[2k', vt)
|
||
cursor(4, 2, state)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Horizontal Tab
|
||
push('\t', vt)
|
||
cursor(0, 8, state)
|
||
push(' ', vt)
|
||
cursor(0, 11, state)
|
||
push('\t', vt)
|
||
cursor(0, 16, state)
|
||
push(' ', vt)
|
||
cursor(0, 23, state)
|
||
push('\t', vt)
|
||
cursor(0, 24, state)
|
||
push(' ', vt)
|
||
cursor(0, 32, state)
|
||
push('\t', vt)
|
||
cursor(0, 40, state)
|
||
|
||
-- Cursor Horizontal Tab
|
||
push('\x1b[I', vt)
|
||
cursor(0, 48, state)
|
||
push('\x1b[2I', vt)
|
||
cursor(0, 64, state)
|
||
|
||
-- Cursor Backward Tab
|
||
push('\x1b[Z', vt)
|
||
cursor(0, 56, state)
|
||
push('\x1b[2Z', vt)
|
||
cursor(0, 40, state)
|
||
end)
|
||
|
||
itp('12state_scroll', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { s = true })
|
||
|
||
-- Linefeed
|
||
push(string.rep('\n', 24), vt)
|
||
cursor(24, 0, state)
|
||
push('\n', vt)
|
||
expect('scrollrect 0..25,0..80 => +1,+0')
|
||
cursor(24, 0, state)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Index
|
||
push('\x1b[25H', vt)
|
||
push('\x1bD', vt)
|
||
expect('scrollrect 0..25,0..80 => +1,+0')
|
||
|
||
reset(state, nil)
|
||
|
||
-- Reverse Index
|
||
push('\x1bM', vt)
|
||
expect('scrollrect 0..25,0..80 => -1,+0')
|
||
|
||
reset(state, nil)
|
||
|
||
-- Linefeed in DECSTBM
|
||
push('\x1b[1;10r', vt)
|
||
cursor(0, 0, state)
|
||
push(string.rep('\n', 9), vt)
|
||
cursor(9, 0, state)
|
||
push('\n', vt)
|
||
expect('scrollrect 0..10,0..80 => +1,+0')
|
||
cursor(9, 0, state)
|
||
|
||
-- Linefeed outside DECSTBM
|
||
push('\x1b[20H', vt)
|
||
cursor(19, 0, state)
|
||
push('\n', vt)
|
||
cursor(20, 0, state)
|
||
|
||
-- Index in DECSTBM
|
||
push('\x1b[9;10r', vt)
|
||
push('\x1b[10H', vt)
|
||
push('\x1bM', vt)
|
||
cursor(8, 0, state)
|
||
push('\x1bM', vt)
|
||
expect('scrollrect 8..10,0..80 => -1,+0')
|
||
|
||
-- Reverse Index in DECSTBM
|
||
push('\x1b[25H', vt)
|
||
cursor(24, 0, state)
|
||
push('\n', vt)
|
||
-- no scrollrect
|
||
cursor(24, 0, state)
|
||
|
||
-- Linefeed in DECSTBM+DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[3;10r\x1b[10;40s', vt)
|
||
push('\x1b[10;10H\n', vt)
|
||
expect('scrollrect 2..10,9..40 => +1,+0')
|
||
|
||
-- IND/RI in DECSTBM+DECSLRM
|
||
push('\x1bD', vt)
|
||
expect('scrollrect 2..10,9..40 => +1,+0')
|
||
push('\x1b[3;10H\x1bM', vt)
|
||
expect('scrollrect 2..10,9..40 => -1,+0')
|
||
|
||
-- DECRQSS on DECSTBM
|
||
push('\x1bP$qr\x1b\\', vt)
|
||
expect_output('\x1bP1$r3;10r\x1b\\')
|
||
|
||
-- DECRQSS on DECSLRM
|
||
push('\x1bP$qs\x1b\\', vt)
|
||
expect_output('\x1bP1$r10;40s\x1b\\')
|
||
|
||
-- Setting invalid DECSLRM with !DECVSSM is still rejected
|
||
push('\x1b[?69l\x1b[;0s\x1b[?69h', vt)
|
||
|
||
reset(state, nil)
|
||
|
||
-- Scroll Down
|
||
push('\x1b[S', vt)
|
||
expect('scrollrect 0..25,0..80 => +1,+0')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2S', vt)
|
||
expect('scrollrect 0..25,0..80 => +2,+0')
|
||
cursor(0, 0, state)
|
||
push('\x1b[100S', vt)
|
||
expect('scrollrect 0..25,0..80 => +25,+0')
|
||
|
||
-- Scroll Up
|
||
push('\x1b[T', vt)
|
||
expect('scrollrect 0..25,0..80 => -1,+0')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2T', vt)
|
||
expect('scrollrect 0..25,0..80 => -2,+0')
|
||
cursor(0, 0, state)
|
||
push('\x1b[100T', vt)
|
||
expect('scrollrect 0..25,0..80 => -25,+0')
|
||
|
||
-- SD/SU in DECSTBM
|
||
push('\x1b[5;20r', vt)
|
||
push('\x1b[S', vt)
|
||
expect('scrollrect 4..20,0..80 => +1,+0')
|
||
push('\x1b[T', vt)
|
||
expect('scrollrect 4..20,0..80 => -1,+0')
|
||
|
||
reset(state, nil)
|
||
|
||
-- SD/SU in DECSTBM+DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[3;10r\x1b[10;40s', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[3;10H', vt)
|
||
cursor(2, 9, state)
|
||
push('\x1b[S', vt)
|
||
expect('scrollrect 2..10,9..40 => +1,+0')
|
||
push('\x1b[?69l', vt)
|
||
push('\x1b[S', vt)
|
||
expect('scrollrect 2..10,0..80 => +1,+0')
|
||
|
||
-- Invalid boundaries
|
||
reset(state, nil)
|
||
|
||
push('\x1b[100;105r\x1bD', vt)
|
||
push('\x1b[5;2r\x1bD', vt)
|
||
|
||
reset(state, nil)
|
||
state = wantstate(vt, { m = true, e = true })
|
||
|
||
-- Scroll Down move+erase emulation
|
||
push('\x1b[S', vt)
|
||
expect('moverect 1..25,0..80 -> 0..24,0..80\nerase 24..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2S', vt)
|
||
expect('moverect 2..25,0..80 -> 0..23,0..80\nerase 23..25,0..80')
|
||
cursor(0, 0, state)
|
||
|
||
-- Scroll Up move+erase emulation
|
||
push('\x1b[T', vt)
|
||
expect('moverect 0..24,0..80 -> 1..25,0..80\nerase 0..1,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2T', vt)
|
||
expect('moverect 0..23,0..80 -> 2..25,0..80\nerase 0..2,0..80')
|
||
cursor(0, 0, state)
|
||
|
||
-- DECSTBM resets cursor position
|
||
push('\x1b[5;5H', vt)
|
||
cursor(4, 4, state)
|
||
push('\x1b[r', vt)
|
||
cursor(0, 0, state)
|
||
end)
|
||
|
||
itp('13state_edit', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { s = true, e = true, b = true })
|
||
|
||
-- ICH
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ACD', vt)
|
||
push('\x1b[2D', vt)
|
||
cursor(0, 1, state)
|
||
push('\x1b[@', vt)
|
||
expect('scrollrect 0..1,1..80 => +0,-1')
|
||
cursor(0, 1, state)
|
||
push('B', vt)
|
||
cursor(0, 2, state)
|
||
push('\x1b[3@', vt)
|
||
expect('scrollrect 0..1,2..80 => +0,-3')
|
||
|
||
-- ICH with DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[;50s', vt)
|
||
push('\x1b[20G\x1b[@', vt)
|
||
expect('scrollrect 0..1,19..50 => +0,-1')
|
||
|
||
-- ICH outside DECSLRM
|
||
push('\x1b[70G\x1b[@', vt)
|
||
-- nothing happens
|
||
|
||
-- DCH
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ABBC', vt)
|
||
push('\x1b[3D', vt)
|
||
cursor(0, 1, state)
|
||
push('\x1b[P', vt)
|
||
expect('scrollrect 0..1,1..80 => +0,+1')
|
||
cursor(0, 1, state)
|
||
push('\x1b[3P', vt)
|
||
expect('scrollrect 0..1,1..80 => +0,+3')
|
||
cursor(0, 1, state)
|
||
|
||
-- DCH with DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[;50s', vt)
|
||
push('\x1b[20G\x1b[P', vt)
|
||
expect('scrollrect 0..1,19..50 => +0,+1')
|
||
|
||
-- DCH outside DECSLRM
|
||
push('\x1b[70G\x1b[P', vt)
|
||
-- nothing happens
|
||
|
||
-- ECH
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ABC', vt)
|
||
push('\x1b[2D', vt)
|
||
cursor(0, 1, state)
|
||
push('\x1b[X', vt)
|
||
expect('erase 0..1,1..2')
|
||
cursor(0, 1, state)
|
||
push('\x1b[3X', vt)
|
||
expect('erase 0..1,1..4')
|
||
cursor(0, 1, state)
|
||
-- ECH more columns than there are should be bounded
|
||
push('\x1b[100X', vt)
|
||
expect('erase 0..1,1..80')
|
||
|
||
-- IL
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('A\r\nC', vt)
|
||
cursor(1, 1, state)
|
||
push('\x1b[L', vt)
|
||
expect('scrollrect 1..25,0..80 => -1,+0')
|
||
-- TODO(libvterm): ECMA-48 says we should move to line home, but neither xterm nor xfce4-terminal do this
|
||
cursor(1, 1, state)
|
||
push('\rB', vt)
|
||
cursor(1, 1, state)
|
||
push('\x1b[3L', vt)
|
||
expect('scrollrect 1..25,0..80 => -3,+0')
|
||
|
||
-- IL with DECSTBM
|
||
push('\x1b[5;15r', vt)
|
||
push('\x1b[5H\x1b[L', vt)
|
||
expect('scrollrect 4..15,0..80 => -1,+0')
|
||
|
||
-- IL outside DECSTBM
|
||
push('\x1b[20H\x1b[L', vt)
|
||
-- nothing happens
|
||
|
||
-- IL with DECSTBM+DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[10;50s', vt)
|
||
push('\x1b[5;10H\x1b[L', vt)
|
||
expect('scrollrect 4..15,9..50 => -1,+0')
|
||
|
||
-- DL
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('A\r\nB\r\nB\r\nC', vt)
|
||
cursor(3, 1, state)
|
||
push('\x1b[2H', vt)
|
||
cursor(1, 0, state)
|
||
push('\x1b[M', vt)
|
||
expect('scrollrect 1..25,0..80 => +1,+0')
|
||
cursor(1, 0, state)
|
||
push('\x1b[3M', vt)
|
||
expect('scrollrect 1..25,0..80 => +3,+0')
|
||
cursor(1, 0, state)
|
||
|
||
-- DL with DECSTBM
|
||
push('\x1b[5;15r', vt)
|
||
push('\x1b[5H\x1b[M', vt)
|
||
expect('scrollrect 4..15,0..80 => +1,+0')
|
||
|
||
-- DL outside DECSTBM
|
||
push('\x1b[20H\x1b[M', vt)
|
||
-- nothing happens
|
||
|
||
-- DL with DECSTBM+DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[10;50s', vt)
|
||
push('\x1b[5;10H\x1b[M', vt)
|
||
expect('scrollrect 4..15,9..50 => +1,+0')
|
||
|
||
-- DECIC
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
push("\x1b[20G\x1b[5'}", vt)
|
||
expect('scrollrect 0..25,19..80 => +0,-5')
|
||
|
||
-- DECIC with DECSTBM+DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[4;20r\x1b[20;60s', vt)
|
||
push("\x1b[4;20H\x1b[3'}", vt)
|
||
expect('scrollrect 3..20,19..60 => +0,-3')
|
||
|
||
-- DECIC outside DECSLRM
|
||
push("\x1b[70G\x1b['}", vt)
|
||
-- nothing happens
|
||
|
||
-- DECDC
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
push("\x1b[20G\x1b[5'~", vt)
|
||
expect('scrollrect 0..25,19..80 => +0,+5')
|
||
|
||
-- DECDC with DECSTBM+DECSLRM
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[4;20r\x1b[20;60s', vt)
|
||
push("\x1b[4;20H\x1b[3'~", vt)
|
||
expect('scrollrect 3..20,19..60 => +0,+3')
|
||
|
||
-- DECDC outside DECSLRM
|
||
push("\x1b[70G\x1b['~", vt)
|
||
-- nothing happens
|
||
|
||
-- EL 0
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ABCDE', vt)
|
||
push('\x1b[3D', vt)
|
||
cursor(0, 2, state)
|
||
push('\x1b[0K', vt)
|
||
expect('erase 0..1,2..80')
|
||
cursor(0, 2, state)
|
||
|
||
-- EL 1
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ABCDE', vt)
|
||
push('\x1b[3D', vt)
|
||
cursor(0, 2, state)
|
||
push('\x1b[1K', vt)
|
||
expect('erase 0..1,0..3')
|
||
cursor(0, 2, state)
|
||
|
||
-- EL 2
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ABCDE', vt)
|
||
push('\x1b[3D', vt)
|
||
cursor(0, 2, state)
|
||
push('\x1b[2K', vt)
|
||
expect('erase 0..1,0..80')
|
||
cursor(0, 2, state)
|
||
|
||
-- SEL
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[11G', vt)
|
||
cursor(0, 10, state)
|
||
push('\x1b[?0K', vt)
|
||
expect('erase 0..1,10..80 selective')
|
||
cursor(0, 10, state)
|
||
push('\x1b[?1K', vt)
|
||
expect('erase 0..1,0..11 selective')
|
||
cursor(0, 10, state)
|
||
push('\x1b[?2K', vt)
|
||
expect('erase 0..1,0..80 selective')
|
||
cursor(0, 10, state)
|
||
|
||
-- ED 0
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2;2H', vt)
|
||
cursor(1, 1, state)
|
||
push('\x1b[0J', vt)
|
||
expect('erase 1..2,1..80\nerase 2..25,0..80')
|
||
cursor(1, 1, state)
|
||
|
||
-- ED 1
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2;2H', vt)
|
||
cursor(1, 1, state)
|
||
push('\x1b[1J', vt)
|
||
expect('erase 0..1,0..80\nerase 1..2,0..2')
|
||
cursor(1, 1, state)
|
||
|
||
-- ED 2
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[2;2H', vt)
|
||
cursor(1, 1, state)
|
||
push('\x1b[2J', vt)
|
||
expect('erase 0..25,0..80')
|
||
cursor(1, 1, state)
|
||
|
||
-- ED 3
|
||
push('\x1b[3J', vt)
|
||
expect('sb_clear')
|
||
|
||
-- SED
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
push('\x1b[5;5H', vt)
|
||
cursor(4, 4, state)
|
||
push('\x1b[?0J', vt)
|
||
expect('erase 4..5,4..80 selective\nerase 5..25,0..80 selective')
|
||
cursor(4, 4, state)
|
||
push('\x1b[?1J', vt)
|
||
expect('erase 0..4,0..80 selective\nerase 4..5,0..5 selective')
|
||
cursor(4, 4, state)
|
||
push('\x1b[?2J', vt)
|
||
expect('erase 0..25,0..80 selective')
|
||
cursor(4, 4, state)
|
||
|
||
-- DECRQSS on DECSCA
|
||
push('\x1b[2"q', vt)
|
||
push('\x1bP$q"q\x1b\\', vt)
|
||
expect_output('\x1bP1$r2"q\x1b\\')
|
||
|
||
state = wantstate(vt, { m = true, e = true, b = true })
|
||
expect('erase 0..25,0..80') -- TODO(dundargoc): strange, this should not be needed according to the original code
|
||
|
||
-- ICH move+erase emuation
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ACD', vt)
|
||
push('\x1b[2D', vt)
|
||
cursor(0, 1, state)
|
||
push('\x1b[@', vt)
|
||
expect('moverect 0..1,1..79 -> 0..1,2..80\nerase 0..1,1..2')
|
||
cursor(0, 1, state)
|
||
push('B', vt)
|
||
cursor(0, 2, state)
|
||
push('\x1b[3@', vt)
|
||
expect('moverect 0..1,2..77 -> 0..1,5..80\nerase 0..1,2..5')
|
||
|
||
-- DCH move+erase emulation
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('ABBC', vt)
|
||
push('\x1b[3D', vt)
|
||
cursor(0, 1, state)
|
||
push('\x1b[P', vt)
|
||
expect('moverect 0..1,2..80 -> 0..1,1..79\nerase 0..1,79..80')
|
||
cursor(0, 1, state)
|
||
push('\x1b[3P', vt)
|
||
expect('moverect 0..1,4..80 -> 0..1,1..77\nerase 0..1,77..80')
|
||
cursor(0, 1, state)
|
||
end)
|
||
|
||
itp('14state_encoding', function()
|
||
local vt = init()
|
||
vterm.vterm_set_utf8(vt, false)
|
||
local state = wantstate(vt, { g = true })
|
||
|
||
-- Default
|
||
reset(state, nil)
|
||
push('#', vt)
|
||
expect('putglyph 23 1 0,0')
|
||
|
||
-- Designate G0=UK
|
||
reset(state, nil)
|
||
push('\x1b(A', vt)
|
||
push('#', vt)
|
||
expect('putglyph a3 1 0,0')
|
||
|
||
-- Designate G0=DEC drawing
|
||
reset(state, nil)
|
||
push('\x1b(0', vt)
|
||
push('a', vt)
|
||
expect('putglyph 2592 1 0,0')
|
||
|
||
-- Designate G1 + LS1
|
||
reset(state, nil)
|
||
push('\x1b)0', vt)
|
||
push('a', vt)
|
||
expect('putglyph 61 1 0,0')
|
||
push('\x0e', vt)
|
||
push('a', vt)
|
||
expect('putglyph 2592 1 0,1')
|
||
-- LS0
|
||
push('\x0f', vt)
|
||
push('a', vt)
|
||
expect('putglyph 61 1 0,2')
|
||
|
||
-- Designate G2 + LS2
|
||
push('\x1b*0', vt)
|
||
push('a', vt)
|
||
expect('putglyph 61 1 0,3')
|
||
push('\x1bn', vt)
|
||
push('a', vt)
|
||
expect('putglyph 2592 1 0,4')
|
||
push('\x0f', vt)
|
||
push('a', vt)
|
||
expect('putglyph 61 1 0,5')
|
||
|
||
-- Designate G3 + LS3
|
||
push('\x1b+0', vt)
|
||
push('a', vt)
|
||
expect('putglyph 61 1 0,6')
|
||
push('\x1bo', vt)
|
||
push('a', vt)
|
||
expect('putglyph 2592 1 0,7')
|
||
push('\x0f', vt)
|
||
push('a', vt)
|
||
expect('putglyph 61 1 0,8')
|
||
|
||
-- SS2
|
||
push('a\x8eaa', vt)
|
||
expect('putglyph 61 1 0,9\nputglyph 2592 1 0,10\nputglyph 61 1 0,11')
|
||
|
||
-- SS3
|
||
push('a\x8faa', vt)
|
||
expect('putglyph 61 1 0,12\nputglyph 2592 1 0,13\nputglyph 61 1 0,14')
|
||
|
||
-- LS1R
|
||
reset(state, nil)
|
||
push('\x1b~', vt)
|
||
push('\xe1', vt)
|
||
expect('putglyph 61 1 0,0')
|
||
push('\x1b)0', vt)
|
||
push('\xe1', vt)
|
||
expect('putglyph 2592 1 0,1')
|
||
|
||
-- LS2R
|
||
reset(state, nil)
|
||
push('\x1b}', vt)
|
||
push('\xe1', vt)
|
||
expect('putglyph 61 1 0,0')
|
||
push('\x1b*0', vt)
|
||
push('\xe1', vt)
|
||
expect('putglyph 2592 1 0,1')
|
||
|
||
-- LS3R
|
||
reset(state, nil)
|
||
push('\x1b|', vt)
|
||
push('\xe1', vt)
|
||
expect('putglyph 61 1 0,0')
|
||
push('\x1b+0', vt)
|
||
push('\xe1', vt)
|
||
expect('putglyph 2592 1 0,1')
|
||
|
||
vterm.vterm_set_utf8(vt, true)
|
||
-- U+0108 == c4 88
|
||
reset(state, nil)
|
||
push('\x1b(B', vt)
|
||
push('AB\xc4\x88D', vt)
|
||
expect('putglyph 41 1 0,0\nputglyph 42 1 0,1\nputglyph 108 1 0,2\nputglyph 44 1 0,3')
|
||
end)
|
||
|
||
itp('15state_mode', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true, m = true, e = true })
|
||
|
||
-- Insert/Replace Mode
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('AC\x1b[DB', vt)
|
||
expect('putglyph 41 1 0,0\nputglyph 43 1 0,1\nputglyph 42 1 0,1')
|
||
push('\x1b[4h', vt)
|
||
push('\x1b[G', vt)
|
||
push('AC\x1b[DB', vt)
|
||
expect(
|
||
'moverect 0..1,0..79 -> 0..1,1..80\nerase 0..1,0..1\nputglyph 41 1 0,0\nmoverect 0..1,1..79 -> 0..1,2..80\nerase 0..1,1..2\nputglyph 43 1 0,1\nmoverect 0..1,1..79 -> 0..1,2..80\nerase 0..1,1..2\nputglyph 42 1 0,1'
|
||
)
|
||
|
||
-- Insert mode only happens once for UTF-8 combining
|
||
push('e', vt)
|
||
expect('moverect 0..1,2..79 -> 0..1,3..80\nerase 0..1,2..3\nputglyph 65 1 0,2')
|
||
push('\xCC\x81', vt)
|
||
expect('putglyph 65,301 1 0,2')
|
||
|
||
-- Newline/Linefeed mode
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[5G\n', vt)
|
||
cursor(1, 4, state)
|
||
push('\x1b[20h', vt)
|
||
push('\x1b[5G\n', vt)
|
||
cursor(2, 0, state)
|
||
|
||
-- DEC origin mode
|
||
reset(state, nil)
|
||
expect('erase 0..25,0..80')
|
||
cursor(0, 0, state)
|
||
push('\x1b[5;15r', vt)
|
||
push('\x1b[H', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[3;3H', vt)
|
||
cursor(2, 2, state)
|
||
push('\x1b[?6h', vt)
|
||
push('\x1b[H', vt)
|
||
cursor(4, 0, state)
|
||
push('\x1b[3;3H', vt)
|
||
cursor(6, 2, state)
|
||
|
||
-- DECRQM on DECOM
|
||
push('\x1b[?6h', vt)
|
||
push('\x1b[?6$p', vt)
|
||
expect_output('\x1b[?6;1$y')
|
||
push('\x1b[?6l', vt)
|
||
push('\x1b[?6$p', vt)
|
||
expect_output('\x1b[?6;2$y')
|
||
|
||
-- Origin mode with DECSLRM
|
||
push('\x1b[?6h', vt)
|
||
push('\x1b[?69h', vt)
|
||
push('\x1b[20;60s', vt)
|
||
push('\x1b[H', vt)
|
||
cursor(4, 19, state)
|
||
|
||
push('\x1b[?69l', vt)
|
||
|
||
-- Origin mode bounds cursor to scrolling region
|
||
push('\x1b[H', vt)
|
||
push('\x1b[10A', vt)
|
||
cursor(4, 0, state)
|
||
push('\x1b[20B', vt)
|
||
cursor(14, 0, state)
|
||
|
||
-- Origin mode without scroll region
|
||
push('\x1b[?6l', vt)
|
||
push('\x1b[r\x1b[?6h', vt)
|
||
cursor(0, 0, state)
|
||
end)
|
||
|
||
itp('16state_resize', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true })
|
||
|
||
-- Placement
|
||
reset(state, nil)
|
||
push('AB\x1b[79GCDE', vt)
|
||
expect(
|
||
'putglyph 41 1 0,0\nputglyph 42 1 0,1\nputglyph 43 1 0,78\nputglyph 44 1 0,79\nputglyph 45 1 1,0'
|
||
)
|
||
|
||
-- Resize
|
||
reset(state, nil)
|
||
resize(27, 85, vt)
|
||
push('AB\x1b[79GCDE', vt)
|
||
expect(
|
||
'putglyph 41 1 0,0\nputglyph 42 1 0,1\nputglyph 43 1 0,78\nputglyph 44 1 0,79\nputglyph 45 1 0,80'
|
||
)
|
||
cursor(0, 81, state)
|
||
|
||
-- Resize without reset
|
||
resize(28, 90, vt)
|
||
cursor(0, 81, state)
|
||
push('FGHI', vt)
|
||
expect('putglyph 46 1 0,81\nputglyph 47 1 0,82\nputglyph 48 1 0,83\nputglyph 49 1 0,84')
|
||
cursor(0, 85, state)
|
||
|
||
-- Resize shrink moves cursor
|
||
resize(25, 80, vt)
|
||
cursor(0, 79, state)
|
||
|
||
-- Resize grow doesn't cancel phantom
|
||
reset(state, nil)
|
||
push('\x1b[79GAB', vt)
|
||
expect('putglyph 41 1 0,78\nputglyph 42 1 0,79')
|
||
cursor(0, 79, state)
|
||
resize(30, 100, vt)
|
||
cursor(0, 80, state)
|
||
push('C', vt)
|
||
expect('putglyph 43 1 0,80')
|
||
cursor(0, 81, state)
|
||
end)
|
||
|
||
itp('17state_mouse', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { p = true })
|
||
|
||
-- DECRQM on with mouse off
|
||
push('\x1b[?1000$p', vt)
|
||
expect_output('\x1b[?1000;2$y')
|
||
push('\x1b[?1002$p', vt)
|
||
expect_output('\x1b[?1002;2$y')
|
||
push('\x1b[?1003$p', vt)
|
||
expect_output('\x1b[?1003;2$y')
|
||
|
||
-- Mouse in simple button report mode
|
||
reset(state, nil)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
push('\x1b[?1000h', vt)
|
||
expect('settermprop 8 1')
|
||
|
||
-- Press 1
|
||
mousemove(0, 0, vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[M\x20\x21\x21')
|
||
|
||
-- Release 1
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[M\x23\x21\x21')
|
||
|
||
-- Ctrl-Press 1
|
||
mousebtn('d', 1, vt, { C = true })
|
||
expect_output('\x1b[M\x30\x21\x21')
|
||
mousebtn('u', 1, vt, { C = true })
|
||
expect_output('\x1b[M\x33\x21\x21')
|
||
|
||
-- Button 2
|
||
mousebtn('d', 2, vt)
|
||
expect_output('\x1b[M\x21\x21\x21')
|
||
mousebtn('u', 2, vt)
|
||
expect_output('\x1b[M\x23\x21\x21')
|
||
|
||
-- Position
|
||
mousemove(10, 20, vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[M\x20\x35\x2b')
|
||
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[M\x23\x35\x2b')
|
||
mousemove(10, 21, vt)
|
||
-- no output
|
||
|
||
-- Wheel events
|
||
mousebtn('d', 4, vt)
|
||
expect_output('\x1b[M\x60\x36\x2b')
|
||
mousebtn('d', 4, vt)
|
||
expect_output('\x1b[M\x60\x36\x2b')
|
||
mousebtn('d', 5, vt)
|
||
expect_output('\x1b[M\x61\x36\x2b')
|
||
mousebtn('d', 6, vt)
|
||
expect_output('\x1b[M\x62\x36\x2b')
|
||
mousebtn('d', 7, vt)
|
||
expect_output('\x1b[M\x63\x36\x2b')
|
||
|
||
-- DECRQM on mouse button mode
|
||
push('\x1b[?1000$p', vt)
|
||
expect_output('\x1b[?1000;1$y')
|
||
push('\x1b[?1002$p', vt)
|
||
expect_output('\x1b[?1002;2$y')
|
||
push('\x1b[?1003$p', vt)
|
||
expect_output('\x1b[?1003;2$y')
|
||
|
||
-- Drag events
|
||
reset(state, nil)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
push('\x1b[?1002h', vt)
|
||
expect('settermprop 8 2')
|
||
|
||
mousemove(5, 5, vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[M\x20\x26\x26')
|
||
mousemove(5, 6, vt)
|
||
expect_output('\x1b[M\x40\x27\x26')
|
||
mousemove(6, 6, vt)
|
||
expect_output('\x1b[M\x40\x27\x27')
|
||
mousemove(6, 6, vt)
|
||
-- no output
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[M\x23\x27\x27')
|
||
mousemove(6, 7, vt)
|
||
-- no output
|
||
|
||
-- DECRQM on mouse drag mode
|
||
push('\x1b[?1000$p', vt)
|
||
expect_output('\x1b[?1000;2$y')
|
||
push('\x1b[?1002$p', vt)
|
||
expect_output('\x1b[?1002;1$y')
|
||
push('\x1b[?1003$p', vt)
|
||
expect_output('\x1b[?1003;2$y')
|
||
|
||
-- Non-drag motion events
|
||
push('\x1b[?1003h', vt)
|
||
expect('settermprop 8 3')
|
||
|
||
mousemove(6, 8, vt)
|
||
expect_output('\x1b[M\x43\x29\x27')
|
||
|
||
-- DECRQM on mouse motion mode
|
||
push('\x1b[?1000$p', vt)
|
||
expect_output('\x1b[?1000;2$y')
|
||
push('\x1b[?1002$p', vt)
|
||
expect_output('\x1b[?1002;2$y')
|
||
push('\x1b[?1003$p', vt)
|
||
expect_output('\x1b[?1003;1$y')
|
||
|
||
-- Bounds checking
|
||
mousemove(300, 300, vt)
|
||
expect_output('\x1b[M\x43\xff\xff')
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[M\x20\xff\xff')
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[M\x23\xff\xff')
|
||
|
||
-- DECRQM on standard encoding mode
|
||
push('\x1b[?1005$p', vt)
|
||
expect_output('\x1b[?1005;2$y')
|
||
push('\x1b[?1006$p', vt)
|
||
expect_output('\x1b[?1006;2$y')
|
||
push('\x1b[?1015$p', vt)
|
||
expect_output('\x1b[?1015;2$y')
|
||
|
||
-- UTF-8 extended encoding mode
|
||
-- 300 + 32 + 1 = 333 = U+014d = \xc5\x8d
|
||
push('\x1b[?1005h', vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[M\x20\xc5\x8d\xc5\x8d')
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[M\x23\xc5\x8d\xc5\x8d')
|
||
|
||
-- DECRQM on UTF-8 extended encoding mode
|
||
push('\x1b[?1005$p', vt)
|
||
expect_output('\x1b[?1005;1$y')
|
||
push('\x1b[?1006$p', vt)
|
||
expect_output('\x1b[?1006;2$y')
|
||
push('\x1b[?1015$p', vt)
|
||
expect_output('\x1b[?1015;2$y')
|
||
|
||
-- SGR extended encoding mode
|
||
push('\x1b[?1006h', vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[<0;301;301M')
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[<0;301;301m')
|
||
|
||
-- DECRQM on SGR extended encoding mode
|
||
push('\x1b[?1005$p', vt)
|
||
expect_output('\x1b[?1005;2$y')
|
||
push('\x1b[?1006$p', vt)
|
||
expect_output('\x1b[?1006;1$y')
|
||
push('\x1b[?1015$p', vt)
|
||
expect_output('\x1b[?1015;2$y')
|
||
|
||
-- rxvt extended encoding mode
|
||
push('\x1b[?1015h', vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[0;301;301M')
|
||
mousebtn('u', 1, vt)
|
||
expect_output('\x1b[3;301;301M')
|
||
|
||
-- DECRQM on rxvt extended encoding mode
|
||
push('\x1b[?1005$p', vt)
|
||
expect_output('\x1b[?1005;2$y')
|
||
push('\x1b[?1006$p', vt)
|
||
expect_output('\x1b[?1006;2$y')
|
||
push('\x1b[?1015$p', vt)
|
||
expect_output('\x1b[?1015;1$y')
|
||
|
||
-- Mouse disabled reports nothing
|
||
reset(state, nil)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
mousemove(0, 0, vt)
|
||
mousebtn('d', 1, vt)
|
||
mousebtn('u', 1, vt)
|
||
|
||
-- DECSM can set multiple modes at once
|
||
push('\x1b[?1002;1006h', vt)
|
||
expect('settermprop 8 2')
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[<0;1;1M')
|
||
end)
|
||
|
||
itp('18state_termprops', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { p = true })
|
||
|
||
reset(state, nil)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
|
||
-- Cursor visibility
|
||
push('\x1b[?25h', vt)
|
||
expect('settermprop 1 true')
|
||
push('\x1b[?25$p', vt)
|
||
expect_output('\x1b[?25;1$y')
|
||
push('\x1b[?25l', vt)
|
||
expect('settermprop 1 false')
|
||
push('\x1b[?25$p', vt)
|
||
expect_output('\x1b[?25;2$y')
|
||
|
||
-- Cursor blink
|
||
push('\x1b[?12h', vt)
|
||
expect('settermprop 2 true')
|
||
push('\x1b[?12$p', vt)
|
||
expect_output('\x1b[?12;1$y')
|
||
push('\x1b[?12l', vt)
|
||
expect('settermprop 2 false')
|
||
push('\x1b[?12$p', vt)
|
||
expect_output('\x1b[?12;2$y')
|
||
|
||
-- Cursor shape
|
||
push('\x1b[3 q', vt)
|
||
expect('settermprop 2 true\nsettermprop 7 2')
|
||
|
||
-- Title
|
||
push('\x1b]2;Here is my title\a', vt)
|
||
expect('settermprop 4 ["Here is my title"]')
|
||
|
||
-- Title split write
|
||
push('\x1b]2;Here is', vt)
|
||
expect('settermprop 4 ["Here is"')
|
||
push(' another title\a', vt)
|
||
expect('settermprop 4 " another title"]')
|
||
end)
|
||
|
||
itp('20state_wrapping', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true, m = true })
|
||
|
||
-- 79th Column
|
||
push('\x1b[75G', vt)
|
||
push(string.rep('A', 5), vt)
|
||
expect(
|
||
'putglyph 41 1 0,74\nputglyph 41 1 0,75\nputglyph 41 1 0,76\nputglyph 41 1 0,77\nputglyph 41 1 0,78'
|
||
)
|
||
cursor(0, 79, state)
|
||
|
||
-- 80th Column Phantom
|
||
push('A', vt)
|
||
expect('putglyph 41 1 0,79')
|
||
cursor(0, 79, state)
|
||
|
||
-- Line Wraparound
|
||
push('B', vt)
|
||
expect('putglyph 42 1 1,0')
|
||
cursor(1, 1, state)
|
||
|
||
-- Line Wraparound during combined write
|
||
push('\x1b[78G', vt)
|
||
push('BBBCC', vt)
|
||
expect(
|
||
'putglyph 42 1 1,77\nputglyph 42 1 1,78\nputglyph 42 1 1,79\nputglyph 43 1 2,0\nputglyph 43 1 2,1'
|
||
)
|
||
cursor(2, 2, state)
|
||
|
||
-- DEC Auto Wrap Mode
|
||
reset(state, nil)
|
||
push('\x1b[?7l', vt)
|
||
push('\x1b[75G', vt)
|
||
push(string.rep('D', 6), vt)
|
||
expect(
|
||
'putglyph 44 1 0,74\nputglyph 44 1 0,75\nputglyph 44 1 0,76\nputglyph 44 1 0,77\nputglyph 44 1 0,78\nputglyph 44 1 0,79'
|
||
)
|
||
cursor(0, 79, state)
|
||
push('D', vt)
|
||
expect('putglyph 44 1 0,79')
|
||
cursor(0, 79, state)
|
||
push('\x1b[?7h', vt)
|
||
|
||
-- 80th column causes linefeed on wraparound
|
||
push('\x1b[25;78HABC', vt)
|
||
expect('putglyph 41 1 24,77\nputglyph 42 1 24,78\nputglyph 43 1 24,79')
|
||
cursor(24, 79, state)
|
||
push('D', vt)
|
||
expect('moverect 1..25,0..80 -> 0..24,0..80\nputglyph 44 1 24,0')
|
||
|
||
-- 80th column phantom linefeed phantom cancelled by explicit cursor move
|
||
push('\x1b[25;78HABC', vt)
|
||
expect('putglyph 41 1 24,77\nputglyph 42 1 24,78\nputglyph 43 1 24,79')
|
||
cursor(24, 79, state)
|
||
push('\x1b[25;1HD', vt)
|
||
expect('putglyph 44 1 24,0')
|
||
end)
|
||
|
||
itp('21state_tabstops', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true })
|
||
|
||
-- Initial
|
||
reset(state, nil)
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,8')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,16')
|
||
cursor(0, 17, state)
|
||
|
||
-- HTS
|
||
push('\x1b[5G\x1bH', vt)
|
||
push('\x1b[G\tX', vt)
|
||
expect('putglyph 58 1 0,4')
|
||
cursor(0, 5, state)
|
||
|
||
-- TBC 0
|
||
push('\x1b[9G\x1b[g', vt)
|
||
push('\x1b[G\tX\tX', vt)
|
||
expect('putglyph 58 1 0,4\nputglyph 58 1 0,16')
|
||
cursor(0, 17, state)
|
||
|
||
-- TBC 3
|
||
push('\x1b[3g\x1b[50G\x1bH\x1b[G', vt)
|
||
cursor(0, 0, state)
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,49')
|
||
cursor(0, 50, state)
|
||
|
||
-- Tabstops after resize
|
||
reset(state, nil)
|
||
resize(30, 100, vt)
|
||
-- Should be 100/8 = 12 tabstops
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,8')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,16')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,24')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,32')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,40')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,48')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,56')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,64')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,72')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,80')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,88')
|
||
push('\tX', vt)
|
||
expect('putglyph 58 1 0,96')
|
||
cursor(0, 97, state)
|
||
end)
|
||
|
||
itp('22state_save', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { p = true })
|
||
|
||
reset(state, nil)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
|
||
-- Set up state
|
||
push('\x1b[2;2H', vt)
|
||
cursor(1, 1, state)
|
||
push('\x1b[1m', vt)
|
||
pen('bold', true, state)
|
||
|
||
-- Save
|
||
push('\x1b[?1048h', vt)
|
||
|
||
-- Change state
|
||
push('\x1b[5;5H', vt)
|
||
cursor(4, 4, state)
|
||
push('\x1b[4 q', vt)
|
||
expect('settermprop 2 false\nsettermprop 7 2')
|
||
push('\x1b[22;4m', vt)
|
||
pen('bold', false, state)
|
||
pen('underline', 1, state)
|
||
|
||
-- Restore
|
||
push('\x1b[?1048l', vt)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
cursor(1, 1, state)
|
||
pen('bold', true, state)
|
||
pen('underline', 0, state)
|
||
|
||
-- Save/restore using DECSC/DECRC
|
||
push('\x1b[2;2H\x1b7', vt)
|
||
cursor(1, 1, state)
|
||
|
||
push('\x1b[5;5H', vt)
|
||
cursor(4, 4, state)
|
||
push('\x1b8', vt)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
cursor(1, 1, state)
|
||
|
||
-- Save twice, restore twice happens on both edge transitions
|
||
push('\x1b[2;10H\x1b[?1048h\x1b[6;10H\x1b[?1048h', vt)
|
||
push('\x1b[H', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[?1048l', vt)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
cursor(5, 9, state)
|
||
push('\x1b[H', vt)
|
||
cursor(0, 0, state)
|
||
push('\x1b[?1048l', vt)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
cursor(5, 9, state)
|
||
end)
|
||
|
||
itp('25state_input', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
|
||
-- Unmodified ASCII
|
||
inchar(41, vt)
|
||
expect('output 29')
|
||
inchar(61, vt)
|
||
expect('output 3d')
|
||
|
||
-- Ctrl modifier on ASCII letters
|
||
inchar(41, vt, { C = true })
|
||
expect('output 1b,5b,34,31,3b,35,75')
|
||
inchar(61, vt, { C = true })
|
||
expect('output 1b,5b,36,31,3b,35,75')
|
||
|
||
-- Alt modifier on ASCII letters
|
||
inchar(41, vt, { A = true })
|
||
expect('output 1b,29')
|
||
inchar(61, vt, { A = true })
|
||
expect('output 1b,3d')
|
||
|
||
-- Ctrl-Alt modifier on ASCII letters
|
||
inchar(41, vt, { C = true, A = true })
|
||
expect('output 1b,5b,34,31,3b,37,75')
|
||
inchar(61, vt, { C = true, A = true })
|
||
expect('output 1b,5b,36,31,3b,37,75')
|
||
|
||
-- Special handling of Ctrl-I
|
||
inchar(49, vt)
|
||
expect('output 31')
|
||
inchar(69, vt)
|
||
expect('output 45')
|
||
inchar(49, vt, { C = true })
|
||
expect('output 1b,5b,34,39,3b,35,75')
|
||
inchar(69, vt, { C = true })
|
||
expect('output 1b,5b,36,39,3b,35,75')
|
||
inchar(49, vt, { A = true })
|
||
expect('output 1b,31')
|
||
inchar(69, vt, { A = true })
|
||
expect('output 1b,45')
|
||
inchar(49, vt, { A = true, C = true })
|
||
expect('output 1b,5b,34,39,3b,37,75')
|
||
inchar(69, vt, { A = true, C = true })
|
||
expect('output 1b,5b,36,39,3b,37,75')
|
||
|
||
-- Special handling of Space
|
||
inchar(20, vt)
|
||
expect('output 14')
|
||
inchar(20, vt, { S = true })
|
||
expect('output 14')
|
||
inchar(20, vt, { C = true })
|
||
expect('output 1b,5b,32,30,3b,35,75')
|
||
inchar(20, vt, { C = true, S = true })
|
||
expect('output 1b,5b,32,30,3b,35,75')
|
||
inchar(20, vt, { A = true })
|
||
expect('output 1b,14')
|
||
inchar(20, vt, { S = true, A = true })
|
||
expect('output 1b,14')
|
||
inchar(20, vt, { C = true, A = true })
|
||
expect('output 1b,5b,32,30,3b,37,75')
|
||
inchar(20, vt, { S = true, C = true, A = true })
|
||
expect('output 1b,5b,32,30,3b,37,75')
|
||
|
||
-- Cursor keys in reset (cursor) mode
|
||
inkey('up', vt)
|
||
expect_output('\x1b[A')
|
||
inkey('up', vt, { S = true })
|
||
expect_output('\x1b[1;2A')
|
||
inkey('up', vt, { C = true })
|
||
expect_output('\x1b[1;5A')
|
||
inkey('up', vt, { S = true, C = true })
|
||
expect_output('\x1b[1;6A')
|
||
inkey('up', vt, { A = true })
|
||
expect_output('\x1b[1;3A')
|
||
inkey('up', vt, { S = true, A = true })
|
||
expect_output('\x1b[1;4A')
|
||
inkey('up', vt, { C = true, A = true })
|
||
expect_output('\x1b[1;7A')
|
||
inkey('up', vt, { S = true, C = true, A = true })
|
||
expect_output('\x1b[1;8A')
|
||
|
||
-- Cursor keys in application mode
|
||
push('\x1b[?1h', vt)
|
||
-- Plain "Up" should be SS3 A now
|
||
inkey('up', vt)
|
||
expect_output('\x1bOA')
|
||
-- Modified keys should still use CSI
|
||
inkey('up', vt, { S = true })
|
||
expect_output('\x1b[1;2A')
|
||
inkey('up', vt, { C = true })
|
||
expect_output('\x1b[1;5A')
|
||
|
||
-- Shift-Tab should be different
|
||
inkey('tab', vt)
|
||
expect_output('\x09')
|
||
inkey('tab', vt, { S = true })
|
||
expect_output('\x1b[Z')
|
||
inkey('tab', vt, { C = true })
|
||
expect_output('\x1b[9;5u')
|
||
inkey('tab', vt, { A = true })
|
||
expect_output('\x1b\x09')
|
||
inkey('tab', vt, { C = true, A = true })
|
||
expect_output('\x1b[9;7u')
|
||
|
||
-- Enter in linefeed mode
|
||
inkey('enter', vt)
|
||
expect_output('\x0d')
|
||
|
||
-- Enter in newline mode
|
||
push('\x1b[20h', vt)
|
||
inkey('enter', vt)
|
||
expect_output('\x0d\x0a')
|
||
|
||
-- Unmodified F1 is SS3 P
|
||
inkey('f1', vt)
|
||
expect_output('\x1bOP')
|
||
|
||
-- Modified F1 is CSI P
|
||
inkey('f1', vt, { S = true })
|
||
expect_output('\x1b[1;2P')
|
||
inkey('f1', vt, { A = true })
|
||
expect_output('\x1b[1;3P')
|
||
inkey('f1', vt, { C = true })
|
||
expect_output('\x1b[1;5P')
|
||
|
||
-- Keypad in DECKPNM
|
||
inkey('kp0', vt)
|
||
expect_output('0')
|
||
|
||
-- Keypad in DECKPAM
|
||
push('\x1b=', vt)
|
||
inkey('kp0', vt)
|
||
expect_output('\x1bOp')
|
||
|
||
-- Bracketed paste mode off
|
||
vterm.vterm_keyboard_start_paste(vt)
|
||
vterm.vterm_keyboard_end_paste(vt)
|
||
|
||
-- Bracketed paste mode on
|
||
push('\x1b[?2004h', vt)
|
||
vterm.vterm_keyboard_start_paste(vt)
|
||
expect_output('\x1b[200~')
|
||
vterm.vterm_keyboard_end_paste(vt)
|
||
expect_output('\x1b[201~')
|
||
|
||
-- Focus reporting disabled
|
||
vterm.vterm_state_focus_in(state)
|
||
vterm.vterm_state_focus_out(state)
|
||
|
||
-- Focus reporting enabled
|
||
state = wantstate(vt, { p = true })
|
||
push('\x1b[?1004h', vt)
|
||
expect('settermprop 9 true')
|
||
vterm.vterm_state_focus_in(state)
|
||
expect_output('\x1b[I')
|
||
vterm.vterm_state_focus_out(state)
|
||
expect_output('\x1b[O')
|
||
end)
|
||
|
||
itp('26state_query', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
|
||
-- DA
|
||
reset(state, nil)
|
||
push('\x1b[c', vt)
|
||
expect_output('\x1b[?1;2c')
|
||
|
||
-- XTVERSION
|
||
reset(state, nil)
|
||
push('\x1b[>q', vt)
|
||
expect_output('\x1bP>|libvterm(0.3)\x1b\\')
|
||
|
||
-- DSR
|
||
reset(state, nil)
|
||
push('\x1b[5n', vt)
|
||
expect_output('\x1b[0n')
|
||
|
||
-- CPR
|
||
push('\x1b[6n', vt)
|
||
expect_output('\x1b[1;1R')
|
||
push('\x1b[10;10H\x1b[6n', vt)
|
||
expect_output('\x1b[10;10R')
|
||
|
||
-- DECCPR
|
||
push('\x1b[?6n', vt)
|
||
expect_output('\x1b[?10;10R')
|
||
|
||
-- DECRQSS on DECSCUSR
|
||
push('\x1b[3 q', vt)
|
||
push('\x1bP$q q\x1b\\', vt)
|
||
expect_output('\x1bP1$r3 q\x1b\\')
|
||
|
||
-- DECRQSS on SGR
|
||
push('\x1b[1;5;7m', vt)
|
||
push('\x1bP$qm\x1b\\', vt)
|
||
expect_output('\x1bP1$r1;5;7m\x1b\\')
|
||
|
||
-- DECRQSS on SGR ANSI colours
|
||
push('\x1b[0;31;42m', vt)
|
||
push('\x1bP$qm\x1b\\', vt)
|
||
expect_output('\x1bP1$r31;42m\x1b\\')
|
||
|
||
-- DECRQSS on SGR ANSI hi-bright colours
|
||
push('\x1b[0;93;104m', vt)
|
||
push('\x1bP$qm\x1b\\', vt)
|
||
expect_output('\x1bP1$r93;104m\x1b\\')
|
||
|
||
-- DECRQSS on SGR 256-palette colours
|
||
push('\x1b[0;38:5:56;48:5:78m', vt)
|
||
push('\x1bP$qm\x1b\\', vt)
|
||
expect_output('\x1bP1$r38:5:56;48:5:78m\x1b\\')
|
||
|
||
-- DECRQSS on SGR RGB8 colours
|
||
push('\x1b[0;38:2:24:68:112;48:2:13:57:101m', vt)
|
||
push('\x1bP$qm\x1b\\', vt)
|
||
expect_output('\x1bP1$r38:2:24:68:112;48:2:13:57:101m\x1b\\')
|
||
|
||
-- S8C1T on DSR
|
||
push('\x1b G', vt)
|
||
push('\x1b[5n', vt)
|
||
expect_output('\x9b0n')
|
||
push('\x1b F', vt)
|
||
end)
|
||
|
||
itp('27state_reset', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
|
||
reset(state, nil)
|
||
|
||
-- RIS homes cursor
|
||
push('\x1b[5;5H', vt)
|
||
cursor(4, 4, state)
|
||
state = wantstate(vt, { m = true })
|
||
push('\x1bc', vt)
|
||
cursor(0, 0, state)
|
||
wantstate(vt)
|
||
|
||
-- RIS cancels scrolling region
|
||
push('\x1b[5;10r', vt)
|
||
wantstate(vt, { s = true })
|
||
push('\x1bc\x1b[25H\n', vt)
|
||
expect('scrollrect 0..25,0..80 => +1,+0')
|
||
wantstate(vt)
|
||
|
||
-- RIS erases screen
|
||
push('ABCDE', vt)
|
||
state = wantstate(vt, { e = true })
|
||
push('\x1bc', vt)
|
||
expect('erase 0..25,0..80')
|
||
wantstate(vt)
|
||
|
||
-- RIS clears tabstops
|
||
push('\x1b[5G\x1bH\x1b[G\t', vt)
|
||
cursor(0, 4, state)
|
||
push('\x1bc\t', vt)
|
||
cursor(0, 8, state)
|
||
end)
|
||
|
||
itp('28state_dbl_wh', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true })
|
||
|
||
-- Single Width, Single Height
|
||
reset(state, nil)
|
||
push('\x1b#5', vt)
|
||
push('Hello', vt)
|
||
expect(
|
||
'putglyph 48 1 0,0\nputglyph 65 1 0,1\nputglyph 6c 1 0,2\nputglyph 6c 1 0,3\nputglyph 6f 1 0,4'
|
||
)
|
||
|
||
-- Double Width, Single Height
|
||
reset(state, nil)
|
||
push('\x1b#6', vt)
|
||
push('Hello', vt)
|
||
expect(
|
||
'putglyph 48 1 0,0 dwl\nputglyph 65 1 0,1 dwl\nputglyph 6c 1 0,2 dwl\nputglyph 6c 1 0,3 dwl\nputglyph 6f 1 0,4 dwl'
|
||
)
|
||
cursor(0, 5, state)
|
||
push('\x1b[40GAB', vt)
|
||
expect('putglyph 41 1 0,39 dwl\nputglyph 42 1 1,0')
|
||
cursor(1, 1, state)
|
||
|
||
-- Double Height
|
||
reset(state, nil)
|
||
push('\x1b#3', vt)
|
||
push('Hello', vt)
|
||
expect(
|
||
'putglyph 48 1 0,0 dwl dhl-top\nputglyph 65 1 0,1 dwl dhl-top\nputglyph 6c 1 0,2 dwl dhl-top\nputglyph 6c 1 0,3 dwl dhl-top\nputglyph 6f 1 0,4 dwl dhl-top'
|
||
)
|
||
cursor(0, 5, state)
|
||
push('\r\n\x1b#4', vt)
|
||
push('Hello', vt)
|
||
expect(
|
||
'putglyph 48 1 1,0 dwl dhl-bottom\nputglyph 65 1 1,1 dwl dhl-bottom\nputglyph 6c 1 1,2 dwl dhl-bottom\nputglyph 6c 1 1,3 dwl dhl-bottom\nputglyph 6f 1 1,4 dwl dhl-bottom'
|
||
)
|
||
cursor(1, 5, state)
|
||
|
||
-- Double Width scrolling
|
||
reset(state, nil)
|
||
push('\x1b[20H\x1b#6ABC', vt)
|
||
expect('putglyph 41 1 19,0 dwl\nputglyph 42 1 19,1 dwl\nputglyph 43 1 19,2 dwl')
|
||
push('\x1b[25H\n', vt)
|
||
push('\x1b[19;4HDE', vt)
|
||
expect('putglyph 44 1 18,3 dwl\nputglyph 45 1 18,4 dwl')
|
||
push('\x1b[H\x1bM', vt)
|
||
push('\x1b[20;6HFG', vt)
|
||
expect('putglyph 46 1 19,5 dwl\nputglyph 47 1 19,6 dwl')
|
||
end)
|
||
|
||
itp('29state_fallback', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { f = true })
|
||
reset(state, nil)
|
||
|
||
-- Unrecognised control
|
||
push('\x03', vt)
|
||
expect('control 03')
|
||
|
||
-- Unrecognised CSI
|
||
push('\x1b[?15;2z', vt)
|
||
expect('csi 7a L=3f 15,2')
|
||
|
||
-- Unrecognised OSC
|
||
push('\x1b]27;Something\x1b\\', vt)
|
||
expect('osc [27;Something]')
|
||
|
||
-- Unrecognised DCS
|
||
push('\x1bPz123\x1b\\', vt)
|
||
expect('dcs [z123]')
|
||
|
||
-- Unrecognised APC
|
||
push('\x1b_z123\x1b\\', vt)
|
||
expect('apc [z123]')
|
||
|
||
-- Unrecognised PM
|
||
push('\x1b^z123\x1b\\', vt)
|
||
expect('pm [z123]')
|
||
|
||
-- Unrecognised SOS
|
||
push('\x1bXz123\x1b\\', vt)
|
||
expect('sos [z123]')
|
||
end)
|
||
|
||
itp('30state_pen', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
|
||
-- Reset
|
||
push('\x1b[m', vt)
|
||
pen('bold', false, state)
|
||
pen('underline', 0, state)
|
||
pen('italic', false, state)
|
||
pen('blink', false, state)
|
||
pen('reverse', false, state)
|
||
pen('font', 0, state)
|
||
-- TODO(dundargoc): fix
|
||
-- ?pen foreground = rgb(240,240,240,is_default_fg)
|
||
-- ?pen background = rgb(0,0,0,is_default_bg)
|
||
|
||
-- Bold
|
||
push('\x1b[1m', vt)
|
||
pen('bold', true, state)
|
||
push('\x1b[22m', vt)
|
||
pen('bold', false, state)
|
||
push('\x1b[1m\x1b[m', vt)
|
||
pen('bold', false, state)
|
||
|
||
-- Underline
|
||
push('\x1b[4m', vt)
|
||
pen('underline', 1, state)
|
||
push('\x1b[21m', vt)
|
||
pen('underline', 2, state)
|
||
push('\x1b[24m', vt)
|
||
pen('underline', 0, state)
|
||
push('\x1b[4m\x1b[4:0m', vt)
|
||
pen('underline', 0, state)
|
||
push('\x1b[4:1m', vt)
|
||
pen('underline', 1, state)
|
||
push('\x1b[4:2m', vt)
|
||
pen('underline', 2, state)
|
||
push('\x1b[4:3m', vt)
|
||
pen('underline', 3, state)
|
||
push('\x1b[4m\x1b[m', vt)
|
||
pen('underline', 0, state)
|
||
|
||
-- Italic
|
||
push('\x1b[3m', vt)
|
||
pen('italic', true, state)
|
||
push('\x1b[23m', vt)
|
||
pen('italic', false, state)
|
||
push('\x1b[3m\x1b[m', vt)
|
||
pen('italic', false, state)
|
||
|
||
-- Blink
|
||
push('\x1b[5m', vt)
|
||
pen('blink', true, state)
|
||
push('\x1b[25m', vt)
|
||
pen('blink', false, state)
|
||
push('\x1b[5m\x1b[m', vt)
|
||
pen('blink', false, state)
|
||
|
||
-- Reverse
|
||
push('\x1b[7m', vt)
|
||
pen('reverse', true, state)
|
||
push('\x1b[27m', vt)
|
||
pen('reverse', false, state)
|
||
push('\x1b[7m\x1b[m', vt)
|
||
pen('reverse', false, state)
|
||
|
||
-- Font Selection
|
||
push('\x1b[11m', vt)
|
||
pen('font', 1, state)
|
||
push('\x1b[19m', vt)
|
||
pen('font', 9, state)
|
||
push('\x1b[10m', vt)
|
||
pen('font', 0, state)
|
||
push('\x1b[11m\x1b[m', vt)
|
||
pen('font', 0, state)
|
||
|
||
-- TODO(dundargoc): fix
|
||
-- Foreground
|
||
-- push "\x1b[31m"
|
||
-- ?pen foreground = idx(1)
|
||
-- push "\x1b[32m"
|
||
-- ?pen foreground = idx(2)
|
||
-- push "\x1b[34m"
|
||
-- ?pen foreground = idx(4)
|
||
-- push "\x1b[91m"
|
||
-- ?pen foreground = idx(9)
|
||
-- push "\x1b[38:2:10:20:30m"
|
||
-- ?pen foreground = rgb(10,20,30)
|
||
-- push "\x1b[38:5:1m"
|
||
-- ?pen foreground = idx(1)
|
||
-- push "\x1b[39m"
|
||
-- ?pen foreground = rgb(240,240,240,is_default_fg)
|
||
--
|
||
-- Background
|
||
-- push "\x1b[41m"
|
||
-- ?pen background = idx(1)
|
||
-- push "\x1b[42m"
|
||
-- ?pen background = idx(2)
|
||
-- push "\x1b[44m"
|
||
-- ?pen background = idx(4)
|
||
-- push "\x1b[101m"
|
||
-- ?pen background = idx(9)
|
||
-- push "\x1b[48:2:10:20:30m"
|
||
-- ?pen background = rgb(10,20,30)
|
||
-- push "\x1b[48:5:1m"
|
||
-- ?pen background = idx(1)
|
||
-- push "\x1b[49m"
|
||
-- ?pen background = rgb(0,0,0,is_default_bg)
|
||
--
|
||
-- Bold+ANSI colour == highbright
|
||
-- push "\x1b[m\x1b[1;37m"
|
||
-- ?pen bold = on
|
||
-- ?pen foreground = idx(15)
|
||
-- push "\x1b[m\x1b[37;1m"
|
||
-- ?pen bold = on
|
||
-- ?pen foreground = idx(15)
|
||
--
|
||
-- Super/Subscript
|
||
-- push "\x1b[73m"
|
||
-- ?pen small = on
|
||
-- ?pen baseline = raise
|
||
-- push "\x1b[74m"
|
||
-- ?pen small = on
|
||
-- ?pen baseline = lower
|
||
-- push "\x1b[75m"
|
||
-- ?pen small = off
|
||
-- ?pen baseline = normal
|
||
--
|
||
-- DECSTR resets pen attributes
|
||
-- push "\x1b[1;4m"
|
||
-- ?pen bold = on
|
||
-- ?pen underline = 1
|
||
-- push "\x1b[!p"
|
||
-- ?pen bold = off
|
||
-- ?pen underline = 0
|
||
end)
|
||
|
||
itp('31state_rep', function()
|
||
local vt = init()
|
||
local state = wantstate(vt, { g = true })
|
||
|
||
-- REP no argument
|
||
reset(state, nil)
|
||
push('a\x1b[b', vt)
|
||
expect('putglyph 61 1 0,0\nputglyph 61 1 0,1')
|
||
|
||
-- REP zero (zero should be interpreted as one)
|
||
reset(state, nil)
|
||
push('a\x1b[0b', vt)
|
||
expect('putglyph 61 1 0,0\nputglyph 61 1 0,1')
|
||
|
||
-- REP lowercase a times two
|
||
reset(state, nil)
|
||
push('a\x1b[2b', vt)
|
||
expect('putglyph 61 1 0,0\nputglyph 61 1 0,1\nputglyph 61 1 0,2')
|
||
|
||
-- REP with UTF-8 1 char
|
||
-- U+00E9 = C3 A9 name: LATIN SMALL LETTER E WITH ACUTE
|
||
reset(state, nil)
|
||
push('\xC3\xA9\x1b[b', vt)
|
||
expect('putglyph e9 1 0,0\nputglyph e9 1 0,1')
|
||
|
||
-- REP with UTF-8 wide char
|
||
-- U+00E9 = C3 A9 name: LATIN SMALL LETTER E WITH ACUTE
|
||
reset(state, nil)
|
||
push('\xEF\xBC\x90\x1b[b', vt)
|
||
expect('putglyph ff10 2 0,0\nputglyph ff10 2 0,2')
|
||
|
||
-- REP with UTF-8 combining character
|
||
reset(state, nil)
|
||
push('e\xCC\x81\x1b[b', vt)
|
||
expect('putglyph 65,301 1 0,0\nputglyph 65,301 1 0,1')
|
||
|
||
-- REP till end of line
|
||
reset(state, nil)
|
||
push('a\x1b[1000bb', vt)
|
||
expect(
|
||
'putglyph 61 1 0,0\nputglyph 61 1 0,1\nputglyph 61 1 0,2\nputglyph 61 1 0,3\nputglyph 61 1 0,4\nputglyph 61 1 0,5\nputglyph 61 1 0,6\nputglyph 61 1 0,7\nputglyph 61 1 0,8\nputglyph 61 1 0,9\nputglyph 61 1 0,10\nputglyph 61 1 0,11\nputglyph 61 1 0,12\nputglyph 61 1 0,13\nputglyph 61 1 0,14\nputglyph 61 1 0,15\nputglyph 61 1 0,16\nputglyph 61 1 0,17\nputglyph 61 1 0,18\nputglyph 61 1 0,19\nputglyph 61 1 0,20\nputglyph 61 1 0,21\nputglyph 61 1 0,22\nputglyph 61 1 0,23\nputglyph 61 1 0,24\nputglyph 61 1 0,25\nputglyph 61 1 0,26\nputglyph 61 1 0,27\nputglyph 61 1 0,28\nputglyph 61 1 0,29\nputglyph 61 1 0,30\nputglyph 61 1 0,31\nputglyph 61 1 0,32\nputglyph 61 1 0,33\nputglyph 61 1 0,34\nputglyph 61 1 0,35\nputglyph 61 1 0,36\nputglyph 61 1 0,37\nputglyph 61 1 0,38\nputglyph 61 1 0,39\nputglyph 61 1 0,40\nputglyph 61 1 0,41\nputglyph 61 1 0,42\nputglyph 61 1 0,43\nputglyph 61 1 0,44\nputglyph 61 1 0,45\nputglyph 61 1 0,46\nputglyph 61 1 0,47\nputglyph 61 1 0,48\nputglyph 61 1 0,49\nputglyph 61 1 0,50\nputglyph 61 1 0,51\nputglyph 61 1 0,52\nputglyph 61 1 0,53\nputglyph 61 1 0,54\nputglyph 61 1 0,55\nputglyph 61 1 0,56\nputglyph 61 1 0,57\nputglyph 61 1 0,58\nputglyph 61 1 0,59\nputglyph 61 1 0,60\nputglyph 61 1 0,61\nputglyph 61 1 0,62\nputglyph 61 1 0,63\nputglyph 61 1 0,64\nputglyph 61 1 0,65\nputglyph 61 1 0,66\nputglyph 61 1 0,67\nputglyph 61 1 0,68\nputglyph 61 1 0,69\nputglyph 61 1 0,70\nputglyph 61 1 0,71\nputglyph 61 1 0,72\nputglyph 61 1 0,73\nputglyph 61 1 0,74\nputglyph 61 1 0,75\nputglyph 61 1 0,76\nputglyph 61 1 0,77\nputglyph 61 1 0,78\nputglyph 61 1 0,79\nputglyph 62 1 1,0'
|
||
)
|
||
end)
|
||
|
||
itp('32state_flow', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
|
||
-- Many of these test cases inspired by
|
||
-- https://blueprints.launchpad.net/libvterm/+spec/reflow-cases
|
||
|
||
-- Spillover text marks continuation on second line
|
||
reset(state, nil)
|
||
push(string.rep('A', 100), vt)
|
||
push('\r\n', vt)
|
||
lineinfo(0, {}, state)
|
||
lineinfo(1, { cont = true }, state)
|
||
|
||
-- CRLF in column 80 does not mark continuation
|
||
reset(state, nil)
|
||
push(string.rep('B', 80), vt)
|
||
push('\r\n', vt)
|
||
push(string.rep('B', 20), vt)
|
||
push('\r\n', vt)
|
||
lineinfo(0, {}, state)
|
||
lineinfo(1, {}, state)
|
||
|
||
-- EL cancels continuation of following line
|
||
reset(state, nil)
|
||
push(string.rep('D', 100), vt)
|
||
lineinfo(1, { cont = true }, state)
|
||
push('\x1bM\x1b[79G\x1b[K', vt)
|
||
lineinfo(1, {}, state)
|
||
end)
|
||
|
||
itp('40state_selection', function()
|
||
local vt = init()
|
||
wantstate(vt)
|
||
|
||
-- Set clipboard; final chunk len 4
|
||
push('\x1b]52;c;SGVsbG8s\x1b\\', vt)
|
||
expect('selection-set mask=0001 [Hello,]')
|
||
|
||
-- Set clipboard; final chunk len 3
|
||
push('\x1b]52;c;SGVsbG8sIHc=\x1b\\', vt)
|
||
expect('selection-set mask=0001 [Hello, w]')
|
||
|
||
-- Set clipboard; final chunk len 2
|
||
push('\x1b]52;c;SGVsbG8sIHdvcmxkCg==\x1b\\', vt)
|
||
expect('selection-set mask=0001 [Hello, world\n]')
|
||
|
||
-- Set clipboard; split between chunks
|
||
push('\x1b]52;c;SGVs', vt)
|
||
expect('selection-set mask=0001 [Hel')
|
||
push('bG8s\x1b\\', vt)
|
||
expect('selection-set mask=0001 lo,]')
|
||
|
||
-- Set clipboard; split within chunk
|
||
push('\x1b]52;c;SGVsbG', vt)
|
||
expect('selection-set mask=0001 [Hel')
|
||
push('8s\x1b\\', vt)
|
||
expect('selection-set mask=0001 lo,]')
|
||
|
||
-- Set clipboard; empty first chunk
|
||
push('\x1b]52;c;', vt)
|
||
push('SGVsbG8s\x1b\\', vt)
|
||
expect('selection-set mask=0001 [Hello,]')
|
||
|
||
-- Set clipboard; empty final chunk
|
||
push('\x1b]52;c;SGVsbG8s', vt)
|
||
expect('selection-set mask=0001 [Hello,')
|
||
push('\x1b\\', vt)
|
||
expect('selection-set mask=0001 ]')
|
||
|
||
-- Set clipboard; longer than buffer
|
||
push('\x1b]52;c;' .. string.rep('LS0t', 10) .. '\x1b\\', vt)
|
||
expect('selection-set mask=0001 [---------------\nselection-set mask=0001 ---------------]')
|
||
|
||
-- Clear clipboard
|
||
push('\x1b]52;c;\x1b\\', vt)
|
||
expect('selection-set mask=0001 []')
|
||
|
||
-- Set invalid data clears and ignores
|
||
push('\x1b]52;c;SGVs*SGVsbG8s\x1b\\', vt)
|
||
expect('selection-set mask=0001 []')
|
||
|
||
-- Query clipboard
|
||
push('\x1b]52;c;?\x1b\\', vt)
|
||
expect('selection-query mask=0001')
|
||
|
||
-- TODO(dundargoc): fix
|
||
-- Send clipboard; final chunk len 4
|
||
-- SELECTION 1 ["Hello,"]
|
||
-- output "\x1b]52;c;"
|
||
-- output "SGVsbG8s"
|
||
-- output "\x1b\\"
|
||
--
|
||
-- Send clipboard; final chunk len 3
|
||
-- SELECTION 1 ["Hello, w"]
|
||
-- output "\x1b]52;c;"
|
||
-- output "SGVsbG8s"
|
||
-- output "IHc=\x1b\\"
|
||
--
|
||
-- Send clipboard; final chunk len 2
|
||
-- SELECTION 1 ["Hello, world\n"]
|
||
-- output "\x1b]52;c;"
|
||
-- output "SGVsbG8sIHdvcmxk"
|
||
-- output "Cg==\x1b\\"
|
||
--
|
||
-- Send clipboard; split between chunks
|
||
-- SELECTION 1 ["Hel"
|
||
-- output "\x1b]52;c;"
|
||
-- output "SGVs"
|
||
-- SELECTION 1 "lo,"]
|
||
-- output "bG8s"
|
||
-- output "\x1b\\"
|
||
--
|
||
-- Send clipboard; split within chunk
|
||
-- SELECTION 1 ["Hello"
|
||
-- output "\x1b]52;c;"
|
||
-- output "SGVs"
|
||
-- SELECTION 1 ","]
|
||
-- output "bG8s"
|
||
-- output "\x1b\\"
|
||
end)
|
||
|
||
itp('60screen_ascii', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt, { a = true, c = true })
|
||
|
||
-- Get
|
||
reset(nil, screen)
|
||
push('ABC', vt)
|
||
expect('movecursor 0,3')
|
||
screen_chars(0, 0, 1, 3, 'ABC', screen)
|
||
screen_chars(0, 0, 1, 80, 'ABC', screen)
|
||
screen_text(0, 0, 1, 3, '41,42,43', screen)
|
||
screen_text(0, 0, 1, 80, '41,42,43', screen)
|
||
screen_cell(0, 0, '{41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(0, 1, '{42} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(0, 2, '{43} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_row(0, 'ABC', screen)
|
||
screen_eol(0, 0, 0, screen)
|
||
screen_eol(0, 2, 0, screen)
|
||
screen_eol(0, 3, 1, screen)
|
||
push('\x1b[H', vt)
|
||
expect('movecursor 0,0')
|
||
screen_row(0, 'ABC', screen)
|
||
screen_text(0, 0, 1, 80, '41,42,43', screen)
|
||
push('E', vt)
|
||
expect('movecursor 0,1')
|
||
screen_row(0, 'EBC', screen)
|
||
screen_text(0, 0, 1, 80, '45,42,43', screen)
|
||
|
||
screen = wantscreen(vt, { a = true })
|
||
|
||
-- Erase
|
||
reset(nil, screen)
|
||
push('ABCDE\x1b[H\x1b[K', vt)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row(0, '', screen)
|
||
screen_text(0, 0, 1, 80, '', screen)
|
||
|
||
-- Copycell
|
||
reset(nil, screen)
|
||
push('ABC\x1b[H\x1b[@', vt)
|
||
push('1', vt)
|
||
screen_row(0, '1ABC', screen)
|
||
|
||
reset(nil, screen)
|
||
push('ABC\x1b[H\x1b[P', vt)
|
||
screen_chars(0, 0, 1, 1, 'B', screen)
|
||
screen_chars(0, 1, 1, 2, 'C', screen)
|
||
screen_chars(0, 0, 1, 80, 'BC', screen)
|
||
|
||
-- Space padding
|
||
reset(nil, screen)
|
||
push('Hello\x1b[CWorld', vt)
|
||
screen_row(0, 'Hello World', screen)
|
||
screen_text(0, 0, 1, 80, '48,65,6c,6c,6f,20,57,6f,72,6c,64', screen)
|
||
|
||
-- Linefeed padding
|
||
reset(nil, screen)
|
||
push('Hello\r\nWorld', vt)
|
||
screen_chars(0, 0, 2, 80, 'Hello\nWorld', screen)
|
||
screen_text(0, 0, 2, 80, '48,65,6c,6c,6f,0a,57,6f,72,6c,64', screen)
|
||
|
||
-- Altscreen
|
||
reset(nil, screen)
|
||
push('P', vt)
|
||
screen_row(0, 'P', screen)
|
||
-- TODO(dundargoc): fix
|
||
-- push('\x1b[?1049h', vt)
|
||
-- screen_row(0, '', screen)
|
||
-- push('\x1b[2K\x1b[HA', vt)
|
||
-- screen_row(0, 'A', screen)
|
||
-- push('\x1b[?1049l', vt)
|
||
-- screen_row(0, 'P', screen)
|
||
end)
|
||
|
||
itp('61screen_unicode', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
-- Single width UTF-8
|
||
-- U+00C1 = C3 81 name: LATIN CAPITAL LETTER A WITH ACUTE
|
||
-- U+00E9 = C3 A9 name: LATIN SMALL LETTER E WITH ACUTE
|
||
reset(nil, screen)
|
||
push('\xC3\x81\xC3\xA9', vt)
|
||
screen_row(0, 'Áé', screen)
|
||
screen_text(0, 0, 1, 80, 'c3,81,c3,a9', screen)
|
||
screen_cell(0, 0, '{c1} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Wide char
|
||
-- U+FF10 = EF BC 90 name: FULLWIDTH DIGIT ZERO
|
||
reset(nil, screen)
|
||
push('0123\x1b[H', vt)
|
||
push('\xEF\xBC\x90', vt)
|
||
screen_row(0, '023', screen)
|
||
screen_text(0, 0, 1, 80, 'ef,bc,90,32,33', screen)
|
||
screen_cell(0, 0, '{ff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Combining char
|
||
-- U+0301 = CC 81 name: COMBINING ACUTE
|
||
reset(nil, screen)
|
||
push('0123\x1b[H', vt)
|
||
push('e\xCC\x81', vt)
|
||
screen_row(0, 'é123', screen)
|
||
screen_text(0, 0, 1, 80, '65,cc,81,31,32,33', screen)
|
||
screen_cell(0, 0, '{65,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- 10 combining accents should not crash
|
||
reset(nil, screen)
|
||
push('e\xCC\x81\xCC\x82\xCC\x83\xCC\x84\xCC\x85\xCC\x86\xCC\x87\xCC\x88\xCC\x89\xCC\x8A', vt)
|
||
screen_cell(
|
||
0,
|
||
0,
|
||
'{65,301,302,303,304,305} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
|
||
screen
|
||
)
|
||
|
||
-- 40 combining accents in two split writes of 20 should not crash
|
||
reset(nil, screen)
|
||
push(
|
||
'e\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81',
|
||
vt
|
||
)
|
||
push(
|
||
'\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81',
|
||
vt
|
||
)
|
||
screen_cell(
|
||
0,
|
||
0,
|
||
'{65,301,301,301,301,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
|
||
screen
|
||
)
|
||
|
||
-- Outputing CJK doublewidth in 80th column should wraparound to next line and not crash"
|
||
reset(nil, screen)
|
||
push('\x1b[80G\xEF\xBC\x90', vt)
|
||
screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(1, 0, '{ff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
end)
|
||
|
||
pending('62screen_damage', function() end)
|
||
|
||
itp('63screen_resize', function()
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
local screen = wantscreen(vt)
|
||
|
||
-- Resize wider preserves cells
|
||
reset(state, screen)
|
||
resize(25, 80, vt)
|
||
push('AB\r\nCD', vt)
|
||
screen_chars(0, 0, 1, 80, 'AB', screen)
|
||
screen_chars(1, 0, 2, 80, 'CD', screen)
|
||
resize(25, 100, vt)
|
||
screen_chars(0, 0, 1, 100, 'AB', screen)
|
||
screen_chars(1, 0, 2, 100, 'CD', screen)
|
||
|
||
-- Resize wider allows print in new area
|
||
reset(state, screen)
|
||
resize(25, 80, vt)
|
||
push('AB\x1b[79GCD', vt)
|
||
screen_chars(0, 0, 1, 2, 'AB', screen)
|
||
screen_chars(0, 78, 1, 80, 'CD', screen)
|
||
resize(25, 100, vt)
|
||
screen_chars(0, 0, 1, 2, 'AB', screen)
|
||
screen_chars(0, 78, 1, 80, 'CD', screen)
|
||
push('E', vt)
|
||
screen_chars(0, 78, 1, 81, 'CDE', screen)
|
||
|
||
-- Resize shorter with blanks just truncates
|
||
reset(state, screen)
|
||
resize(25, 80, vt)
|
||
push('Top\x1b[10HLine 10', vt)
|
||
screen_row(0, 'Top', screen)
|
||
screen_row(9, 'Line 10', screen)
|
||
cursor(9, 7, state)
|
||
resize(20, 80, vt)
|
||
screen_row(0, 'Top', screen)
|
||
screen_row(9, 'Line 10', screen)
|
||
cursor(9, 7, state)
|
||
|
||
-- Resize shorter with content must scroll
|
||
reset(state, screen)
|
||
resize(25, 80, vt)
|
||
push('Top\x1b[25HLine 25\x1b[15H', vt)
|
||
screen_row(0, 'Top', screen)
|
||
screen_row(24, 'Line 25', screen)
|
||
cursor(14, 0, state)
|
||
screen = wantscreen(vt, { b = true })
|
||
resize(20, 80, vt)
|
||
expect(
|
||
'sb_pushline 80 = 54 6F 70\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 ='
|
||
)
|
||
-- TODO(dundargoc): fix or remove
|
||
-- screen_row( 0 , "",screen)
|
||
screen_row(19, 'Line 25', screen)
|
||
cursor(9, 0, state)
|
||
|
||
-- Resize shorter does not lose line with cursor
|
||
-- See also https://github.com/neovim/libvterm/commit/1b745d29d45623aa8d22a7b9288c7b0e331c7088
|
||
reset(state, screen)
|
||
wantscreen(vt)
|
||
resize(25, 80, vt)
|
||
screen = wantscreen(vt, { b = true })
|
||
push('\x1b[24HLine 24\r\nLine 25\r\n', vt)
|
||
expect('sb_pushline 80 =')
|
||
screen_row(23, 'Line 25', screen)
|
||
cursor(24, 0, state)
|
||
resize(24, 80, vt)
|
||
expect('sb_pushline 80 =')
|
||
screen_row(22, 'Line 25', screen)
|
||
cursor(23, 0, state)
|
||
|
||
-- Resize shorter does not send the cursor to a negative row
|
||
-- See also https://github.com/vim/vim/pull/6141
|
||
reset(state, screen)
|
||
wantscreen(vt)
|
||
resize(25, 80, vt)
|
||
screen = wantscreen(vt, { b = true })
|
||
push('\x1b[24HLine 24\r\nLine 25\x1b[H', vt)
|
||
cursor(0, 0, state)
|
||
resize(20, 80, vt)
|
||
expect(
|
||
'sb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 ='
|
||
)
|
||
cursor(0, 0, state)
|
||
|
||
-- Resize taller attempts to pop scrollback
|
||
reset(state, screen)
|
||
screen = wantscreen(vt)
|
||
resize(25, 80, vt)
|
||
push('Line 1\x1b[25HBottom\x1b[15H', vt)
|
||
screen_row(0, 'Line 1', screen)
|
||
screen_row(24, 'Bottom', screen)
|
||
cursor(14, 0, state)
|
||
screen = wantscreen(vt, { b = true })
|
||
resize(30, 80, vt)
|
||
expect('sb_popline 80\nsb_popline 80\nsb_popline 80\nsb_popline 80\nsb_popline 80')
|
||
screen_row(0, 'ABCDE', screen)
|
||
screen_row(5, 'Line 1', screen)
|
||
screen_row(29, 'Bottom', screen)
|
||
cursor(19, 0, state)
|
||
screen = wantscreen(vt)
|
||
|
||
-- Resize can operate on altscreen
|
||
reset(state, screen)
|
||
screen = wantscreen(vt, { a = true })
|
||
resize(25, 80, vt)
|
||
push('Main screen\x1b[?1049h\x1b[HAlt screen', vt)
|
||
resize(30, 80, vt)
|
||
screen_row(0, 'Alt screen', screen)
|
||
push('\x1b[?1049l', vt)
|
||
screen_row(0, 'Main screen', screen)
|
||
end)
|
||
|
||
itp('64screen_pen', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
reset(nil, screen)
|
||
|
||
-- Plain
|
||
push('A', vt)
|
||
screen_cell(0, 0, '{41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Bold
|
||
push('\x1b[1mB', vt)
|
||
screen_cell(0, 1, '{42} width=1 attrs={B} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Italic
|
||
push('\x1b[3mC', vt)
|
||
screen_cell(0, 2, '{43} width=1 attrs={BI} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Underline
|
||
push('\x1b[4mD', vt)
|
||
screen_cell(0, 3, '{44} width=1 attrs={BU1I} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Reset
|
||
push('\x1b[mE', vt)
|
||
screen_cell(0, 4, '{45} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Font
|
||
push('\x1b[11mF\x1b[m', vt)
|
||
screen_cell(0, 5, '{46} width=1 attrs={F1} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Foreground
|
||
push('\x1b[31mG\x1b[m', vt)
|
||
screen_cell(0, 6, '{47} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Background
|
||
push('\x1b[42mH\x1b[m', vt)
|
||
screen_cell(0, 7, '{48} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,224,0)', screen)
|
||
|
||
-- Super/subscript
|
||
push('x\x1b[74m0\x1b[73m2\x1b[m', vt)
|
||
screen_cell(0, 8, '{78} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(0, 9, '{30} width=1 attrs={S_} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(0, 10, '{32} width=1 attrs={S^} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- EL sets only colours to end of line, not other attrs
|
||
push('\x1b[H\x1b[7;33;44m\x1b[K', vt)
|
||
screen_cell(0, 0, '{} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
|
||
screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
|
||
|
||
-- DECSCNM xors reverse for entire screen
|
||
push('R\x1b[?5h', vt)
|
||
screen_cell(0, 0, '{52} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
|
||
screen_cell(1, 0, '{} width=1 attrs={R} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
push('\x1b[?5$p', vt)
|
||
expect_output('\x1b[?5;1$y')
|
||
push('\x1b[?5l', vt)
|
||
screen_cell(0, 0, '{52} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
|
||
screen_cell(1, 0, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
-- TODO(dundargoc): fix
|
||
-- push('\x1b[?5$p')
|
||
-- expect_output('\x1b[?5;2$y')
|
||
|
||
-- Set default colours
|
||
reset(nil, screen)
|
||
push('ABC\x1b[31mDEF\x1b[m', vt)
|
||
screen_cell(0, 0, '{41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(0, 3, '{44} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)', screen)
|
||
-- TODO(dundargoc): fix
|
||
-- SETDEFAULTCOL rgb(252,253,254)
|
||
-- ?screen_cell 0,0 = {41} width=1 attrs={} fg=rgb(252,253,254) bg=rgb(0,0,0)
|
||
-- ?screen_cell 0,3 = {44} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)
|
||
-- SETDEFAULTCOL rgb(250,250,250) rgb(10,20,30)
|
||
-- ?screen_cell 0,0 = {41} width=1 attrs={} fg=rgb(250,250,250) bg=rgb(10,20,30)
|
||
-- ?screen_cell 0,3 = {44} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(10,20,30)
|
||
end)
|
||
|
||
itp('65screen_protect', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
-- Selective erase
|
||
reset(nil, screen)
|
||
push('A\x1b[1"qB\x1b["qC', vt)
|
||
screen_row(0, 'ABC', screen)
|
||
push('\x1b[G\x1b[?J', vt)
|
||
screen_row(0, ' B', screen)
|
||
|
||
-- Non-selective erase
|
||
reset(nil, screen)
|
||
push('A\x1b[1"qB\x1b["qC', vt)
|
||
screen_row(0, 'ABC', screen)
|
||
-- TODO(dundargoc): fix
|
||
-- push('\x1b[G\x1b[J', vt)
|
||
-- screen_row(0, '', screen)
|
||
end)
|
||
|
||
itp('66screen_extent', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
-- Bold extent
|
||
reset(nil, screen)
|
||
push('AB\x1b[1mCD\x1b[mE', vt)
|
||
screen_attrs_extent(0, 0, '0,0-1,1', screen)
|
||
screen_attrs_extent(0, 1, '0,0-1,1', screen)
|
||
screen_attrs_extent(0, 2, '0,2-1,3', screen)
|
||
screen_attrs_extent(0, 3, '0,2-1,3', screen)
|
||
screen_attrs_extent(0, 4, '0,4-1,79', screen)
|
||
end)
|
||
|
||
itp('67screen_dbl_wh', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
reset(nil, screen)
|
||
|
||
-- Single Width, Single Height
|
||
reset(nil, screen)
|
||
push('\x1b#5', vt)
|
||
push('abcde', vt)
|
||
screen_cell(0, 0, '{61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Double Width, Single Height
|
||
reset(nil, screen)
|
||
push('\x1b#6', vt)
|
||
push('abcde', vt)
|
||
screen_cell(0, 0, '{61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- Double Height
|
||
reset(nil, screen)
|
||
push('\x1b#3', vt)
|
||
push('abcde', vt)
|
||
push('\r\n\x1b#4', vt)
|
||
push('abcde', vt)
|
||
screen_cell(0, 0, '{61} width=1 attrs={} dwl dhl-top fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(
|
||
1,
|
||
0,
|
||
'{61} width=1 attrs={} dwl dhl-bottom fg=rgb(240,240,240) bg=rgb(0,0,0)',
|
||
screen
|
||
)
|
||
|
||
-- Late change
|
||
reset(nil, screen)
|
||
push('abcde', vt)
|
||
screen_cell(0, 0, '{61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
push('\x1b#6', vt)
|
||
screen_cell(0, 0, '{61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
|
||
-- DWL doesn't spill over on scroll
|
||
reset(nil, screen)
|
||
push('\x1b[25H\x1b#6Final\r\n', vt)
|
||
screen_cell(23, 0, '{46} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
screen_cell(24, 0, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
|
||
end)
|
||
|
||
itp('68screen_termprops', function()
|
||
local vt = init()
|
||
local screen = wantscreen(vt, { p = true })
|
||
|
||
reset(nil, screen)
|
||
expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
|
||
|
||
-- Cursor visibility
|
||
push('\x1b[?25h', vt)
|
||
expect('settermprop 1 true')
|
||
push('\x1b[?25l', vt)
|
||
expect('settermprop 1 false')
|
||
|
||
-- Title
|
||
push('\x1b]2;Here is my title\a', vt)
|
||
expect('settermprop 4 ["Here is my title"]')
|
||
end)
|
||
|
||
itp('69screen_pushline', function()
|
||
local vt = init()
|
||
-- Run these tests on a much smaller default screen, so debug output is nowhere near as noisy
|
||
resize(5, 10, vt)
|
||
local state = wantstate(vt)
|
||
local screen = wantscreen(vt, { r = true })
|
||
reset(state, screen)
|
||
|
||
-- Resize wider reflows wide lines
|
||
reset(state, screen)
|
||
push(string.rep('A', 12), vt)
|
||
screen_row(0, 'AAAAAAAAAA', screen, vt.cols)
|
||
screen_row(1, 'AA', screen, vt.cols)
|
||
lineinfo(1, { cont = true }, state)
|
||
cursor(1, 2, state)
|
||
resize(5, 15, vt)
|
||
screen_row(0, 'AAAAAAAAAAAA', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row(1, '', screen, vt.cols)
|
||
lineinfo(1, {}, state)
|
||
cursor(0, 12, state)
|
||
resize(5, 20, vt)
|
||
screen_row(0, 'AAAAAAAAAAAA', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 1 ,'',screen, vt.cols)
|
||
lineinfo(1, {}, state)
|
||
cursor(0, 12, state)
|
||
|
||
-- Resize narrower can create continuation lines
|
||
reset(state, screen)
|
||
resize(5, 10, vt)
|
||
push('ABCDEFGHI', vt)
|
||
screen_row(0, 'ABCDEFGHI', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 1 , "",screen, vt.cols)
|
||
lineinfo(1, {}, state)
|
||
cursor(0, 9, state)
|
||
resize(5, 8, vt)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 0 , "ABCDEFGH",screen,vt.cols)
|
||
screen_row(1, 'I', screen, vt.cols)
|
||
lineinfo(1, { cont = true }, state)
|
||
cursor(1, 1, state)
|
||
resize(5, 6, vt)
|
||
screen_row(0, 'ABCDEF', screen, vt.cols)
|
||
screen_row(1, 'GHI', screen, vt.cols)
|
||
lineinfo(1, { cont = true }, state)
|
||
cursor(1, 3, state)
|
||
|
||
-- Shell wrapped prompt behaviour
|
||
reset(state, screen)
|
||
resize(5, 10, vt)
|
||
push('PROMPT GOES HERE\r\n> \r\n\r\nPROMPT GOES HERE\r\n> ', vt)
|
||
screen_row(0, '> ', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 1 , "",screen,vt.cols)
|
||
screen_row(2, 'PROMPT GOE', screen, vt.cols)
|
||
screen_row(3, 'S HERE', screen, vt.cols)
|
||
lineinfo(3, { cont = true }, state)
|
||
screen_row(4, '> ', screen, vt.cols)
|
||
cursor(4, 2, state)
|
||
resize(5, 11, vt)
|
||
screen_row(0, '> ', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 1 , "",screen,vt.cols)
|
||
screen_row(2, 'PROMPT GOES', screen, vt.cols)
|
||
screen_row(3, ' HERE', screen, vt.cols)
|
||
lineinfo(3, { cont = true }, state)
|
||
screen_row(4, '> ', screen, vt.cols)
|
||
cursor(4, 2, state)
|
||
resize(5, 12, vt)
|
||
screen_row(0, '> ', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 1 , "",screen,vt.cols)
|
||
screen_row(2, 'PROMPT GOES ', screen, vt.cols)
|
||
screen_row(3, 'HERE', screen, vt.cols)
|
||
lineinfo(3, { cont = true }, state)
|
||
screen_row(4, '> ', screen, vt.cols)
|
||
cursor(4, 2, state)
|
||
resize(5, 16, vt)
|
||
screen_row(0, '> ', screen, vt.cols)
|
||
-- TODO(dundargoc): fix
|
||
-- screen_row( 1 , "",screen,vt.cols)
|
||
-- screen_row( 2 , "PROMPT GOES HERE",screen,vt.cols)
|
||
lineinfo(3, {}, state)
|
||
screen_row(3, '> ', screen, vt.cols)
|
||
cursor(3, 2, state)
|
||
|
||
-- Cursor goes missing
|
||
-- For more context: https://github.com/neovim/neovim/pull/21124
|
||
reset(state, screen)
|
||
resize(5, 5, vt)
|
||
resize(3, 1, vt)
|
||
push('\x1b[2;1Habc\r\n\x1b[H', vt)
|
||
resize(1, 1, vt)
|
||
cursor(0, 0, state)
|
||
end)
|
||
|
||
pending('90vttest_01-movement-1', function() end)
|
||
pending('90vttest_01-movement-2', function() end)
|
||
|
||
itp('90vttest_01-movement-3', function()
|
||
-- Test of cursor-control characters inside ESC sequences
|
||
local vt = init()
|
||
local state = wantstate(vt)
|
||
local screen = wantscreen(vt)
|
||
|
||
reset(state, screen)
|
||
|
||
push('A B C D E F G H I', vt)
|
||
push('\x0d\x0a', vt)
|
||
push('A\x1b[2\bCB\x1b[2\bCC\x1b[2\bCD\x1b[2\bCE\x1b[2\bCF\x1b[2\bCG\x1b[2\bCH\x1b[2\bCI', vt)
|
||
push('\x0d\x0a', vt)
|
||
push(
|
||
'A \x1b[\x0d2CB\x1b[\x0d4CC\x1b[\x0d6CD\x1b[\x0d8CE\x1b[\x0d10CF\x1b[\x0d12CG\x1b[\x0d14CH\x1b[\x0d16CI',
|
||
vt
|
||
)
|
||
push('\x0d\x0a', vt)
|
||
push(
|
||
'A \x1b[1\x0bAB \x1b[1\x0bAC \x1b[1\x0bAD \x1b[1\x0bAE \x1b[1\x0bAF \x1b[1\x0bAG \x1b[1\x0bAH \x1b[1\x0bAI \x1b[1\x0bA',
|
||
vt
|
||
)
|
||
|
||
-- Output
|
||
|
||
for i = 0, 2 do
|
||
screen_row(i, 'A B C D E F G H I', screen)
|
||
end
|
||
screen_row(3, 'A B C D E F G H I ', screen)
|
||
|
||
cursor(3, 18, state)
|
||
end)
|
||
|
||
itp('90vttest_01-movement-4', function()
|
||
-- Test of leading zeroes in ESC sequences
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
reset(nil, screen)
|
||
|
||
push('\x1b[00000000004;000000001HT', vt)
|
||
push('\x1b[00000000004;000000002Hh', vt)
|
||
push('\x1b[00000000004;000000003Hi', vt)
|
||
push('\x1b[00000000004;000000004Hs', vt)
|
||
push('\x1b[00000000004;000000005H ', vt)
|
||
push('\x1b[00000000004;000000006Hi', vt)
|
||
push('\x1b[00000000004;000000007Hs', vt)
|
||
push('\x1b[00000000004;000000008H ', vt)
|
||
push('\x1b[00000000004;000000009Ha', vt)
|
||
push('\x1b[00000000004;0000000010H ', vt)
|
||
push('\x1b[00000000004;0000000011Hc', vt)
|
||
push('\x1b[00000000004;0000000012Ho', vt)
|
||
push('\x1b[00000000004;0000000013Hr', vt)
|
||
push('\x1b[00000000004;0000000014Hr', vt)
|
||
push('\x1b[00000000004;0000000015He', vt)
|
||
push('\x1b[00000000004;0000000016Hc', vt)
|
||
push('\x1b[00000000004;0000000017Ht', vt)
|
||
push('\x1b[00000000004;0000000018H ', vt)
|
||
push('\x1b[00000000004;0000000019Hs', vt)
|
||
push('\x1b[00000000004;0000000020He', vt)
|
||
push('\x1b[00000000004;0000000021Hn', vt)
|
||
push('\x1b[00000000004;0000000022Ht', vt)
|
||
push('\x1b[00000000004;0000000023He', vt)
|
||
push('\x1b[00000000004;0000000024Hn', vt)
|
||
push('\x1b[00000000004;0000000025Hc', vt)
|
||
push('\x1b[00000000004;0000000026He', vt)
|
||
|
||
-- Output
|
||
|
||
screen_row(3, 'This is a correct sentence', screen)
|
||
end)
|
||
|
||
pending('90vttest_02-screen-1', function() end)
|
||
pending('90vttest_02-screen-2', function() end)
|
||
|
||
itp('90vttest_02-screen-3', function()
|
||
-- Origin mode
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
reset(nil, screen)
|
||
|
||
push('\x1b[?6h', vt)
|
||
push('\x1b[23;24r', vt)
|
||
push('\n', vt)
|
||
push('Bottom', vt)
|
||
push('\x1b[1;1H', vt)
|
||
push('Above', vt)
|
||
|
||
-- Output
|
||
screen_row(22, 'Above', screen)
|
||
screen_row(23, 'Bottom', screen)
|
||
end)
|
||
|
||
itp('90vttest_02-screen-4', function()
|
||
-- Origin mode (2)
|
||
local vt = init()
|
||
local screen = wantscreen(vt)
|
||
|
||
reset(nil, screen)
|
||
|
||
push('\x1b[?6l', vt)
|
||
push('\x1b[23;24r', vt)
|
||
push('\x1b[24;1H', vt)
|
||
push('Bottom', vt)
|
||
push('\x1b[1;1H', vt)
|
||
push('Top', vt)
|
||
|
||
-- Output
|
||
screen_row(23, 'Bottom', screen)
|
||
screen_row(0, 'Top', screen)
|
||
end)
|
||
|
||
itp('Mouse reporting should not break by idempotent DECSM 1002', function()
|
||
-- Regression test for https://bugs.launchpad.net/libvterm/+bug/1640917
|
||
-- Related: https://github.com/neovim/neovim/issues/5583
|
||
local vt = init()
|
||
wantstate(vt, {})
|
||
|
||
push('\x1b[?1002h', vt)
|
||
mousemove(0, 0, vt)
|
||
mousebtn('d', 1, vt)
|
||
expect_output('\x1b[M\x20\x21\x21')
|
||
mousemove(1, 0, vt)
|
||
expect_output('\x1b[M\x40\x21\x22')
|
||
push('\x1b[?1002h', vt)
|
||
mousemove(2, 0, vt)
|
||
expect_output('\x1b[M\x40\x21\x23')
|
||
end)
|
||
end)
|