neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

vterm_spec.lua (109402B)


      1 local t = require('test.unit.testutil')
      2 local itp = t.gen_itp(it)
      3 local bit = require('bit')
      4 
      5 --- @class vterm
      6 --- @field ENC_UTF8 integer
      7 --- @field VTERM_ATTR_BLINK integer
      8 --- @field VTERM_ATTR_BOLD integer
      9 --- @field VTERM_ATTR_FONT integer
     10 --- @field VTERM_ATTR_ITALIC integer
     11 --- @field VTERM_ATTR_REVERSE integer
     12 --- @field VTERM_ATTR_UNDERLINE integer
     13 --- @field VTERM_BASELINE_RAISE integer
     14 --- @field VTERM_KEY_ENTER integer
     15 --- @field VTERM_KEY_FUNCTION_0 integer
     16 --- @field VTERM_KEY_KP_0 integer
     17 --- @field VTERM_KEY_NONE integer
     18 --- @field VTERM_KEY_TAB integer
     19 --- @field VTERM_KEY_UP integer
     20 --- @field VTERM_KEY_BACKSPACE integer
     21 --- @field VTERM_KEY_ESCAPE integer
     22 --- @field VTERM_KEY_DEL integer
     23 --- @field VTERM_MOD_ALT integer
     24 --- @field VTERM_MOD_CTRL integer
     25 --- @field VTERM_MOD_SHIFT integer
     26 --- @field parser_apc function
     27 --- @field parser_csi function
     28 --- @field parser_dcs function
     29 --- @field parser_osc function
     30 --- @field parser_pm function
     31 --- @field parser_sos function
     32 --- @field parser_text function
     33 --- @field print_color function
     34 --- @field schar_get fun(any, any):integer
     35 --- @field screen_sb_clear function
     36 --- @field screen_sb_popline function
     37 --- @field screen_sb_pushline function
     38 --- @field selection_query function
     39 --- @field selection_set function
     40 --- @field state_erase function
     41 --- @field state_movecursor function
     42 --- @field state_moverect function
     43 --- @field state_pos function
     44 --- @field state_putglyph function
     45 --- @field state_sb_clear function
     46 --- @field state_scrollrect function
     47 --- @field state_setpenattr function
     48 --- @field state_settermprop function
     49 --- @field term_output function
     50 --- @field utf_ptr2char fun(any):integer
     51 --- @field utf_ptr2len fun(any):integer
     52 --- @field vterm_input_write function
     53 --- @field vterm_keyboard_end_paste function
     54 --- @field vterm_keyboard_key function
     55 --- @field vterm_keyboard_start_paste function
     56 --- @field vterm_keyboard_unichar function
     57 --- @field vterm_lookup_encoding fun(any, any):any
     58 --- @field vterm_mouse_button function
     59 --- @field vterm_mouse_move function
     60 --- @field vterm_new fun(any, any):any
     61 --- @field vterm_obtain_screen fun(any):any
     62 --- @field vterm_obtain_state fun(any): any
     63 --- @field vterm_output_set_callback function
     64 --- @field vterm_parser_set_callbacks fun(any, any, any):any
     65 --- @field vterm_screen_convert_color_to_rgb function
     66 --- @field vterm_screen_enable_altscreen function
     67 --- @field vterm_screen_enable_reflow function
     68 --- @field vterm_screen_get_attrs_extent function
     69 --- @field vterm_screen_get_cell function
     70 --- @field vterm_screen_get_text fun(any, any, any, any):any
     71 --- @field vterm_screen_is_eol fun(any, any):any
     72 --- @field vterm_screen_reset function
     73 --- @field vterm_screen_set_callbacks function
     74 --- @field vterm_set_size function
     75 --- @field vterm_set_utf8 fun(any, any, any):any
     76 --- @field vterm_state_focus_in function
     77 --- @field vterm_state_focus_out function
     78 --- @field vterm_state_get_cursorpos fun(any, any)
     79 --- @field vterm_state_get_lineinfo fun(any, any):any
     80 --- @field vterm_state_get_penattr function
     81 --- @field vterm_state_reset function
     82 --- @field vterm_state_set_bold_highbright function
     83 --- @field vterm_state_set_callbacks function
     84 --- @field vterm_state_set_selection_callbacks function
     85 --- @field vterm_state_set_unrecognised_fallbacks function
     86 local vterm = t.cimport(
     87  './src/nvim/grid.h',
     88  './src/nvim/mbyte.h',
     89  './src/nvim/vterm/encoding.h',
     90  './src/nvim/vterm/keyboard.h',
     91  './src/nvim/vterm/mouse.h',
     92  './src/nvim/vterm/parser.h',
     93  './src/nvim/vterm/pen.h',
     94  './src/nvim/vterm/screen.h',
     95  './src/nvim/vterm/state.h',
     96  './src/nvim/vterm/vterm.h',
     97  './src/nvim/vterm/vterm_internal_defs.h',
     98  './test/unit/fixtures/vterm_test.h'
     99 )
    100 
    101 --- @return string
    102 local function read_rm()
    103  local f = assert(io.open(t.paths.vterm_test_file, 'rb'))
    104  local text = f:read('*a')
    105  f:close()
    106  vim.fs.rm(t.paths.vterm_test_file, { force = true })
    107  return text
    108 end
    109 
    110 local function append(str)
    111  local f = assert(io.open(t.paths.vterm_test_file, 'a'))
    112  f:write(str)
    113  f:close()
    114  return 1
    115 end
    116 
    117 local function parser_control(control)
    118  return append(string.format('control %02x\n', control))
    119 end
    120 
    121 local function parser_escape(bytes)
    122  return append(string.format('escape %s\n', t.ffi.string(bytes)))
    123 end
    124 
    125 local function wantparser(vt)
    126  assert(vt)
    127 
    128  local parser_cbs = t.ffi.new('VTermParserCallbacks')
    129  parser_cbs['text'] = vterm.parser_text
    130  parser_cbs['control'] = parser_control
    131  parser_cbs['escape'] = parser_escape
    132  parser_cbs['csi'] = vterm.parser_csi
    133  parser_cbs['osc'] = vterm.parser_osc
    134  parser_cbs['dcs'] = vterm.parser_dcs
    135  parser_cbs['apc'] = vterm.parser_apc
    136  parser_cbs['pm'] = vterm.parser_pm
    137  parser_cbs['sos'] = vterm.parser_sos
    138 
    139  vterm.vterm_parser_set_callbacks(vt, parser_cbs, nil)
    140 end
    141 
    142 --- @return any
    143 local function init()
    144  local vt = vterm.vterm_new(25, 80)
    145  vterm.vterm_output_set_callback(vt, vterm.term_output, nil)
    146  vterm.vterm_set_utf8(vt, true)
    147  return vt
    148 end
    149 
    150 local function state_setlineinfo()
    151  return 1
    152 end
    153 
    154 --- @return any
    155 local function wantstate(vt, opts)
    156  opts = opts or {}
    157  assert(vt)
    158  local state = vterm.vterm_obtain_state(vt)
    159 
    160  local state_cbs = t.ffi.new('VTermStateCallbacks')
    161  state_cbs['putglyph'] = vterm.state_putglyph
    162  state_cbs['movecursor'] = vterm.state_movecursor
    163  state_cbs['scrollrect'] = vterm.state_scrollrect
    164  state_cbs['moverect'] = vterm.state_moverect
    165  state_cbs['erase'] = vterm.state_erase
    166  state_cbs['setpenattr'] = vterm.state_setpenattr
    167  state_cbs['settermprop'] = vterm.state_settermprop
    168  state_cbs['setlineinfo'] = state_setlineinfo
    169  state_cbs['sb_clear'] = vterm.state_sb_clear
    170 
    171  local selection_cbs = t.ffi.new('VTermSelectionCallbacks')
    172  selection_cbs['set'] = vterm.selection_set
    173  selection_cbs['query'] = vterm.selection_query
    174 
    175  vterm.vterm_state_set_callbacks(state, state_cbs, nil)
    176 
    177  -- In some tests we want to check the behaviour of overflowing the buffer, so make it nicely small
    178  vterm.vterm_state_set_selection_callbacks(state, selection_cbs, nil, nil, 16)
    179  vterm.vterm_state_set_bold_highbright(state, 1)
    180  vterm.vterm_state_reset(state, 1)
    181 
    182  local fallbacks = t.ffi.new('VTermStateFallbacks')
    183  fallbacks['control'] = parser_control
    184  fallbacks['csi'] = vterm.parser_csi
    185  fallbacks['osc'] = vterm.parser_osc
    186  fallbacks['dcs'] = vterm.parser_dcs
    187  fallbacks['apc'] = vterm.parser_apc
    188  fallbacks['pm'] = vterm.parser_pm
    189  fallbacks['sos'] = vterm.parser_sos
    190 
    191  vterm.want_state_scrollback = opts.b or false
    192  vterm.want_state_erase = opts.e or false
    193  vterm.vterm_state_set_unrecognised_fallbacks(state, opts.f and fallbacks or nil, nil)
    194  vterm.want_state_putglyph = opts.g or false
    195  vterm.want_state_moverect = opts.m or false
    196  vterm.want_state_settermprop = opts.p or false
    197  vterm.want_state_scrollrect = opts.s or false
    198 
    199  return state
    200 end
    201 
    202 --- @return any
    203 local function wantscreen(vt, opts)
    204  opts = opts or {}
    205  local screen = vterm.vterm_obtain_screen(vt)
    206  local screen_cbs = t.ffi.new('VTermScreenCallbacks')
    207 
    208  -- TODO(dundargoc): fix
    209  -- screen_cbs['damage']      = vterm.screen_damage
    210  screen_cbs['moverect'] = vterm.state_moverect
    211  screen_cbs['movecursor'] = vterm.state_movecursor
    212  screen_cbs['settermprop'] = vterm.state_settermprop
    213  screen_cbs['sb_pushline'] = vterm.screen_sb_pushline
    214  screen_cbs['sb_popline'] = vterm.screen_sb_popline
    215  screen_cbs['sb_clear'] = vterm.screen_sb_clear
    216 
    217  vterm.vterm_screen_set_callbacks(screen, screen_cbs, nil)
    218 
    219  if opts.a then
    220    vterm.vterm_screen_enable_altscreen(screen, 1)
    221  end
    222  vterm.want_screen_scrollback = opts.b or false
    223  vterm.want_state_movecursor = opts.c or false
    224  -- TODO(dundargoc): fix
    225  -- vterm.want_screen_damage = opts.d or opts.D or false
    226  -- vterm.want_screen_cells = opts.D or false
    227  vterm.want_state_moverect = opts.m or false
    228  vterm.want_state_settermprop = opts.p or false
    229  if opts.r then
    230    vterm.vterm_screen_enable_reflow(screen, true)
    231  end
    232 
    233  return screen
    234 end
    235 
    236 local function reset(state, screen)
    237  if state then
    238    vterm.vterm_state_reset(state, 1)
    239    vterm.vterm_state_get_cursorpos(state, vterm.state_pos)
    240  end
    241  if screen then
    242    vterm.vterm_screen_reset(screen, 1)
    243  end
    244 end
    245 
    246 local function push(input, vt)
    247  vterm.vterm_input_write(vt, input, string.len(input))
    248 end
    249 
    250 local function expect(expected)
    251  local actual = read_rm()
    252  t.eq(expected .. '\n', actual)
    253 end
    254 
    255 local function expect_output(expected_preformat)
    256  local actual = read_rm()
    257  local expected = 'output '
    258 
    259  for c in string.gmatch(expected_preformat, '.') do
    260    if expected ~= 'output ' then
    261      expected = expected .. ','
    262    end
    263    expected = string.format('%s%x', expected, string.byte(c))
    264  end
    265 
    266  t.eq(expected .. '\n', actual)
    267 end
    268 
    269 local function cursor(row, col, state)
    270  local pos = t.ffi.new('VTermPos') --- @type {row: integer, col: integer}
    271  vterm.vterm_state_get_cursorpos(state, pos)
    272  t.eq(row, pos.row)
    273  t.eq(col, pos.col)
    274 end
    275 
    276 local function lineinfo(row, expected, state)
    277  local info = vterm.vterm_state_get_lineinfo(state, row)
    278  local dwl = info.doublewidth == 1
    279  local dhl = info.doubleheight == 1
    280  local cont = info.continuation == 1
    281 
    282  t.eq(dwl, expected.dwl or false)
    283  t.eq(dhl, expected.dhl or false)
    284  t.eq(cont, expected.cont or false)
    285 end
    286 
    287 local function pen(attribute, expected, state)
    288  local is_bool =
    289    { bold = true, italic = true, blink = true, reverse = true, dim = true, overline = true }
    290  local vterm_attribute = {
    291    bold = vterm.VTERM_ATTR_BOLD,
    292    underline = vterm.VTERM_ATTR_UNDERLINE,
    293    italic = vterm.VTERM_ATTR_ITALIC,
    294    blink = vterm.VTERM_ATTR_BLINK,
    295    reverse = vterm.VTERM_ATTR_REVERSE,
    296    font = vterm.VTERM_ATTR_FONT,
    297    dim = vterm.VTERM_ATTR_DIM,
    298    overline = vterm.VTERM_ATTR_OVERLINE,
    299  }
    300 
    301  local val = t.ffi.new('VTermValue') --- @type {boolean: integer}
    302  vterm.vterm_state_get_penattr(state, vterm_attribute[attribute], val)
    303  local actual = val.boolean --- @type integer|boolean
    304  if is_bool[attribute] then
    305    actual = val.boolean == 1
    306  end
    307  t.eq(expected, actual)
    308 end
    309 
    310 local function resize(rows, cols, vt)
    311  vterm.vterm_set_size(vt, rows, cols)
    312 end
    313 
    314 local function screen_chars(start_row, start_col, end_row, end_col, expected, screen)
    315  local rect = t.ffi.new('VTermRect')
    316  rect['start_row'] = start_row
    317  rect['start_col'] = start_col
    318  rect['end_row'] = end_row
    319  rect['end_col'] = end_col
    320 
    321  local len = vterm.vterm_screen_get_text(screen, nil, 0, rect)
    322 
    323  local text = t.ffi.new('unsigned char[?]', len)
    324  vterm.vterm_screen_get_text(screen, text, len, rect)
    325 
    326  local actual = t.ffi.string(text, len)
    327  t.eq(expected, actual)
    328 end
    329 
    330 local function screen_text(start_row, start_col, end_row, end_col, expected, screen)
    331  local rect = t.ffi.new('VTermRect')
    332  rect['start_row'] = start_row
    333  rect['start_col'] = start_col
    334  rect['end_row'] = end_row
    335  rect['end_col'] = end_col
    336 
    337  local len = vterm.vterm_screen_get_text(screen, nil, 0, rect)
    338 
    339  local text = t.ffi.new('unsigned char[?]', len)
    340  vterm.vterm_screen_get_text(screen, text, len, rect)
    341 
    342  local actual = ''
    343  for i = 0, tonumber(len) - 1 do
    344    actual = string.format('%s%02x,', actual, text[i])
    345  end
    346  actual = actual:sub(1, -2)
    347 
    348  t.eq(expected, actual)
    349 end
    350 
    351 --- @param row integer
    352 local function screen_row(row, expected, screen, end_col)
    353  local rect = t.ffi.new('VTermRect')
    354  rect['start_row'] = row
    355  rect['start_col'] = 0
    356  rect['end_row'] = row + 1
    357  rect['end_col'] = end_col or 80
    358 
    359  local len = vterm.vterm_screen_get_text(screen, nil, 0, rect)
    360 
    361  local text = t.ffi.new('unsigned char[?]', len)
    362  vterm.vterm_screen_get_text(screen, text, len, rect)
    363 
    364  t.eq(expected, t.ffi.string(text, len))
    365 end
    366 
    367 local function screen_cell(row, col, expected, screen)
    368  local pos = t.ffi.new('VTermPos')
    369  pos['row'] = row
    370  pos['col'] = col
    371 
    372  local cell = t.ffi.new('VTermScreenCell') ---@type any
    373  vterm.vterm_screen_get_cell(screen, pos, cell)
    374 
    375  local buf = t.ffi.new('unsigned char[32]')
    376  vterm.schar_get(buf, cell.schar)
    377 
    378  local actual = '{'
    379  local i = 0
    380  while buf[i] > 0 do
    381    local char = vterm.utf_ptr2char(buf + i)
    382    local charlen = vterm.utf_ptr2len(buf + i)
    383    if i > 0 then
    384      actual = actual .. ','
    385    end
    386    local invalid = char >= 128 and charlen == 1
    387    actual = string.format('%s%s%02x', actual, invalid and '?' or '', char)
    388    i = i + charlen
    389  end
    390  actual = string.format('%s} width=%d attrs={', actual, cell['width'])
    391  actual = actual .. (cell['attrs'].bold ~= 0 and 'B' or '')
    392  actual = actual .. (cell['attrs'].dim ~= 0 and 'D' or '')
    393  actual = actual
    394    .. (cell['attrs'].underline ~= 0 and string.format('U%d', cell['attrs'].underline) or '')
    395  actual = actual .. (cell['attrs'].italic ~= 0 and 'I' or '')
    396  actual = actual .. (cell['attrs'].blink ~= 0 and 'K' or '')
    397  actual = actual .. (cell['attrs'].reverse ~= 0 and 'R' or '')
    398  actual = actual .. (cell['attrs'].overline ~= 0 and 'O' or '')
    399  actual = actual .. (cell['attrs'].font ~= 0 and string.format('F%d', cell['attrs'].font) or '')
    400  actual = actual .. (cell['attrs'].small ~= 0 and 'S' or '')
    401  if cell['attrs'].baseline ~= 0 then
    402    actual = actual .. (cell['attrs'].baseline == vterm.VTERM_BASELINE_RAISE and '^' or '_')
    403  end
    404  actual = actual .. '} '
    405 
    406  actual = actual .. (cell['attrs'].dwl ~= 0 and 'dwl ' or '')
    407  if cell['attrs'].dhl ~= 0 then
    408    actual = actual .. string.format('dhl-%s ', cell['attrs'].dhl == 2 and 'bottom' or 'top')
    409  end
    410 
    411  actual = string.format('%sfg=', actual)
    412  vterm.vterm_screen_convert_color_to_rgb(screen, cell['fg'])
    413  vterm.print_color(cell['fg'])
    414 
    415  actual = actual .. read_rm()
    416  actual = actual .. ' bg='
    417 
    418  vterm.vterm_screen_convert_color_to_rgb(screen, cell['bg'])
    419  vterm.print_color(cell['bg'])
    420 
    421  actual = actual .. read_rm()
    422 
    423  t.eq(expected, actual)
    424 end
    425 
    426 local function screen_eol(row, col, expected, screen)
    427  local pos = t.ffi.new('VTermPos')
    428  pos['row'] = row
    429  pos['col'] = col
    430 
    431  local is_eol = vterm.vterm_screen_is_eol(screen, pos)
    432  t.eq(expected, is_eol)
    433 end
    434 
    435 local function screen_attrs_extent(row, col, expected, screen)
    436  local pos = t.ffi.new('VTermPos')
    437  pos['row'] = row
    438  pos['col'] = col
    439 
    440  local rect = t.ffi.new('VTermRect')
    441  rect['start_col'] = 0
    442  rect['end_col'] = -1
    443  vterm.vterm_screen_get_attrs_extent(screen, rect, pos, 1)
    444 
    445  local actual = string.format(
    446    '%d,%d-%d,%d',
    447    rect['start_row'],
    448    rect['start_col'],
    449    rect['end_row'],
    450    rect['end_col']
    451  )
    452 
    453  t.eq(expected, actual)
    454 end
    455 
    456 local function wantencoding()
    457  local encoding = t.ffi.new('VTermEncodingInstance')
    458  encoding['enc'] = vterm.vterm_lookup_encoding(vterm.ENC_UTF8, string.byte('u'))
    459  if encoding.enc.init then
    460    encoding.enc.init(encoding.enc, encoding['data'])
    461  end
    462  return encoding
    463 end
    464 
    465 local function encin(input, encoding)
    466  local len = string.len(input)
    467 
    468  local cp = t.ffi.new('uint32_t[?]', len)
    469  local cpi = t.ffi.new('int[1]')
    470  local pos = t.ffi.new('size_t[1]', 0)
    471 
    472  encoding.enc.decode(encoding.enc, encoding.data, cp, cpi, len, input, pos, len)
    473 
    474  local f = assert(io.open(t.paths.vterm_test_file, 'w'))
    475  if tonumber(cpi[0]) > 0 then
    476    f:write('encout ')
    477    for i = 0, cpi[0] - 1 do
    478      if i == 0 then
    479        f:write(string.format('%x', cp[i]))
    480      else
    481        f:write(string.format(',%x', cp[i]))
    482      end
    483    end
    484    f:write('\n')
    485  end
    486  f:close()
    487 end
    488 
    489 local function strpe_modifiers(input_mod)
    490  local mod = t.ffi.new('VTermModifier') ---@type any
    491  if input_mod.C then
    492    mod = bit.bor(mod, vterm.VTERM_MOD_CTRL)
    493  end
    494  if input_mod.S then
    495    mod = bit.bor(mod, vterm.VTERM_MOD_SHIFT)
    496  end
    497  if input_mod.A then
    498    mod = bit.bor(mod, vterm.VTERM_MOD_ALT)
    499  end
    500  return mod
    501 end
    502 
    503 local function strp_key(input_key)
    504  if input_key == 'up' then
    505    return vterm.VTERM_KEY_UP
    506  end
    507 
    508  if input_key == 'tab' then
    509    return vterm.VTERM_KEY_TAB
    510  end
    511 
    512  if input_key == 'enter' then
    513    return vterm.VTERM_KEY_ENTER
    514  end
    515 
    516  if input_key == 'bs' then
    517    return vterm.VTERM_KEY_BACKSPACE
    518  end
    519 
    520  if input_key == 'del' then
    521    return vterm.VTERM_KEY_DEL
    522  end
    523 
    524  if input_key == 'esc' then
    525    return vterm.VTERM_KEY_ESCAPE
    526  end
    527 
    528  if input_key == 'f1' then
    529    return vterm.VTERM_KEY_FUNCTION_0 + 1
    530  end
    531 
    532  if input_key == 'kp0' then
    533    return vterm.VTERM_KEY_KP_0
    534  end
    535 
    536  return vterm.VTERM_KEY_NONE
    537 end
    538 
    539 local function mousemove(row, col, vt, input_mod)
    540  input_mod = input_mod or {}
    541  local mod = strpe_modifiers(input_mod)
    542  vterm.vterm_mouse_move(vt, row, col, mod)
    543 end
    544 
    545 local function mousebtn(press, button, vt, input_mod)
    546  input_mod = input_mod or {}
    547  local mod = strpe_modifiers(input_mod)
    548  local flag = press == 'd' or press == 'D'
    549  vterm.vterm_mouse_button(vt, button, flag, mod)
    550 end
    551 
    552 local function inchar(c, vt, input_mod)
    553  input_mod = input_mod or {}
    554  local mod = strpe_modifiers(input_mod)
    555  vterm.vterm_keyboard_unichar(vt, c, mod)
    556 end
    557 
    558 local function inkey(input_key, vt, input_mod)
    559  input_mod = input_mod or {}
    560  local mod = strpe_modifiers(input_mod)
    561  local key = strp_key(input_key)
    562  vterm.vterm_keyboard_key(vt, key, mod)
    563 end
    564 
    565 before_each(function()
    566  vim.fs.rm(t.paths.vterm_test_file, { force = true })
    567 end)
    568 
    569 describe('vterm', function()
    570  itp('02parser', function()
    571    local vt = init()
    572    vterm.vterm_set_utf8(vt, false)
    573    wantparser(vt)
    574 
    575    -- Basic text
    576    push('hello', vt)
    577    expect('text 68,65,6c,6c,6f')
    578 
    579    -- C0
    580    push('\x03', vt)
    581    expect('control 03')
    582    push('\x1f', vt)
    583    expect('control 1f')
    584 
    585    -- C1 8bit
    586    push('\x83', vt)
    587    expect('control 83')
    588    push('\x99', vt)
    589    expect('control 99')
    590 
    591    -- C1 7bit
    592    push('\x1b\x43', vt)
    593    expect('control 83')
    594    push('\x1b\x59', vt)
    595    expect('control 99')
    596 
    597    -- High bytes
    598    push('\xa0\xcc\xfe', vt)
    599    expect('text a0,cc,fe')
    600 
    601    -- Mixed
    602    push('1\n2', vt)
    603    expect('text 31\ncontrol 0a\ntext 32')
    604 
    605    -- Escape
    606    push('\x1b=', vt)
    607    expect('escape =')
    608 
    609    -- Escape 2-byte
    610    push('\x1b(X', vt)
    611    expect('escape (X')
    612 
    613    -- Split write Escape
    614    push('\x1b(', vt)
    615    push('Y', vt)
    616    expect('escape (Y')
    617 
    618    -- Escape cancels Escape, starts another
    619    push('\x1b(\x1b)Z', vt)
    620    expect('escape )Z')
    621 
    622    -- CAN cancels Escape, returns to normal mode
    623    push('\x1b(\x18AB', vt)
    624    expect('text 41,42')
    625 
    626    -- C0 in Escape interrupts and continues
    627    push('\x1b(\nX', vt)
    628    expect('control 0a\nescape (X')
    629 
    630    -- CSI 0 args
    631    push('\x1b[a', vt)
    632    expect('csi 61 *')
    633 
    634    -- CSI 1 arg
    635    push('\x1b[9b', vt)
    636    expect('csi 62 9')
    637 
    638    -- CSI 2 args
    639    push('\x1b[3;4c', vt)
    640    expect('csi 63 3,4')
    641 
    642    -- CSI 1 arg 1 sub
    643    push('\x1b[1:2c', vt)
    644    expect('csi 63 1+,2')
    645 
    646    -- CSI many digits
    647    push('\x1b[678d', vt)
    648    expect('csi 64 678')
    649 
    650    -- CSI leading zero
    651    push('\x1b[007e', vt)
    652    expect('csi 65 7')
    653 
    654    -- CSI qmark
    655    push('\x1b[?2;7f', vt)
    656    expect('csi 66 L=3f 2,7')
    657 
    658    -- CSI greater
    659    push('\x1b[>c', vt)
    660    expect('csi 63 L=3e *')
    661 
    662    -- CSI SP
    663    push('\x1b[12 q', vt)
    664    expect('csi 71 12 I=20')
    665 
    666    -- Mixed CSI
    667    push('A\x1b[8mB', vt)
    668    expect('text 41\ncsi 6d 8\ntext 42')
    669 
    670    -- Split write
    671    push('\x1b', vt)
    672    push('[a', vt)
    673    expect('csi 61 *')
    674    push('foo\x1b[', vt)
    675    expect('text 66,6f,6f')
    676    push('4b', vt)
    677    expect('csi 62 4')
    678    push('\x1b[12;', vt)
    679    push('3c', vt)
    680    expect('csi 63 12,3')
    681 
    682    -- Escape cancels CSI, starts Escape
    683    push('\x1b[123\x1b9', vt)
    684    expect('escape 9')
    685 
    686    -- CAN cancels CSI, returns to normal mode
    687    push('\x1b[12\x18AB', vt)
    688    expect('text 41,42')
    689 
    690    -- C0 in Escape interrupts and continues
    691    push('\x1b(\nX', vt)
    692    expect('control 0a\nescape (X')
    693 
    694    -- OSC BEL
    695    push('\x1b]1;Hello\x07', vt)
    696    expect('osc [1;Hello]')
    697 
    698    -- OSC ST (7bit)
    699    push('\x1b]1;Hello\x1b\\', vt)
    700    expect('osc [1;Hello]')
    701 
    702    -- OSC ST (8bit)
    703    push('\x9d1;Hello\x9c', vt)
    704    expect('osc [1;Hello]')
    705 
    706    -- OSC in parts
    707    push('\x1b]52;abc', vt)
    708    expect('osc [52;abc')
    709    push('def', vt)
    710    expect('osc def')
    711    push('ghi\x1b\\', vt)
    712    expect('osc ghi]')
    713 
    714    -- OSC BEL without semicolon
    715    push('\x1b]1234\x07', vt)
    716    expect('osc [1234;]')
    717 
    718    -- OSC ST without semicolon
    719    push('\x1b]1234\x1b\\', vt)
    720    expect('osc [1234;]')
    721 
    722    -- Escape cancels OSC, starts Escape
    723    push('\x1b]Something\x1b9', vt)
    724    expect('escape 9')
    725 
    726    -- CAN cancels OSC, returns to normal mode
    727    push('\x1b]12\x18AB', vt)
    728    expect('text 41,42')
    729 
    730    -- C0 in OSC interrupts and continues
    731    push('\x1b]2;\nBye\x07', vt)
    732    expect('osc [2;\ncontrol 0a\nosc Bye]')
    733 
    734    -- DCS BEL
    735    push('\x1bPHello\x07', vt)
    736    expect('dcs [Hello]')
    737 
    738    -- DCS ST (7bit)
    739    push('\x1bPHello\x1b\\', vt)
    740    expect('dcs [Hello]')
    741 
    742    -- DCS ST (8bit)
    743    push('\x90Hello\x9c', vt)
    744    expect('dcs [Hello]')
    745 
    746    -- Split write of 7bit ST
    747    push('\x1bPABC\x1b', vt)
    748    expect('dcs [ABC')
    749    push('\\', vt)
    750    expect('dcs ]')
    751 
    752    -- Escape cancels DCS, starts Escape
    753    push('\x1bPSomething\x1b9', vt)
    754    expect('escape 9')
    755 
    756    -- CAN cancels DCS, returns to normal mode
    757    push('\x1bP12\x18AB', vt)
    758    expect('text 41,42')
    759 
    760    -- C0 in OSC interrupts and continues
    761    push('\x1bPBy\ne\x07', vt)
    762    expect('dcs [By\ncontrol 0a\ndcs e]')
    763 
    764    -- APC BEL
    765    push('\x1b_Hello\x07', vt)
    766    expect('apc [Hello]')
    767 
    768    -- APC ST (7bit)
    769    push('\x1b_Hello\x1b\\', vt)
    770    expect('apc [Hello]')
    771 
    772    -- APC ST (8bit)
    773    push('\x9fHello\x9c', vt)
    774    expect('apc [Hello]')
    775 
    776    -- PM BEL
    777    push('\x1b^Hello\x07', vt)
    778    expect('pm [Hello]')
    779 
    780    -- PM ST (7bit)
    781    push('\x1b^Hello\x1b\\', vt)
    782    expect('pm [Hello]')
    783 
    784    -- PM ST (8bit)
    785    push('\x9eHello\x9c', vt)
    786    expect('pm [Hello]')
    787 
    788    -- SOS BEL
    789    push('\x1bXHello\x07', vt)
    790    expect('sos [Hello]')
    791 
    792    -- SOS ST (7bit)
    793    push('\x1bXHello\x1b\\', vt)
    794    expect('sos [Hello]')
    795 
    796    -- SOS ST (8bit)
    797    push('\x98Hello\x9c', vt)
    798    expect('sos [Hello]')
    799 
    800    push('\x1bXABC\x01DEF\x1b\\', vt)
    801    expect('sos [ABC\x01DEF]')
    802    push('\x1bXABC\x99DEF\x1b\\', vt)
    803    expect('sos [ABC\x99DEF]')
    804 
    805    -- NUL ignored
    806    push('\x00', vt)
    807 
    808    -- NUL ignored within CSI
    809    push('\x1b[12\x003m', vt)
    810    expect('csi 6d 123')
    811 
    812    -- DEL ignored
    813    push('\x7f', vt)
    814 
    815    -- DEL ignored within CSI
    816    push('\x1b[12\x7f3m', vt)
    817    expect('csi 6d 123')
    818 
    819    -- DEL inside text"
    820    push('AB\x7fC', vt)
    821    expect('text 41,42\ntext 43')
    822  end)
    823 
    824  itp('03encoding_utf8', function()
    825    local encoding = wantencoding()
    826 
    827    -- Low
    828    encin('123', encoding)
    829    expect('encout 31,32,33')
    830 
    831    -- We want to prove the UTF-8 parser correctly handles all the sequences.
    832    -- Easy way to do this is to check it does low/high boundary cases, as that
    833    -- leaves only two for each sequence length
    834    --
    835    -- These ranges are therefore:
    836    --
    837    -- Two bytes:
    838    -- U+0080 = 000 10000000 =>    00010   000000
    839    --                       => 11000010 10000000 = C2 80
    840    -- U+07FF = 111 11111111 =>    11111   111111
    841    --                       => 11011111 10111111 = DF BF
    842    --
    843    -- Three bytes:
    844    -- U+0800 = 00001000 00000000 =>     0000   100000   000000
    845    --                            => 11100000 10100000 10000000 = E0 A0 80
    846    -- U+FFFD = 11111111 11111101 =>     1111   111111   111101
    847    --                            => 11101111 10111111 10111101 = EF BF BD
    848    -- (We avoid U+FFFE and U+FFFF as they're invalid codepoints)
    849    --
    850    -- Four bytes:
    851    -- U+10000  = 00001 00000000 00000000 =>      000   010000   000000   000000
    852    --                                    => 11110000 10010000 10000000 10000000 = F0 90 80 80
    853    -- U+1FFFFF = 11111 11111111 11111111 =>      111   111111   111111   111111
    854    --                                    => 11110111 10111111 10111111 10111111 = F7 BF BF BF
    855 
    856    -- 2 byte
    857    encin('\xC2\x80\xDF\xBF', encoding)
    858    expect('encout 80,7ff')
    859 
    860    -- 3 byte
    861    encin('\xE0\xA0\x80\xEF\xBF\xBD', encoding)
    862    expect('encout 800,fffd')
    863 
    864    -- 4 byte
    865    encin('\xF0\x90\x80\x80\xF7\xBF\xBF\xBF', encoding)
    866    expect('encout 10000,1fffff')
    867 
    868    -- Next up, we check some invalid sequences
    869    --  + Early termination (back to low bytes too soon)
    870    --  + Early restart (another sequence introduction before the previous one was finished)
    871 
    872    -- Early termination
    873    encin('\xC2!', encoding)
    874    expect('encout fffd,21')
    875 
    876    encin('\xE0!\xE0\xA0!', encoding)
    877    expect('encout fffd,21,fffd,21')
    878 
    879    encin('\xF0!\xF0\x90!\xF0\x90\x80!', encoding)
    880    expect('encout fffd,21,fffd,21,fffd,21')
    881 
    882    -- Early restart
    883    encin('\xC2\xC2\x90', encoding)
    884    expect('encout fffd,90')
    885 
    886    encin('\xE0\xC2\x90\xE0\xA0\xC2\x90', encoding)
    887    expect('encout fffd,90,fffd,90')
    888 
    889    encin('\xF0\xC2\x90\xF0\x90\xC2\x90\xF0\x90\x80\xC2\x90', encoding)
    890    expect('encout fffd,90,fffd,90,fffd,90')
    891 
    892    -- Test the overlong sequences by giving an overlong encoding of U+0000 and
    893    -- an encoding of the highest codepoint still too short
    894    --
    895    -- Two bytes:
    896    -- U+0000 = C0 80
    897    -- U+007F = 000 01111111 =>    00001   111111 =>
    898    --                       => 11000001 10111111 => C1 BF
    899    --
    900    -- Three bytes:
    901    -- U+0000 = E0 80 80
    902    -- U+07FF = 00000111 11111111 =>     0000   011111   111111
    903    --                            => 11100000 10011111 10111111 = E0 9F BF
    904    --
    905    -- Four bytes:
    906    -- U+0000 = F0 80 80 80
    907    -- U+FFFF = 11111111 11111111 =>      000   001111   111111   111111
    908    --                            => 11110000 10001111 10111111 10111111 = F0 8F BF BF
    909 
    910    -- Overlong
    911    encin('\xC0\x80\xC1\xBF', encoding)
    912    expect('encout fffd,fffd')
    913 
    914    encin('\xE0\x80\x80\xE0\x9F\xBF', encoding)
    915    expect('encout fffd,fffd')
    916 
    917    encin('\xF0\x80\x80\x80\xF0\x8F\xBF\xBF', encoding)
    918    expect('encout fffd,fffd')
    919 
    920    -- UTF-16 surrogates U+D800 and U+DFFF
    921    -- UTF-16 Surrogates
    922    encin('\xED\xA0\x80\xED\xBF\xBF', encoding)
    923    expect('encout fffd,fffd')
    924 
    925    -- Split write
    926    encin('\xC2', encoding)
    927    encin('\xA0', encoding)
    928    expect('encout a0')
    929 
    930    encin('\xE0', encoding)
    931    encin('\xA0\x80', encoding)
    932    expect('encout 800')
    933    encin('\xE0\xA0', encoding)
    934    encin('\x80', encoding)
    935    expect('encout 800')
    936 
    937    encin('\xF0', encoding)
    938    encin('\x90\x80\x80', encoding)
    939    expect('encout 10000')
    940    encin('\xF0\x90', encoding)
    941    encin('\x80\x80', encoding)
    942    expect('encout 10000')
    943    encin('\xF0\x90\x80', encoding)
    944    encin('\x80', encoding)
    945    expect('encout 10000')
    946  end)
    947 
    948  itp('10state_putglyph', function()
    949    local vt = init()
    950    local state = wantstate(vt, { g = true })
    951 
    952    -- Low
    953    reset(state, nil)
    954    push('ABC', vt)
    955    expect('putglyph 41 1 0,0\nputglyph 42 1 0,1\nputglyph 43 1 0,2')
    956 
    957    -- UTF-8 1 char
    958    -- U+00C1 = 0xC3 0x81  name: LATIN CAPITAL LETTER A WITH ACUTE
    959    -- U+00E9 = 0xC3 0xA9  name: LATIN SMALL LETTER E WITH ACUTE
    960    reset(state, nil)
    961    push('\xC3\x81\xC3\xA9', vt)
    962    expect('putglyph c1 1 0,0\nputglyph e9 1 0,1')
    963 
    964    -- UTF-8 split writes
    965    reset(state, nil)
    966    push('\xC3', vt)
    967    push('\x81', vt)
    968    expect('putglyph c1 1 0,0')
    969 
    970    -- UTF-8 wide char
    971    -- U+FF10 = EF BC 90  name: FULLWIDTH DIGIT ZERO
    972    reset(state, nil)
    973    push('\xEF\xBC\x90 ', vt)
    974    expect('putglyph ff10 2 0,0\nputglyph 20 1 0,2')
    975 
    976    -- UTF-8 emoji wide char
    977    -- U+1F600 = F0 9F 98 80  name: GRINNING FACE
    978    reset(state, nil)
    979    push('\xF0\x9F\x98\x80 ', vt)
    980    expect('putglyph 1f600 2 0,0\nputglyph 20 1 0,2')
    981 
    982    -- UTF-8 combining chars
    983    -- U+0301 = CC 81  name: COMBINING ACUTE
    984    reset(state, nil)
    985    push('e\xCC\x81Z', vt)
    986    expect('putglyph 65,301 1 0,0\nputglyph 5a 1 0,1')
    987 
    988    -- Combining across buffers
    989    reset(state, nil)
    990    push('e', vt)
    991    expect('putglyph 65 1 0,0')
    992    push('\xCC\x81Z', vt)
    993    expect('putglyph 65,301 1 0,0\nputglyph 5a 1 0,1')
    994 
    995    -- Spare combining chars get truncated
    996    reset(state, nil)
    997    push('e' .. string.rep('\xCC\x81', 20), vt)
    998    expect('putglyph 65,301,301,301,301,301,301,301,301,301,301,301,301,301,301 1 0,0') -- and nothing more
    999 
   1000    -- Combining across buffers multiple times
   1001    reset(state, nil)
   1002    push('e', vt)
   1003    expect('putglyph 65 1 0,0')
   1004    push('\xCC\x81', vt)
   1005    expect('putglyph 65,301 1 0,0')
   1006    push('\xCC\x82', vt)
   1007    expect('putglyph 65,301,302 1 0,0')
   1008 
   1009    -- Combining across buffers at right edge
   1010    reset(state, nil)
   1011    push('\x1b[5;80H', vt)
   1012    push('e', vt)
   1013    expect('putglyph 65 1 4,79')
   1014    push('\xCC\x81', vt)
   1015    expect('putglyph 65,301 1 4,79')
   1016    push('\xCC\x82Z', vt)
   1017    expect('putglyph 65,301,302 1 4,79\nputglyph 5a 1 5,0')
   1018 
   1019    -- Combining regional indicators
   1020    reset(state, nil)
   1021    push('\x1b[5;77H', vt)
   1022    push('🇦', vt)
   1023    expect('putglyph 1f1e6 2 4,76')
   1024    push('🇩', vt)
   1025    expect('putglyph 1f1e6,1f1e9 2 4,76')
   1026    push('🇱', vt)
   1027    expect('putglyph 1f1f1 2 4,78')
   1028    push('🇮', vt)
   1029    expect('putglyph 1f1f1,1f1ee 2 4,78')
   1030    push('🇲', vt)
   1031    expect('putglyph 1f1f2 2 5,0')
   1032    push('🇨', vt)
   1033    expect('putglyph 1f1f2,1f1e8 2 5,0')
   1034 
   1035    -- emoji with ZWJ and variant selectors, as one chunk
   1036    reset(state, nil)
   1037    push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt)
   1038    expect([[putglyph 1f3f3,fe0f,200d,1f308 2 0,0
   1039 putglyph 1f3f3,fe0f,200d,26a7,fe0f 2 0,2
   1040 putglyph 1f3f4,200d,2620,fe0f 2 0,4]])
   1041 
   1042    -- emoji, one code point at a time
   1043    reset(state, nil)
   1044    push('🏳', vt)
   1045    expect('putglyph 1f3f3 2 0,0')
   1046    push('\xef\xb8\x8f', vt)
   1047    expect('putglyph 1f3f3,fe0f 2 0,0')
   1048    push('\xe2\x80\x8d', vt)
   1049    expect('putglyph 1f3f3,fe0f,200d 2 0,0')
   1050    push('🌈', vt)
   1051    expect('putglyph 1f3f3,fe0f,200d,1f308 2 0,0')
   1052 
   1053    -- modifier can change width
   1054    push('❤', vt)
   1055    expect('putglyph 2764 1 0,2')
   1056    push('\xef\xb8\x8f', vt)
   1057    expect('putglyph 2764,fe0f 2 0,2')
   1058 
   1059    -- also works batched
   1060    push('❤️', vt)
   1061    expect('putglyph 2764,fe0f 2 0,4')
   1062 
   1063    -- DECSCA protected
   1064    reset(state, nil)
   1065    push('A\x1b[1"qB\x1b[2"qC', vt)
   1066    expect('putglyph 41 1 0,0\nputglyph 42 1 0,1 prot\nputglyph 43 1 0,2')
   1067  end)
   1068 
   1069  itp('11state_movecursor', function()
   1070    local vt = init()
   1071    local state = wantstate(vt)
   1072 
   1073    -- Implicit
   1074    push('ABC', vt)
   1075    cursor(0, 3, state)
   1076 
   1077    -- Backspace
   1078    push('\b', vt)
   1079    cursor(0, 2, state)
   1080    -- Horizontal Tab
   1081    push('\t', vt)
   1082    cursor(0, 8, state)
   1083    -- Carriage Return
   1084    push('\r', vt)
   1085    cursor(0, 0, state)
   1086    -- Linefeed
   1087    push('\n', vt)
   1088    cursor(1, 0, state)
   1089 
   1090    -- Backspace bounded by lefthand edge
   1091    push('\x1b[4;2H', vt)
   1092    cursor(3, 1, state)
   1093    push('\b', vt)
   1094    cursor(3, 0, state)
   1095    push('\b', vt)
   1096    cursor(3, 0, state)
   1097 
   1098    -- Backspace cancels phantom
   1099    push('\x1b[4;80H', vt)
   1100    cursor(3, 79, state)
   1101    push('X', vt)
   1102    cursor(3, 79, state)
   1103    push('\b', vt)
   1104    cursor(3, 78, state)
   1105 
   1106    -- HT bounded by righthand edge
   1107    push('\x1b[1;78H', vt)
   1108    cursor(0, 77, state)
   1109    push('\t', vt)
   1110    cursor(0, 79, state)
   1111    push('\t', vt)
   1112    cursor(0, 79, state)
   1113 
   1114    reset(state, nil)
   1115 
   1116    -- Index
   1117    push('ABC\x1bD', vt)
   1118    cursor(1, 3, state)
   1119    -- Reverse Index
   1120    push('\x1bM', vt)
   1121    cursor(0, 3, state)
   1122    -- Newline
   1123    push('\x1bE', vt)
   1124    cursor(1, 0, state)
   1125 
   1126    reset(state, nil)
   1127 
   1128    -- Cursor Forward
   1129    push('\x1b[B', vt)
   1130    cursor(1, 0, state)
   1131    push('\x1b[3B', vt)
   1132    cursor(4, 0, state)
   1133    push('\x1b[0B', vt)
   1134    cursor(5, 0, state)
   1135 
   1136    -- Cursor Down
   1137    push('\x1b[C', vt)
   1138    cursor(5, 1, state)
   1139    push('\x1b[3C', vt)
   1140    cursor(5, 4, state)
   1141    push('\x1b[0C', vt)
   1142    cursor(5, 5, state)
   1143 
   1144    -- Cursor Up
   1145    push('\x1b[A', vt)
   1146    cursor(4, 5, state)
   1147    push('\x1b[3A', vt)
   1148    cursor(1, 5, state)
   1149    push('\x1b[0A', vt)
   1150    cursor(0, 5, state)
   1151 
   1152    -- Cursor Backward
   1153    push('\x1b[D', vt)
   1154    cursor(0, 4, state)
   1155    push('\x1b[3D', vt)
   1156    cursor(0, 1, state)
   1157    push('\x1b[0D', vt)
   1158    cursor(0, 0, state)
   1159 
   1160    -- Cursor Next Line
   1161    push('   ', vt)
   1162    cursor(0, 3, state)
   1163    push('\x1b[E', vt)
   1164    cursor(1, 0, state)
   1165    push('   ', vt)
   1166    cursor(1, 3, state)
   1167    push('\x1b[2E', vt)
   1168    cursor(3, 0, state)
   1169    push('\x1b[0E', vt)
   1170    cursor(4, 0, state)
   1171 
   1172    -- Cursor Previous Line
   1173    push('   ', vt)
   1174    cursor(4, 3, state)
   1175    push('\x1b[F', vt)
   1176    cursor(3, 0, state)
   1177    push('   ', vt)
   1178    cursor(3, 3, state)
   1179    push('\x1b[2F', vt)
   1180    cursor(1, 0, state)
   1181    push('\x1b[0F', vt)
   1182    cursor(0, 0, state)
   1183 
   1184    -- Cursor Horizontal Absolute
   1185    push('\n', vt)
   1186    cursor(1, 0, state)
   1187    push('\x1b[20G', vt)
   1188    cursor(1, 19, state)
   1189    push('\x1b[G', vt)
   1190    cursor(1, 0, state)
   1191 
   1192    -- Cursor Position
   1193    push('\x1b[10;5H', vt)
   1194    cursor(9, 4, state)
   1195    push('\x1b[8H', vt)
   1196    cursor(7, 0, state)
   1197    push('\x1b[H', vt)
   1198    cursor(0, 0, state)
   1199 
   1200    -- Cursor Position cancels phantom
   1201    push('\x1b[10;78H', vt)
   1202    cursor(9, 77, state)
   1203    push('ABC', vt)
   1204    cursor(9, 79, state)
   1205    push('\x1b[10;80H', vt)
   1206    push('C', vt)
   1207    cursor(9, 79, state)
   1208    push('X', vt)
   1209    cursor(10, 1, state)
   1210 
   1211    reset(state, nil)
   1212 
   1213    -- Bounds Checking
   1214    push('\x1b[A', vt)
   1215    cursor(0, 0, state)
   1216    push('\x1b[D', vt)
   1217    cursor(0, 0, state)
   1218    push('\x1b[25;80H', vt)
   1219    cursor(24, 79, state)
   1220    push('\x1b[B', vt)
   1221    cursor(24, 79, state)
   1222    push('\x1b[C', vt)
   1223    cursor(24, 79, state)
   1224    push('\x1b[E', vt)
   1225    cursor(24, 0, state)
   1226    push('\x1b[H', vt)
   1227    cursor(0, 0, state)
   1228    push('\x1b[F', vt)
   1229    cursor(0, 0, state)
   1230    push('\x1b[999G', vt)
   1231    cursor(0, 79, state)
   1232    push('\x1b[99;99H', vt)
   1233    cursor(24, 79, state)
   1234 
   1235    reset(state, nil)
   1236 
   1237    -- Horizontal Position Absolute
   1238    push('\x1b[5`', vt)
   1239    cursor(0, 4, state)
   1240 
   1241    -- Horizontal Position Relative
   1242    push('\x1b[3a', vt)
   1243    cursor(0, 7, state)
   1244 
   1245    -- Horizontal Position Backward
   1246    push('\x1b[3j', vt)
   1247    cursor(0, 4, state)
   1248 
   1249    -- Horizontal and Vertical Position
   1250    push('\x1b[3;3f', vt)
   1251    cursor(2, 2, state)
   1252 
   1253    -- Vertical Position Absolute
   1254    push('\x1b[5d', vt)
   1255    cursor(4, 2, state)
   1256 
   1257    -- Vertical Position Relative
   1258    push('\x1b[2e', vt)
   1259    cursor(6, 2, state)
   1260 
   1261    -- Vertical Position Backward
   1262    push('\x1b[2k', vt)
   1263    cursor(4, 2, state)
   1264 
   1265    reset(state, nil)
   1266 
   1267    -- Horizontal Tab
   1268    push('\t', vt)
   1269    cursor(0, 8, state)
   1270    push('   ', vt)
   1271    cursor(0, 11, state)
   1272    push('\t', vt)
   1273    cursor(0, 16, state)
   1274    push('       ', vt)
   1275    cursor(0, 23, state)
   1276    push('\t', vt)
   1277    cursor(0, 24, state)
   1278    push('        ', vt)
   1279    cursor(0, 32, state)
   1280    push('\t', vt)
   1281    cursor(0, 40, state)
   1282 
   1283    -- Cursor Horizontal Tab
   1284    push('\x1b[I', vt)
   1285    cursor(0, 48, state)
   1286    push('\x1b[2I', vt)
   1287    cursor(0, 64, state)
   1288 
   1289    -- Cursor Backward Tab
   1290    push('\x1b[Z', vt)
   1291    cursor(0, 56, state)
   1292    push('\x1b[2Z', vt)
   1293    cursor(0, 40, state)
   1294  end)
   1295 
   1296  itp('12state_scroll', function()
   1297    local vt = init()
   1298    local state = wantstate(vt, { s = true })
   1299 
   1300    -- Linefeed
   1301    push(string.rep('\n', 24), vt)
   1302    cursor(24, 0, state)
   1303    push('\n', vt)
   1304    expect('scrollrect 0..25,0..80 => +1,+0')
   1305    cursor(24, 0, state)
   1306 
   1307    reset(state, nil)
   1308 
   1309    -- Index
   1310    push('\x1b[25H', vt)
   1311    push('\x1bD', vt)
   1312    expect('scrollrect 0..25,0..80 => +1,+0')
   1313 
   1314    reset(state, nil)
   1315 
   1316    -- Reverse Index
   1317    push('\x1bM', vt)
   1318    expect('scrollrect 0..25,0..80 => -1,+0')
   1319 
   1320    reset(state, nil)
   1321 
   1322    -- Linefeed in DECSTBM
   1323    push('\x1b[1;10r', vt)
   1324    cursor(0, 0, state)
   1325    push(string.rep('\n', 9), vt)
   1326    cursor(9, 0, state)
   1327    push('\n', vt)
   1328    expect('scrollrect 0..10,0..80 => +1,+0')
   1329    cursor(9, 0, state)
   1330 
   1331    -- Linefeed outside DECSTBM
   1332    push('\x1b[20H', vt)
   1333    cursor(19, 0, state)
   1334    push('\n', vt)
   1335    cursor(20, 0, state)
   1336 
   1337    -- Index in DECSTBM
   1338    push('\x1b[9;10r', vt)
   1339    push('\x1b[10H', vt)
   1340    push('\x1bM', vt)
   1341    cursor(8, 0, state)
   1342    push('\x1bM', vt)
   1343    expect('scrollrect 8..10,0..80 => -1,+0')
   1344 
   1345    -- Reverse Index in DECSTBM
   1346    push('\x1b[25H', vt)
   1347    cursor(24, 0, state)
   1348    push('\n', vt)
   1349    -- no scrollrect
   1350    cursor(24, 0, state)
   1351 
   1352    -- Linefeed in DECSTBM+DECSLRM
   1353    push('\x1b[?69h', vt)
   1354    push('\x1b[3;10r\x1b[10;40s', vt)
   1355    push('\x1b[10;10H\n', vt)
   1356    expect('scrollrect 2..10,9..40 => +1,+0')
   1357 
   1358    -- IND/RI in DECSTBM+DECSLRM
   1359    push('\x1bD', vt)
   1360    expect('scrollrect 2..10,9..40 => +1,+0')
   1361    push('\x1b[3;10H\x1bM', vt)
   1362    expect('scrollrect 2..10,9..40 => -1,+0')
   1363 
   1364    -- DECRQSS on DECSTBM
   1365    push('\x1bP$qr\x1b\\', vt)
   1366    expect_output('\x1bP1$r3;10r\x1b\\')
   1367 
   1368    -- DECRQSS on DECSLRM
   1369    push('\x1bP$qs\x1b\\', vt)
   1370    expect_output('\x1bP1$r10;40s\x1b\\')
   1371 
   1372    -- Setting invalid DECSLRM with !DECVSSM is still rejected
   1373    push('\x1b[?69l\x1b[;0s\x1b[?69h', vt)
   1374 
   1375    reset(state, nil)
   1376 
   1377    -- Scroll Down
   1378    push('\x1b[S', vt)
   1379    expect('scrollrect 0..25,0..80 => +1,+0')
   1380    cursor(0, 0, state)
   1381    push('\x1b[2S', vt)
   1382    expect('scrollrect 0..25,0..80 => +2,+0')
   1383    cursor(0, 0, state)
   1384    push('\x1b[100S', vt)
   1385    expect('scrollrect 0..25,0..80 => +25,+0')
   1386 
   1387    -- Scroll Up
   1388    push('\x1b[T', vt)
   1389    expect('scrollrect 0..25,0..80 => -1,+0')
   1390    cursor(0, 0, state)
   1391    push('\x1b[2T', vt)
   1392    expect('scrollrect 0..25,0..80 => -2,+0')
   1393    cursor(0, 0, state)
   1394    push('\x1b[100T', vt)
   1395    expect('scrollrect 0..25,0..80 => -25,+0')
   1396 
   1397    -- SD/SU in DECSTBM
   1398    push('\x1b[5;20r', vt)
   1399    push('\x1b[S', vt)
   1400    expect('scrollrect 4..20,0..80 => +1,+0')
   1401    push('\x1b[T', vt)
   1402    expect('scrollrect 4..20,0..80 => -1,+0')
   1403 
   1404    reset(state, nil)
   1405 
   1406    -- SD/SU in DECSTBM+DECSLRM
   1407    push('\x1b[?69h', vt)
   1408    push('\x1b[3;10r\x1b[10;40s', vt)
   1409    cursor(0, 0, state)
   1410    push('\x1b[3;10H', vt)
   1411    cursor(2, 9, state)
   1412    push('\x1b[S', vt)
   1413    expect('scrollrect 2..10,9..40 => +1,+0')
   1414    push('\x1b[?69l', vt)
   1415    push('\x1b[S', vt)
   1416    expect('scrollrect 2..10,0..80 => +1,+0')
   1417 
   1418    -- Invalid boundaries
   1419    reset(state, nil)
   1420 
   1421    push('\x1b[100;105r\x1bD', vt)
   1422    push('\x1b[5;2r\x1bD', vt)
   1423 
   1424    reset(state, nil)
   1425    state = wantstate(vt, { m = true, e = true })
   1426 
   1427    -- Scroll Down move+erase emulation
   1428    push('\x1b[S', vt)
   1429    expect('moverect 1..25,0..80 -> 0..24,0..80\nerase 24..25,0..80')
   1430    cursor(0, 0, state)
   1431    push('\x1b[2S', vt)
   1432    expect('moverect 2..25,0..80 -> 0..23,0..80\nerase 23..25,0..80')
   1433    cursor(0, 0, state)
   1434 
   1435    -- Scroll Up move+erase emulation
   1436    push('\x1b[T', vt)
   1437    expect('moverect 0..24,0..80 -> 1..25,0..80\nerase 0..1,0..80')
   1438    cursor(0, 0, state)
   1439    push('\x1b[2T', vt)
   1440    expect('moverect 0..23,0..80 -> 2..25,0..80\nerase 0..2,0..80')
   1441    cursor(0, 0, state)
   1442 
   1443    -- DECSTBM resets cursor position
   1444    push('\x1b[5;5H', vt)
   1445    cursor(4, 4, state)
   1446    push('\x1b[r', vt)
   1447    cursor(0, 0, state)
   1448  end)
   1449 
   1450  itp('13state_edit', function()
   1451    local vt = init()
   1452    local state = wantstate(vt, { s = true, e = true, b = true })
   1453 
   1454    -- ICH
   1455    reset(state, nil)
   1456    expect('erase 0..25,0..80')
   1457    cursor(0, 0, state)
   1458    push('ACD', vt)
   1459    push('\x1b[2D', vt)
   1460    cursor(0, 1, state)
   1461    push('\x1b[@', vt)
   1462    expect('scrollrect 0..1,1..80 => +0,-1')
   1463    cursor(0, 1, state)
   1464    push('B', vt)
   1465    cursor(0, 2, state)
   1466    push('\x1b[3@', vt)
   1467    expect('scrollrect 0..1,2..80 => +0,-3')
   1468 
   1469    -- ICH with DECSLRM
   1470    push('\x1b[?69h', vt)
   1471    push('\x1b[;50s', vt)
   1472    push('\x1b[20G\x1b[@', vt)
   1473    expect('scrollrect 0..1,19..50 => +0,-1')
   1474 
   1475    -- ICH outside DECSLRM
   1476    push('\x1b[70G\x1b[@', vt)
   1477    -- nothing happens
   1478 
   1479    -- DCH
   1480    reset(state, nil)
   1481    expect('erase 0..25,0..80')
   1482    cursor(0, 0, state)
   1483    push('ABBC', vt)
   1484    push('\x1b[3D', vt)
   1485    cursor(0, 1, state)
   1486    push('\x1b[P', vt)
   1487    expect('scrollrect 0..1,1..80 => +0,+1')
   1488    cursor(0, 1, state)
   1489    push('\x1b[3P', vt)
   1490    expect('scrollrect 0..1,1..80 => +0,+3')
   1491    cursor(0, 1, state)
   1492 
   1493    -- DCH with DECSLRM
   1494    push('\x1b[?69h', vt)
   1495    push('\x1b[;50s', vt)
   1496    push('\x1b[20G\x1b[P', vt)
   1497    expect('scrollrect 0..1,19..50 => +0,+1')
   1498 
   1499    -- DCH outside DECSLRM
   1500    push('\x1b[70G\x1b[P', vt)
   1501    -- nothing happens
   1502 
   1503    -- ECH
   1504    reset(state, nil)
   1505    expect('erase 0..25,0..80')
   1506    cursor(0, 0, state)
   1507    push('ABC', vt)
   1508    push('\x1b[2D', vt)
   1509    cursor(0, 1, state)
   1510    push('\x1b[X', vt)
   1511    expect('erase 0..1,1..2')
   1512    cursor(0, 1, state)
   1513    push('\x1b[3X', vt)
   1514    expect('erase 0..1,1..4')
   1515    cursor(0, 1, state)
   1516    -- ECH more columns than there are should be bounded
   1517    push('\x1b[100X', vt)
   1518    expect('erase 0..1,1..80')
   1519 
   1520    -- IL
   1521    reset(state, nil)
   1522    expect('erase 0..25,0..80')
   1523    cursor(0, 0, state)
   1524    push('A\r\nC', vt)
   1525    cursor(1, 1, state)
   1526    push('\x1b[L', vt)
   1527    expect('scrollrect 1..25,0..80 => -1,+0')
   1528    -- TODO(libvterm): ECMA-48 says we should move to line home, but neither xterm nor xfce4-terminal do this
   1529    cursor(1, 1, state)
   1530    push('\rB', vt)
   1531    cursor(1, 1, state)
   1532    push('\x1b[3L', vt)
   1533    expect('scrollrect 1..25,0..80 => -3,+0')
   1534 
   1535    -- IL with DECSTBM
   1536    push('\x1b[5;15r', vt)
   1537    push('\x1b[5H\x1b[L', vt)
   1538    expect('scrollrect 4..15,0..80 => -1,+0')
   1539 
   1540    -- IL outside DECSTBM
   1541    push('\x1b[20H\x1b[L', vt)
   1542    -- nothing happens
   1543 
   1544    -- IL with DECSTBM+DECSLRM
   1545    push('\x1b[?69h', vt)
   1546    push('\x1b[10;50s', vt)
   1547    push('\x1b[5;10H\x1b[L', vt)
   1548    expect('scrollrect 4..15,9..50 => -1,+0')
   1549 
   1550    -- DL
   1551    reset(state, nil)
   1552    expect('erase 0..25,0..80')
   1553    cursor(0, 0, state)
   1554    push('A\r\nB\r\nB\r\nC', vt)
   1555    cursor(3, 1, state)
   1556    push('\x1b[2H', vt)
   1557    cursor(1, 0, state)
   1558    push('\x1b[M', vt)
   1559    expect('scrollrect 1..25,0..80 => +1,+0')
   1560    cursor(1, 0, state)
   1561    push('\x1b[3M', vt)
   1562    expect('scrollrect 1..25,0..80 => +3,+0')
   1563    cursor(1, 0, state)
   1564 
   1565    -- DL with DECSTBM
   1566    push('\x1b[5;15r', vt)
   1567    push('\x1b[5H\x1b[M', vt)
   1568    expect('scrollrect 4..15,0..80 => +1,+0')
   1569 
   1570    -- DL outside DECSTBM
   1571    push('\x1b[20H\x1b[M', vt)
   1572    -- nothing happens
   1573 
   1574    -- DL with DECSTBM+DECSLRM
   1575    push('\x1b[?69h', vt)
   1576    push('\x1b[10;50s', vt)
   1577    push('\x1b[5;10H\x1b[M', vt)
   1578    expect('scrollrect 4..15,9..50 => +1,+0')
   1579 
   1580    -- DECIC
   1581    reset(state, nil)
   1582    expect('erase 0..25,0..80')
   1583    push("\x1b[20G\x1b[5'}", vt)
   1584    expect('scrollrect 0..25,19..80 => +0,-5')
   1585 
   1586    -- DECIC with DECSTBM+DECSLRM
   1587    push('\x1b[?69h', vt)
   1588    push('\x1b[4;20r\x1b[20;60s', vt)
   1589    push("\x1b[4;20H\x1b[3'}", vt)
   1590    expect('scrollrect 3..20,19..60 => +0,-3')
   1591 
   1592    -- DECIC outside DECSLRM
   1593    push("\x1b[70G\x1b['}", vt)
   1594    -- nothing happens
   1595 
   1596    -- DECDC
   1597    reset(state, nil)
   1598    expect('erase 0..25,0..80')
   1599    push("\x1b[20G\x1b[5'~", vt)
   1600    expect('scrollrect 0..25,19..80 => +0,+5')
   1601 
   1602    -- DECDC with DECSTBM+DECSLRM
   1603    push('\x1b[?69h', vt)
   1604    push('\x1b[4;20r\x1b[20;60s', vt)
   1605    push("\x1b[4;20H\x1b[3'~", vt)
   1606    expect('scrollrect 3..20,19..60 => +0,+3')
   1607 
   1608    -- DECDC outside DECSLRM
   1609    push("\x1b[70G\x1b['~", vt)
   1610    -- nothing happens
   1611 
   1612    -- EL 0
   1613    reset(state, nil)
   1614    expect('erase 0..25,0..80')
   1615    cursor(0, 0, state)
   1616    push('ABCDE', vt)
   1617    push('\x1b[3D', vt)
   1618    cursor(0, 2, state)
   1619    push('\x1b[0K', vt)
   1620    expect('erase 0..1,2..80')
   1621    cursor(0, 2, state)
   1622 
   1623    -- EL 1
   1624    reset(state, nil)
   1625    expect('erase 0..25,0..80')
   1626    cursor(0, 0, state)
   1627    push('ABCDE', vt)
   1628    push('\x1b[3D', vt)
   1629    cursor(0, 2, state)
   1630    push('\x1b[1K', vt)
   1631    expect('erase 0..1,0..3')
   1632    cursor(0, 2, state)
   1633 
   1634    -- EL 2
   1635    reset(state, nil)
   1636    expect('erase 0..25,0..80')
   1637    cursor(0, 0, state)
   1638    push('ABCDE', vt)
   1639    push('\x1b[3D', vt)
   1640    cursor(0, 2, state)
   1641    push('\x1b[2K', vt)
   1642    expect('erase 0..1,0..80')
   1643    cursor(0, 2, state)
   1644 
   1645    -- SEL
   1646    reset(state, nil)
   1647    expect('erase 0..25,0..80')
   1648    cursor(0, 0, state)
   1649    push('\x1b[11G', vt)
   1650    cursor(0, 10, state)
   1651    push('\x1b[?0K', vt)
   1652    expect('erase 0..1,10..80 selective')
   1653    cursor(0, 10, state)
   1654    push('\x1b[?1K', vt)
   1655    expect('erase 0..1,0..11 selective')
   1656    cursor(0, 10, state)
   1657    push('\x1b[?2K', vt)
   1658    expect('erase 0..1,0..80 selective')
   1659    cursor(0, 10, state)
   1660 
   1661    -- ED 0
   1662    reset(state, nil)
   1663    expect('erase 0..25,0..80')
   1664    cursor(0, 0, state)
   1665    push('\x1b[2;2H', vt)
   1666    cursor(1, 1, state)
   1667    push('\x1b[0J', vt)
   1668    expect('erase 1..2,1..80\nerase 2..25,0..80')
   1669    cursor(1, 1, state)
   1670 
   1671    -- ED 1
   1672    reset(state, nil)
   1673    expect('erase 0..25,0..80')
   1674    cursor(0, 0, state)
   1675    push('\x1b[2;2H', vt)
   1676    cursor(1, 1, state)
   1677    push('\x1b[1J', vt)
   1678    expect('erase 0..1,0..80\nerase 1..2,0..2')
   1679    cursor(1, 1, state)
   1680 
   1681    -- ED 2
   1682    reset(state, nil)
   1683    expect('erase 0..25,0..80')
   1684    cursor(0, 0, state)
   1685    push('\x1b[2;2H', vt)
   1686    cursor(1, 1, state)
   1687    push('\x1b[2J', vt)
   1688    expect('erase 0..25,0..80')
   1689    cursor(1, 1, state)
   1690 
   1691    -- ED 3
   1692    push('\x1b[3J', vt)
   1693    expect('sb_clear')
   1694 
   1695    -- SED
   1696    reset(state, nil)
   1697    expect('erase 0..25,0..80')
   1698    push('\x1b[5;5H', vt)
   1699    cursor(4, 4, state)
   1700    push('\x1b[?0J', vt)
   1701    expect('erase 4..5,4..80 selective\nerase 5..25,0..80 selective')
   1702    cursor(4, 4, state)
   1703    push('\x1b[?1J', vt)
   1704    expect('erase 0..4,0..80 selective\nerase 4..5,0..5 selective')
   1705    cursor(4, 4, state)
   1706    push('\x1b[?2J', vt)
   1707    expect('erase 0..25,0..80 selective')
   1708    cursor(4, 4, state)
   1709 
   1710    -- DECRQSS on DECSCA
   1711    push('\x1b[2"q', vt)
   1712    push('\x1bP$q"q\x1b\\', vt)
   1713    expect_output('\x1bP1$r2"q\x1b\\')
   1714 
   1715    state = wantstate(vt, { m = true, e = true, b = true })
   1716    expect('erase 0..25,0..80') -- TODO(dundargoc): strange, this should not be needed according to the original code
   1717 
   1718    -- ICH move+erase emuation
   1719    reset(state, nil)
   1720    expect('erase 0..25,0..80')
   1721    cursor(0, 0, state)
   1722    push('ACD', vt)
   1723    push('\x1b[2D', vt)
   1724    cursor(0, 1, state)
   1725    push('\x1b[@', vt)
   1726    expect('moverect 0..1,1..79 -> 0..1,2..80\nerase 0..1,1..2')
   1727    cursor(0, 1, state)
   1728    push('B', vt)
   1729    cursor(0, 2, state)
   1730    push('\x1b[3@', vt)
   1731    expect('moverect 0..1,2..77 -> 0..1,5..80\nerase 0..1,2..5')
   1732 
   1733    -- DCH move+erase emulation
   1734    reset(state, nil)
   1735    expect('erase 0..25,0..80')
   1736    cursor(0, 0, state)
   1737    push('ABBC', vt)
   1738    push('\x1b[3D', vt)
   1739    cursor(0, 1, state)
   1740    push('\x1b[P', vt)
   1741    expect('moverect 0..1,2..80 -> 0..1,1..79\nerase 0..1,79..80')
   1742    cursor(0, 1, state)
   1743    push('\x1b[3P', vt)
   1744    expect('moverect 0..1,4..80 -> 0..1,1..77\nerase 0..1,77..80')
   1745    cursor(0, 1, state)
   1746  end)
   1747 
   1748  itp('14state_encoding', function()
   1749    local vt = init()
   1750    vterm.vterm_set_utf8(vt, false)
   1751    local state = wantstate(vt, { g = true })
   1752 
   1753    -- Default
   1754    reset(state, nil)
   1755    push('#', vt)
   1756    expect('putglyph 23 1 0,0')
   1757 
   1758    -- Designate G0=DEC drawing
   1759    reset(state, nil)
   1760    push('\x1b(0', vt)
   1761    push('a', vt)
   1762    expect('putglyph 2592 1 0,0')
   1763 
   1764    -- Designate G1 + LS1
   1765    reset(state, nil)
   1766    push('\x1b)0', vt)
   1767    push('a', vt)
   1768    expect('putglyph 61 1 0,0')
   1769    push('\x0e', vt)
   1770    push('a', vt)
   1771    expect('putglyph 2592 1 0,1')
   1772    -- LS0
   1773    push('\x0f', vt)
   1774    push('a', vt)
   1775    expect('putglyph 61 1 0,2')
   1776 
   1777    -- Designate G2 + LS2
   1778    push('\x1b*0', vt)
   1779    push('a', vt)
   1780    expect('putglyph 61 1 0,3')
   1781    push('\x1bn', vt)
   1782    push('a', vt)
   1783    expect('putglyph 2592 1 0,4')
   1784    push('\x0f', vt)
   1785    push('a', vt)
   1786    expect('putglyph 61 1 0,5')
   1787 
   1788    -- Designate G3 + LS3
   1789    push('\x1b+0', vt)
   1790    push('a', vt)
   1791    expect('putglyph 61 1 0,6')
   1792    push('\x1bo', vt)
   1793    push('a', vt)
   1794    expect('putglyph 2592 1 0,7')
   1795    push('\x0f', vt)
   1796    push('a', vt)
   1797    expect('putglyph 61 1 0,8')
   1798 
   1799    -- SS2
   1800    push('a\x8eaa', vt)
   1801    expect('putglyph 61 1 0,9\nputglyph 2592 1 0,10\nputglyph 61 1 0,11')
   1802 
   1803    -- SS3
   1804    push('a\x8faa', vt)
   1805    expect('putglyph 61 1 0,12\nputglyph 2592 1 0,13\nputglyph 61 1 0,14')
   1806 
   1807    -- LS1R
   1808    reset(state, nil)
   1809    push('\x1b~', vt)
   1810    push('\xe1', vt)
   1811    expect('putglyph 61 1 0,0')
   1812    push('\x1b)0', vt)
   1813    push('\xe1', vt)
   1814    expect('putglyph 2592 1 0,1')
   1815 
   1816    -- LS2R
   1817    reset(state, nil)
   1818    push('\x1b}', vt)
   1819    push('\xe1', vt)
   1820    expect('putglyph 61 1 0,0')
   1821    push('\x1b*0', vt)
   1822    push('\xe1', vt)
   1823    expect('putglyph 2592 1 0,1')
   1824 
   1825    -- LS3R
   1826    reset(state, nil)
   1827    push('\x1b|', vt)
   1828    push('\xe1', vt)
   1829    expect('putglyph 61 1 0,0')
   1830    push('\x1b+0', vt)
   1831    push('\xe1', vt)
   1832    expect('putglyph 2592 1 0,1')
   1833 
   1834    vterm.vterm_set_utf8(vt, true)
   1835 
   1836    -- Mixed US-ASCII and UTF-8
   1837    -- U+0108 == c4 88
   1838    reset(state, nil)
   1839    push('\x1b(B', vt)
   1840    push('AB\xc4\x88D', vt)
   1841    expect('putglyph 41 1 0,0\nputglyph 42 1 0,1\nputglyph 108 1 0,2\nputglyph 44 1 0,3')
   1842 
   1843    -- Split UTF-8 after US-ASCII
   1844    reset(state, nil)
   1845    push('AB\xc4', vt)
   1846    expect('putglyph 41 1 0,0\nputglyph 42 1 0,1')
   1847    push('\x88D', vt)
   1848    expect('putglyph 108 1 0,2\nputglyph 44 1 0,3')
   1849  end)
   1850 
   1851  itp('15state_mode', function()
   1852    local vt = init()
   1853    local state = wantstate(vt, { g = true, m = true, e = true })
   1854 
   1855    -- Insert/Replace Mode
   1856    reset(state, nil)
   1857    expect('erase 0..25,0..80')
   1858    cursor(0, 0, state)
   1859    push('AC\x1b[DB', vt)
   1860    expect('putglyph 41 1 0,0\nputglyph 43 1 0,1\nputglyph 42 1 0,1')
   1861    push('\x1b[4h', vt)
   1862    push('\x1b[G', vt)
   1863    push('AC\x1b[DB', vt)
   1864    expect(
   1865      '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'
   1866    )
   1867 
   1868    -- Insert mode only happens once for UTF-8 combining
   1869    push('e', vt)
   1870    expect('moverect 0..1,2..79 -> 0..1,3..80\nerase 0..1,2..3\nputglyph 65 1 0,2')
   1871    push('\xCC\x81', vt)
   1872    expect('putglyph 65,301 1 0,2')
   1873 
   1874    -- Newline/Linefeed mode
   1875    reset(state, nil)
   1876    expect('erase 0..25,0..80')
   1877    cursor(0, 0, state)
   1878    push('\x1b[5G\n', vt)
   1879    cursor(1, 4, state)
   1880    push('\x1b[20h', vt)
   1881    push('\x1b[5G\n', vt)
   1882    cursor(2, 0, state)
   1883 
   1884    -- DEC origin mode
   1885    reset(state, nil)
   1886    expect('erase 0..25,0..80')
   1887    cursor(0, 0, state)
   1888    push('\x1b[5;15r', vt)
   1889    push('\x1b[H', vt)
   1890    cursor(0, 0, state)
   1891    push('\x1b[3;3H', vt)
   1892    cursor(2, 2, state)
   1893    push('\x1b[?6h', vt)
   1894    push('\x1b[H', vt)
   1895    cursor(4, 0, state)
   1896    push('\x1b[3;3H', vt)
   1897    cursor(6, 2, state)
   1898 
   1899    -- DECRQM on DECOM
   1900    push('\x1b[?6h', vt)
   1901    push('\x1b[?6$p', vt)
   1902    expect_output('\x1b[?6;1$y')
   1903    push('\x1b[?6l', vt)
   1904    push('\x1b[?6$p', vt)
   1905    expect_output('\x1b[?6;2$y')
   1906 
   1907    -- Origin mode with DECSLRM
   1908    push('\x1b[?6h', vt)
   1909    push('\x1b[?69h', vt)
   1910    push('\x1b[20;60s', vt)
   1911    push('\x1b[H', vt)
   1912    cursor(4, 19, state)
   1913 
   1914    push('\x1b[?69l', vt)
   1915 
   1916    -- Origin mode bounds cursor to scrolling region
   1917    push('\x1b[H', vt)
   1918    push('\x1b[10A', vt)
   1919    cursor(4, 0, state)
   1920    push('\x1b[20B', vt)
   1921    cursor(14, 0, state)
   1922 
   1923    -- Origin mode without scroll region
   1924    push('\x1b[?6l', vt)
   1925    push('\x1b[r\x1b[?6h', vt)
   1926    cursor(0, 0, state)
   1927  end)
   1928 
   1929  itp('16state_resize', function()
   1930    local vt = init()
   1931    local state = wantstate(vt, { g = true })
   1932 
   1933    -- Placement
   1934    reset(state, nil)
   1935    push('AB\x1b[79GCDE', vt)
   1936    expect(
   1937      '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'
   1938    )
   1939 
   1940    -- Resize
   1941    reset(state, nil)
   1942    resize(27, 85, vt)
   1943    push('AB\x1b[79GCDE', vt)
   1944    expect(
   1945      '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'
   1946    )
   1947    cursor(0, 81, state)
   1948 
   1949    -- Resize without reset
   1950    resize(28, 90, vt)
   1951    cursor(0, 81, state)
   1952    push('FGHI', vt)
   1953    expect('putglyph 46 1 0,81\nputglyph 47 1 0,82\nputglyph 48 1 0,83\nputglyph 49 1 0,84')
   1954    cursor(0, 85, state)
   1955 
   1956    -- Resize shrink moves cursor
   1957    resize(25, 80, vt)
   1958    cursor(0, 79, state)
   1959 
   1960    -- Resize grow doesn't cancel phantom
   1961    reset(state, nil)
   1962    push('\x1b[79GAB', vt)
   1963    expect('putglyph 41 1 0,78\nputglyph 42 1 0,79')
   1964    cursor(0, 79, state)
   1965    resize(30, 100, vt)
   1966    cursor(0, 80, state)
   1967    push('C', vt)
   1968    expect('putglyph 43 1 0,80')
   1969    cursor(0, 81, state)
   1970  end)
   1971 
   1972  itp('17state_mouse', function()
   1973    local vt = init()
   1974    local state = wantstate(vt, { p = true })
   1975 
   1976    -- DECRQM on with mouse off
   1977    push('\x1b[?1000$p', vt)
   1978    expect_output('\x1b[?1000;2$y')
   1979    push('\x1b[?1002$p', vt)
   1980    expect_output('\x1b[?1002;2$y')
   1981    push('\x1b[?1003$p', vt)
   1982    expect_output('\x1b[?1003;2$y')
   1983 
   1984    -- Mouse in simple button report mode
   1985    reset(state, nil)
   1986    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   1987    push('\x1b[?1000h', vt)
   1988    expect('settermprop 8 1')
   1989 
   1990    -- Press 1
   1991    mousemove(0, 0, vt)
   1992    mousebtn('d', 1, vt)
   1993    expect_output('\x1b[M\x20\x21\x21')
   1994 
   1995    -- Release 1
   1996    mousebtn('u', 1, vt)
   1997    expect_output('\x1b[M\x23\x21\x21')
   1998 
   1999    -- Ctrl-Press 1
   2000    mousebtn('d', 1, vt, { C = true })
   2001    expect_output('\x1b[M\x30\x21\x21')
   2002    mousebtn('u', 1, vt, { C = true })
   2003    expect_output('\x1b[M\x33\x21\x21')
   2004 
   2005    -- Button 2
   2006    mousebtn('d', 2, vt)
   2007    expect_output('\x1b[M\x21\x21\x21')
   2008    mousebtn('u', 2, vt)
   2009    expect_output('\x1b[M\x23\x21\x21')
   2010 
   2011    -- Position
   2012    mousemove(10, 20, vt)
   2013    mousebtn('d', 1, vt)
   2014    expect_output('\x1b[M\x20\x35\x2b')
   2015 
   2016    mousebtn('u', 1, vt)
   2017    expect_output('\x1b[M\x23\x35\x2b')
   2018    mousemove(10, 21, vt)
   2019    -- no output
   2020 
   2021    -- Wheel events
   2022    mousebtn('d', 4, vt)
   2023    expect_output('\x1b[M\x60\x36\x2b')
   2024    mousebtn('d', 4, vt)
   2025    expect_output('\x1b[M\x60\x36\x2b')
   2026    mousebtn('d', 5, vt)
   2027    expect_output('\x1b[M\x61\x36\x2b')
   2028    mousebtn('d', 6, vt)
   2029    expect_output('\x1b[M\x62\x36\x2b')
   2030    mousebtn('d', 7, vt)
   2031    expect_output('\x1b[M\x63\x36\x2b')
   2032 
   2033    -- DECRQM on mouse button mode
   2034    push('\x1b[?1000$p', vt)
   2035    expect_output('\x1b[?1000;1$y')
   2036    push('\x1b[?1002$p', vt)
   2037    expect_output('\x1b[?1002;2$y')
   2038    push('\x1b[?1003$p', vt)
   2039    expect_output('\x1b[?1003;2$y')
   2040 
   2041    -- Drag events
   2042    reset(state, nil)
   2043    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2044    push('\x1b[?1002h', vt)
   2045    expect('settermprop 8 2')
   2046 
   2047    mousemove(5, 5, vt)
   2048    mousebtn('d', 1, vt)
   2049    expect_output('\x1b[M\x20\x26\x26')
   2050    mousemove(5, 6, vt)
   2051    expect_output('\x1b[M\x40\x27\x26')
   2052    mousemove(6, 6, vt)
   2053    expect_output('\x1b[M\x40\x27\x27')
   2054    mousemove(6, 6, vt)
   2055    -- no output
   2056    mousebtn('u', 1, vt)
   2057    expect_output('\x1b[M\x23\x27\x27')
   2058    mousemove(6, 7, vt)
   2059    -- no output
   2060 
   2061    -- DECRQM on mouse drag mode
   2062    push('\x1b[?1000$p', vt)
   2063    expect_output('\x1b[?1000;2$y')
   2064    push('\x1b[?1002$p', vt)
   2065    expect_output('\x1b[?1002;1$y')
   2066    push('\x1b[?1003$p', vt)
   2067    expect_output('\x1b[?1003;2$y')
   2068 
   2069    -- Non-drag motion events
   2070    push('\x1b[?1003h', vt)
   2071    expect('settermprop 8 3')
   2072 
   2073    mousemove(6, 8, vt)
   2074    expect_output('\x1b[M\x43\x29\x27')
   2075 
   2076    -- DECRQM on mouse motion mode
   2077    push('\x1b[?1000$p', vt)
   2078    expect_output('\x1b[?1000;2$y')
   2079    push('\x1b[?1002$p', vt)
   2080    expect_output('\x1b[?1002;2$y')
   2081    push('\x1b[?1003$p', vt)
   2082    expect_output('\x1b[?1003;1$y')
   2083 
   2084    -- Bounds checking
   2085    mousemove(300, 300, vt)
   2086    expect_output('\x1b[M\x43\xff\xff')
   2087    mousebtn('d', 1, vt)
   2088    expect_output('\x1b[M\x20\xff\xff')
   2089    mousebtn('u', 1, vt)
   2090    expect_output('\x1b[M\x23\xff\xff')
   2091 
   2092    -- DECRQM on standard encoding mode
   2093    push('\x1b[?1005$p', vt)
   2094    expect_output('\x1b[?1005;2$y')
   2095    push('\x1b[?1006$p', vt)
   2096    expect_output('\x1b[?1006;2$y')
   2097    push('\x1b[?1015$p', vt)
   2098    expect_output('\x1b[?1015;2$y')
   2099 
   2100    -- UTF-8 extended encoding mode
   2101    -- 300 + 32 + 1 = 333 = U+014d = \xc5\x8d
   2102    push('\x1b[?1005h', vt)
   2103    mousebtn('d', 1, vt)
   2104    expect_output('\x1b[M\x20\xc5\x8d\xc5\x8d')
   2105    mousebtn('u', 1, vt)
   2106    expect_output('\x1b[M\x23\xc5\x8d\xc5\x8d')
   2107 
   2108    -- DECRQM on UTF-8 extended encoding mode
   2109    push('\x1b[?1005$p', vt)
   2110    expect_output('\x1b[?1005;1$y')
   2111    push('\x1b[?1006$p', vt)
   2112    expect_output('\x1b[?1006;2$y')
   2113    push('\x1b[?1015$p', vt)
   2114    expect_output('\x1b[?1015;2$y')
   2115 
   2116    -- SGR extended encoding mode
   2117    push('\x1b[?1006h', vt)
   2118    mousebtn('d', 1, vt)
   2119    expect_output('\x1b[<0;301;301M')
   2120    mousebtn('u', 1, vt)
   2121    expect_output('\x1b[<0;301;301m')
   2122 
   2123    -- Button 8 on SGR extended encoding mode
   2124    mousebtn('d', 8, vt)
   2125    expect_output('\x1b[<128;301;301M')
   2126    mousebtn('u', 8, vt)
   2127    expect_output('\x1b[<128;301;301m')
   2128 
   2129    -- Button 9 on SGR extended encoding mode
   2130    mousebtn('d', 9, vt)
   2131    expect_output('\x1b[<129;301;301M')
   2132    mousebtn('u', 9, vt)
   2133    expect_output('\x1b[<129;301;301m')
   2134 
   2135    -- DECRQM on SGR extended encoding mode
   2136    push('\x1b[?1005$p', vt)
   2137    expect_output('\x1b[?1005;2$y')
   2138    push('\x1b[?1006$p', vt)
   2139    expect_output('\x1b[?1006;1$y')
   2140    push('\x1b[?1015$p', vt)
   2141    expect_output('\x1b[?1015;2$y')
   2142 
   2143    -- rxvt extended encoding mode
   2144    push('\x1b[?1015h', vt)
   2145    mousebtn('d', 1, vt)
   2146    expect_output('\x1b[0;301;301M')
   2147    mousebtn('u', 1, vt)
   2148    expect_output('\x1b[3;301;301M')
   2149 
   2150    -- Button 8 on rxvt extended encoding mode
   2151    mousebtn('d', 8, vt)
   2152    expect_output('\x1b[128;301;301M')
   2153    mousebtn('u', 8, vt)
   2154    expect_output('\x1b[3;301;301M')
   2155 
   2156    -- Button 9 on rxvt extended encoding mode
   2157    mousebtn('d', 9, vt)
   2158    expect_output('\x1b[129;301;301M')
   2159    mousebtn('u', 9, vt)
   2160    expect_output('\x1b[3;301;301M')
   2161 
   2162    -- DECRQM on rxvt extended encoding mode
   2163    push('\x1b[?1005$p', vt)
   2164    expect_output('\x1b[?1005;2$y')
   2165    push('\x1b[?1006$p', vt)
   2166    expect_output('\x1b[?1006;2$y')
   2167    push('\x1b[?1015$p', vt)
   2168    expect_output('\x1b[?1015;1$y')
   2169 
   2170    -- Mouse disabled reports nothing
   2171    reset(state, nil)
   2172    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2173    mousemove(0, 0, vt)
   2174    mousebtn('d', 1, vt)
   2175    mousebtn('u', 1, vt)
   2176 
   2177    -- DECSM can set multiple modes at once
   2178    push('\x1b[?1002;1006h', vt)
   2179    expect('settermprop 8 2')
   2180    mousebtn('d', 1, vt)
   2181    expect_output('\x1b[<0;1;1M')
   2182  end)
   2183 
   2184  itp('18state_termprops', function()
   2185    local vt = init()
   2186    local state = wantstate(vt, { p = true })
   2187 
   2188    reset(state, nil)
   2189    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2190 
   2191    -- Cursor visibility
   2192    push('\x1b[?25h', vt)
   2193    expect('settermprop 1 true')
   2194    push('\x1b[?25$p', vt)
   2195    expect_output('\x1b[?25;1$y')
   2196    push('\x1b[?25l', vt)
   2197    expect('settermprop 1 false')
   2198    push('\x1b[?25$p', vt)
   2199    expect_output('\x1b[?25;2$y')
   2200 
   2201    -- Cursor blink
   2202    push('\x1b[?12h', vt)
   2203    expect('settermprop 2 true')
   2204    push('\x1b[?12$p', vt)
   2205    expect_output('\x1b[?12;1$y')
   2206    push('\x1b[?12l', vt)
   2207    expect('settermprop 2 false')
   2208    push('\x1b[?12$p', vt)
   2209    expect_output('\x1b[?12;2$y')
   2210 
   2211    -- Cursor shape
   2212    push('\x1b[3 q', vt)
   2213    expect('settermprop 2 true\nsettermprop 7 2')
   2214 
   2215    -- Title
   2216    push('\x1b]2;Here is my title\a', vt)
   2217    expect('settermprop 4 ["Here is my title"]')
   2218 
   2219    -- Title split write
   2220    push('\x1b]2;Here is', vt)
   2221    expect('settermprop 4 ["Here is"')
   2222    push(' another title\a', vt)
   2223    expect('settermprop 4 " another title"]')
   2224  end)
   2225 
   2226  itp('20state_wrapping', function()
   2227    local vt = init()
   2228    local state = wantstate(vt, { g = true, m = true })
   2229 
   2230    -- 79th Column
   2231    push('\x1b[75G', vt)
   2232    push(string.rep('A', 5), vt)
   2233    expect(
   2234      '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'
   2235    )
   2236    cursor(0, 79, state)
   2237 
   2238    -- 80th Column Phantom
   2239    push('A', vt)
   2240    expect('putglyph 41 1 0,79')
   2241    cursor(0, 79, state)
   2242 
   2243    -- Line Wraparound
   2244    push('B', vt)
   2245    expect('putglyph 42 1 1,0')
   2246    cursor(1, 1, state)
   2247 
   2248    -- Line Wraparound during combined write
   2249    push('\x1b[78G', vt)
   2250    push('BBBCC', vt)
   2251    expect(
   2252      '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'
   2253    )
   2254    cursor(2, 2, state)
   2255 
   2256    -- DEC Auto Wrap Mode
   2257    reset(state, nil)
   2258    push('\x1b[?7l', vt)
   2259    push('\x1b[75G', vt)
   2260    push(string.rep('D', 6), vt)
   2261    expect(
   2262      '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'
   2263    )
   2264    cursor(0, 79, state)
   2265    push('D', vt)
   2266    expect('putglyph 44 1 0,79')
   2267    cursor(0, 79, state)
   2268    push('\x1b[?7h', vt)
   2269 
   2270    -- 80th column causes linefeed on wraparound
   2271    push('\x1b[25;78HABC', vt)
   2272    expect('putglyph 41 1 24,77\nputglyph 42 1 24,78\nputglyph 43 1 24,79')
   2273    cursor(24, 79, state)
   2274    push('D', vt)
   2275    expect('moverect 1..25,0..80 -> 0..24,0..80\nputglyph 44 1 24,0')
   2276 
   2277    -- 80th column phantom linefeed phantom cancelled by explicit cursor move
   2278    push('\x1b[25;78HABC', vt)
   2279    expect('putglyph 41 1 24,77\nputglyph 42 1 24,78\nputglyph 43 1 24,79')
   2280    cursor(24, 79, state)
   2281    push('\x1b[25;1HD', vt)
   2282    expect('putglyph 44 1 24,0')
   2283  end)
   2284 
   2285  itp('21state_tabstops', function()
   2286    local vt = init()
   2287    local state = wantstate(vt, { g = true })
   2288 
   2289    -- Initial
   2290    reset(state, nil)
   2291    push('\tX', vt)
   2292    expect('putglyph 58 1 0,8')
   2293    push('\tX', vt)
   2294    expect('putglyph 58 1 0,16')
   2295    cursor(0, 17, state)
   2296 
   2297    -- HTS
   2298    push('\x1b[5G\x1bH', vt)
   2299    push('\x1b[G\tX', vt)
   2300    expect('putglyph 58 1 0,4')
   2301    cursor(0, 5, state)
   2302 
   2303    -- TBC 0
   2304    push('\x1b[9G\x1b[g', vt)
   2305    push('\x1b[G\tX\tX', vt)
   2306    expect('putglyph 58 1 0,4\nputglyph 58 1 0,16')
   2307    cursor(0, 17, state)
   2308 
   2309    -- TBC 3
   2310    push('\x1b[3g\x1b[50G\x1bH\x1b[G', vt)
   2311    cursor(0, 0, state)
   2312    push('\tX', vt)
   2313    expect('putglyph 58 1 0,49')
   2314    cursor(0, 50, state)
   2315 
   2316    -- Tabstops after resize
   2317    reset(state, nil)
   2318    resize(30, 100, vt)
   2319    -- Should be 100/8 = 12 tabstops
   2320    push('\tX', vt)
   2321    expect('putglyph 58 1 0,8')
   2322    push('\tX', vt)
   2323    expect('putglyph 58 1 0,16')
   2324    push('\tX', vt)
   2325    expect('putglyph 58 1 0,24')
   2326    push('\tX', vt)
   2327    expect('putglyph 58 1 0,32')
   2328    push('\tX', vt)
   2329    expect('putglyph 58 1 0,40')
   2330    push('\tX', vt)
   2331    expect('putglyph 58 1 0,48')
   2332    push('\tX', vt)
   2333    expect('putglyph 58 1 0,56')
   2334    push('\tX', vt)
   2335    expect('putglyph 58 1 0,64')
   2336    push('\tX', vt)
   2337    expect('putglyph 58 1 0,72')
   2338    push('\tX', vt)
   2339    expect('putglyph 58 1 0,80')
   2340    push('\tX', vt)
   2341    expect('putglyph 58 1 0,88')
   2342    push('\tX', vt)
   2343    expect('putglyph 58 1 0,96')
   2344    cursor(0, 97, state)
   2345  end)
   2346 
   2347  itp('22state_save', function()
   2348    local vt = init()
   2349    local state = wantstate(vt, { p = true })
   2350 
   2351    reset(state, nil)
   2352    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2353 
   2354    -- Set up state
   2355    push('\x1b[2;2H', vt)
   2356    cursor(1, 1, state)
   2357    push('\x1b[1m', vt)
   2358    pen('bold', true, state)
   2359 
   2360    -- Save
   2361    push('\x1b[?1048h', vt)
   2362 
   2363    -- Change state
   2364    push('\x1b[5;5H', vt)
   2365    cursor(4, 4, state)
   2366    push('\x1b[4 q', vt)
   2367    expect('settermprop 2 false\nsettermprop 7 2')
   2368    push('\x1b[22;4m', vt)
   2369    pen('bold', false, state)
   2370    pen('underline', 1, state)
   2371 
   2372    -- Restore
   2373    push('\x1b[?1048l', vt)
   2374    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2375    cursor(1, 1, state)
   2376    pen('bold', true, state)
   2377    pen('underline', 0, state)
   2378 
   2379    -- Save/restore using DECSC/DECRC
   2380    push('\x1b[2;2H\x1b7', vt)
   2381    cursor(1, 1, state)
   2382 
   2383    push('\x1b[5;5H', vt)
   2384    cursor(4, 4, state)
   2385    push('\x1b8', vt)
   2386    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2387    cursor(1, 1, state)
   2388 
   2389    -- Save twice, restore twice happens on both edge transitions
   2390    push('\x1b[2;10H\x1b[?1048h\x1b[6;10H\x1b[?1048h', vt)
   2391    push('\x1b[H', vt)
   2392    cursor(0, 0, state)
   2393    push('\x1b[?1048l', vt)
   2394    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2395    cursor(5, 9, state)
   2396    push('\x1b[H', vt)
   2397    cursor(0, 0, state)
   2398    push('\x1b[?1048l', vt)
   2399    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   2400    cursor(5, 9, state)
   2401  end)
   2402 
   2403  itp('25state_input', function()
   2404    local vt = init()
   2405    local state = wantstate(vt)
   2406 
   2407    -- Disambiguate escape codes enabled
   2408    push('\x1b[>1u', vt)
   2409 
   2410    -- Unmodified ASCII
   2411    inchar(0x41, vt)
   2412    expect_output('A')
   2413    inchar(0x61, vt)
   2414    expect_output('a')
   2415 
   2416    -- Ctrl modifier on ASCII letters
   2417    inchar(0x41, vt, { C = true })
   2418    expect_output('\x1b[97;6u')
   2419    inchar(0x61, vt, { C = true })
   2420    expect_output('\x1b[97;5u')
   2421 
   2422    -- Alt modifier on ASCII letters
   2423    inchar(0x41, vt, { A = true })
   2424    expect_output('\x1b[97;4u')
   2425    inchar(0x61, vt, { A = true })
   2426    expect_output('\x1b[97;3u')
   2427 
   2428    -- Ctrl-Alt modifier on ASCII letters
   2429    inchar(0x41, vt, { C = true, A = true })
   2430    expect_output('\x1b[97;8u')
   2431    inchar(0x61, vt, { C = true, A = true })
   2432    expect_output('\x1b[97;7u')
   2433 
   2434    -- Ctrl-I is disambiguated
   2435    inchar(0x49, vt)
   2436    expect_output('I')
   2437    inchar(0x69, vt)
   2438    expect_output('i')
   2439    inchar(0x49, vt, { C = true })
   2440    expect_output('\x1b[105;6u')
   2441    inchar(0x69, vt, { C = true })
   2442    expect_output('\x1b[105;5u')
   2443    inchar(0x49, vt, { A = true })
   2444    expect_output('\x1b[105;4u')
   2445    inchar(0x69, vt, { A = true })
   2446    expect_output('\x1b[105;3u')
   2447    inchar(0x49, vt, { A = true, C = true })
   2448    expect_output('\x1b[105;8u')
   2449    inchar(0x69, vt, { A = true, C = true })
   2450    expect_output('\x1b[105;7u')
   2451 
   2452    -- Ctrl+Digits
   2453    for i = 0, 9 do
   2454      local c = 0x30 + i
   2455      inchar(c, vt)
   2456      expect_output(tostring(i))
   2457      inchar(c, vt, { C = true })
   2458      expect_output(string.format('\x1b[%d;5u', c))
   2459      inchar(c, vt, { C = true, S = true })
   2460      expect_output(string.format('\x1b[%d;6u', c))
   2461      inchar(c, vt, { C = true, A = true })
   2462      expect_output(string.format('\x1b[%d;7u', c))
   2463      inchar(c, vt, { C = true, A = true, S = true })
   2464      expect_output(string.format('\x1b[%d;8u', c))
   2465    end
   2466 
   2467    -- Special handling of Space
   2468    inchar(0x20, vt)
   2469    expect_output(' ')
   2470    inchar(0x20, vt, { S = true })
   2471    expect_output('\x1b[32;2u')
   2472    inchar(0x20, vt, { C = true })
   2473    expect_output('\x1b[32;5u')
   2474    inchar(0x20, vt, { C = true, S = true })
   2475    expect_output('\x1b[32;6u')
   2476    inchar(0x20, vt, { A = true })
   2477    expect_output('\x1b[32;3u')
   2478    inchar(0x20, vt, { S = true, A = true })
   2479    expect_output('\x1b[32;4u')
   2480    inchar(0x20, vt, { C = true, A = true })
   2481    expect_output('\x1b[32;7u')
   2482    inchar(0x20, vt, { S = true, C = true, A = true })
   2483    expect_output('\x1b[32;8u')
   2484 
   2485    -- Cursor keys in reset (cursor) mode
   2486    inkey('up', vt)
   2487    expect_output('\x1b[A')
   2488    inkey('up', vt, { S = true })
   2489    expect_output('\x1b[1;2A')
   2490    inkey('up', vt, { C = true })
   2491    expect_output('\x1b[1;5A')
   2492    inkey('up', vt, { S = true, C = true })
   2493    expect_output('\x1b[1;6A')
   2494    inkey('up', vt, { A = true })
   2495    expect_output('\x1b[1;3A')
   2496    inkey('up', vt, { S = true, A = true })
   2497    expect_output('\x1b[1;4A')
   2498    inkey('up', vt, { C = true, A = true })
   2499    expect_output('\x1b[1;7A')
   2500    inkey('up', vt, { S = true, C = true, A = true })
   2501    expect_output('\x1b[1;8A')
   2502 
   2503    -- Cursor keys in application mode
   2504    push('\x1b[?1h', vt)
   2505    -- Plain "Up" should be SS3 A now
   2506    inkey('up', vt)
   2507    expect_output('\x1bOA')
   2508    -- Modified keys should still use CSI
   2509    inkey('up', vt, { S = true })
   2510    expect_output('\x1b[1;2A')
   2511    inkey('up', vt, { C = true })
   2512    expect_output('\x1b[1;5A')
   2513 
   2514    -- Tab
   2515    inkey('tab', vt)
   2516    expect_output('\x09')
   2517    inkey('tab', vt, { S = true })
   2518    expect_output('\x1b[9;2u')
   2519    inkey('tab', vt, { C = true })
   2520    expect_output('\x1b[9;5u')
   2521    inkey('tab', vt, { A = true })
   2522    expect_output('\x1b[9;3u')
   2523    inkey('tab', vt, { C = true, A = true })
   2524    expect_output('\x1b[9;7u')
   2525 
   2526    -- Backspace
   2527    inkey('bs', vt)
   2528    expect_output('\x7f')
   2529    inkey('bs', vt, { S = true })
   2530    expect_output('\x1b[127;2u')
   2531    inkey('bs', vt, { C = true })
   2532    expect_output('\x1b[127;5u')
   2533    inkey('bs', vt, { A = true })
   2534    expect_output('\x1b[127;3u')
   2535    inkey('bs', vt, { C = true, A = true })
   2536    expect_output('\x1b[127;7u')
   2537 
   2538    -- DEL
   2539    inkey('del', vt)
   2540    expect_output('\x1b[3~')
   2541    inkey('del', vt, { S = true })
   2542    expect_output('\x1b[3;2~')
   2543    inkey('del', vt, { C = true })
   2544    expect_output('\x1b[3;5~')
   2545    inkey('del', vt, { A = true })
   2546    expect_output('\x1b[3;3~')
   2547    inkey('del', vt, { C = true, A = true })
   2548    expect_output('\x1b[3;7~')
   2549 
   2550    -- ESC
   2551    inkey('esc', vt)
   2552    expect_output('\x1b[27;1u')
   2553    inkey('esc', vt, { S = true })
   2554    expect_output('\x1b[27;2u')
   2555    inkey('esc', vt, { C = true })
   2556    expect_output('\x1b[27;5u')
   2557    inkey('esc', vt, { A = true })
   2558    expect_output('\x1b[27;3u')
   2559    inkey('esc', vt, { C = true, A = true })
   2560    expect_output('\x1b[27;7u')
   2561 
   2562    -- Enter in linefeed mode
   2563    inkey('enter', vt)
   2564    expect_output('\x0d')
   2565    inkey('enter', vt, { S = true })
   2566    expect_output('\x1b[13;2u')
   2567    inkey('enter', vt, { C = true })
   2568    expect_output('\x1b[13;5u')
   2569    inkey('enter', vt, { A = true })
   2570    expect_output('\x1b[13;3u')
   2571    inkey('enter', vt, { C = true, A = true })
   2572    expect_output('\x1b[13;7u')
   2573 
   2574    -- Enter in newline mode
   2575    push('\x1b[20h', vt)
   2576    inkey('enter', vt)
   2577    expect_output('\x0d\x0a')
   2578 
   2579    -- Unmodified F1 is SS3 P
   2580    inkey('f1', vt)
   2581    expect_output('\x1bOP')
   2582 
   2583    -- Modified F1 is CSI P
   2584    inkey('f1', vt, { S = true })
   2585    expect_output('\x1b[1;2P')
   2586    inkey('f1', vt, { A = true })
   2587    expect_output('\x1b[1;3P')
   2588    inkey('f1', vt, { C = true })
   2589    expect_output('\x1b[1;5P')
   2590 
   2591    -- Keypad in DECKPNM
   2592    inkey('kp0', vt)
   2593    expect_output('\x1b[57399;1u')
   2594 
   2595    -- Keypad in DECKPAM
   2596    push('\x1b=', vt)
   2597    inkey('kp0', vt)
   2598    expect_output('\x1bOp')
   2599 
   2600    -- Bracketed paste mode off
   2601    vterm.vterm_keyboard_start_paste(vt)
   2602    vterm.vterm_keyboard_end_paste(vt)
   2603 
   2604    -- Bracketed paste mode on
   2605    push('\x1b[?2004h', vt)
   2606    vterm.vterm_keyboard_start_paste(vt)
   2607    expect_output('\x1b[200~')
   2608    vterm.vterm_keyboard_end_paste(vt)
   2609    expect_output('\x1b[201~')
   2610 
   2611    -- Focus reporting disabled
   2612    vterm.vterm_state_focus_in(state)
   2613    vterm.vterm_state_focus_out(state)
   2614 
   2615    -- Focus reporting enabled
   2616    state = wantstate(vt, { p = true })
   2617    push('\x1b[?1004h', vt)
   2618    expect('settermprop 9 true')
   2619    vterm.vterm_state_focus_in(state)
   2620    expect_output('\x1b[I')
   2621    vterm.vterm_state_focus_out(state)
   2622    expect_output('\x1b[O')
   2623 
   2624    -- Disambiguate escape codes disabled
   2625    push('\x1b[<u', vt)
   2626 
   2627    -- Unmodified ASCII
   2628    inchar(0x41, vt)
   2629    expect_output('A')
   2630    inchar(0x61, vt)
   2631    expect_output('a')
   2632 
   2633    -- Ctrl modifier on ASCII letters
   2634    inchar(0x41, vt, { C = true })
   2635    expect_output('\x01')
   2636    inchar(0x61, vt, { C = true })
   2637    expect_output('\x01')
   2638 
   2639    -- Alt modifier on ASCII letters
   2640    inchar(0x41, vt, { A = true })
   2641    expect_output('\x1bA')
   2642    inchar(0x61, vt, { A = true })
   2643    expect_output('\x1ba')
   2644 
   2645    -- Ctrl-Alt modifier on ASCII letters
   2646    inchar(0x41, vt, { C = true, A = true })
   2647    expect_output('\x1b\x01')
   2648    inchar(0x61, vt, { C = true, A = true })
   2649    expect_output('\x1b\x01')
   2650 
   2651    -- Ctrl-I is ambiguous
   2652    inchar(0x49, vt)
   2653    expect_output('I')
   2654    inchar(0x69, vt)
   2655    expect_output('i')
   2656    inchar(0x49, vt, { C = true })
   2657    expect_output('\x09')
   2658    inchar(0x69, vt, { C = true })
   2659    expect_output('\x09')
   2660    inchar(0x49, vt, { A = true })
   2661    expect_output('\x1bI')
   2662    inchar(0x69, vt, { A = true })
   2663    expect_output('\x1bi')
   2664    inchar(0x49, vt, { A = true, C = true })
   2665    expect_output('\x1b\x09')
   2666    inchar(0x69, vt, { A = true, C = true })
   2667    expect_output('\x1b\x09')
   2668 
   2669    -- Ctrl+Digits
   2670    inchar(0x30, vt, { C = true })
   2671    expect_output('0')
   2672    inchar(0x31, vt, { C = true })
   2673    expect_output('1')
   2674    inchar(0x32, vt, { C = true })
   2675    expect_output('\x00')
   2676    inchar(0x33, vt, { C = true })
   2677    expect_output('\x1b')
   2678    inchar(0x34, vt, { C = true })
   2679    expect_output('\x1c')
   2680    inchar(0x35, vt, { C = true })
   2681    expect_output('\x1d')
   2682    inchar(0x36, vt, { C = true })
   2683    expect_output('\x1e')
   2684    inchar(0x37, vt, { C = true })
   2685    expect_output('\x1f')
   2686    inchar(0x38, vt, { C = true })
   2687    expect_output('\x7f')
   2688    inchar(0x39, vt, { C = true })
   2689    expect_output('9')
   2690 
   2691    -- Ctrl+/
   2692    inchar(0x2F, vt, { C = true })
   2693    expect_output('\x1f')
   2694  end)
   2695 
   2696  itp('26state_query', function()
   2697    local vt = init()
   2698    local state = wantstate(vt)
   2699 
   2700    -- DA
   2701    reset(state, nil)
   2702    push('\x1b[c', vt)
   2703    expect_output('\x1b[?61;22;52c')
   2704 
   2705    -- XTVERSION
   2706    reset(state, nil)
   2707    push('\x1b[>q', vt)
   2708    expect_output('\x1bP>|libvterm(0.3)\x1b\\')
   2709 
   2710    -- DSR
   2711    reset(state, nil)
   2712    push('\x1b[5n', vt)
   2713    expect_output('\x1b[0n')
   2714 
   2715    -- CPR
   2716    push('\x1b[6n', vt)
   2717    expect_output('\x1b[1;1R')
   2718    push('\x1b[10;10H\x1b[6n', vt)
   2719    expect_output('\x1b[10;10R')
   2720 
   2721    -- DECCPR
   2722    push('\x1b[?6n', vt)
   2723    expect_output('\x1b[?10;10R')
   2724 
   2725    -- DECRQSS on DECSCUSR
   2726    push('\x1b[3 q', vt)
   2727    push('\x1bP$q q\x1b\\', vt)
   2728    expect_output('\x1bP1$r3 q\x1b\\')
   2729 
   2730    -- DECRQSS on SGR
   2731    push('\x1b[1;5;7m', vt)
   2732    push('\x1bP$qm\x1b\\', vt)
   2733    expect_output('\x1bP1$r1;5;7m\x1b\\')
   2734 
   2735    -- DECRQSS on SGR ANSI colours
   2736    push('\x1b[0;31;42m', vt)
   2737    push('\x1bP$qm\x1b\\', vt)
   2738    expect_output('\x1bP1$r31;42m\x1b\\')
   2739 
   2740    -- DECRQSS on SGR ANSI hi-bright colours
   2741    push('\x1b[0;93;104m', vt)
   2742    push('\x1bP$qm\x1b\\', vt)
   2743    expect_output('\x1bP1$r93;104m\x1b\\')
   2744 
   2745    -- DECRQSS on SGR 256-palette colours
   2746    push('\x1b[0;38:5:56;48:5:78m', vt)
   2747    push('\x1bP$qm\x1b\\', vt)
   2748    expect_output('\x1bP1$r38:5:56;48:5:78m\x1b\\')
   2749 
   2750    -- DECRQSS on SGR RGB8 colours
   2751    push('\x1b[0;38:2:24:68:112;48:2:13:57:101m', vt)
   2752    push('\x1bP$qm\x1b\\', vt)
   2753    expect_output('\x1bP1$r38:2:24:68:112;48:2:13:57:101m\x1b\\')
   2754 
   2755    -- S8C1T on DSR
   2756    push('\x1b G', vt)
   2757    push('\x1b[5n', vt)
   2758    expect_output('\x9b0n')
   2759    push('\x1b F', vt)
   2760  end)
   2761 
   2762  itp('27state_reset', function()
   2763    local vt = init()
   2764    local state = wantstate(vt)
   2765 
   2766    reset(state, nil)
   2767 
   2768    -- RIS homes cursor
   2769    push('\x1b[5;5H', vt)
   2770    cursor(4, 4, state)
   2771    state = wantstate(vt, { m = true })
   2772    push('\x1bc', vt)
   2773    cursor(0, 0, state)
   2774    wantstate(vt)
   2775 
   2776    -- RIS cancels scrolling region
   2777    push('\x1b[5;10r', vt)
   2778    wantstate(vt, { s = true })
   2779    push('\x1bc\x1b[25H\n', vt)
   2780    expect('scrollrect 0..25,0..80 => +1,+0')
   2781    wantstate(vt)
   2782 
   2783    -- RIS erases screen
   2784    push('ABCDE', vt)
   2785    state = wantstate(vt, { e = true })
   2786    push('\x1bc', vt)
   2787    expect('erase 0..25,0..80')
   2788    wantstate(vt)
   2789 
   2790    -- RIS clears tabstops
   2791    push('\x1b[5G\x1bH\x1b[G\t', vt)
   2792    cursor(0, 4, state)
   2793    push('\x1bc\t', vt)
   2794    cursor(0, 8, state)
   2795  end)
   2796 
   2797  itp('28state_dbl_wh', function()
   2798    local vt = init()
   2799    local state = wantstate(vt, { g = true })
   2800 
   2801    -- Single Width, Single Height
   2802    reset(state, nil)
   2803    push('\x1b#5', vt)
   2804    push('Hello', vt)
   2805    expect(
   2806      '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'
   2807    )
   2808 
   2809    -- Double Width, Single Height
   2810    reset(state, nil)
   2811    push('\x1b#6', vt)
   2812    push('Hello', vt)
   2813    expect(
   2814      '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'
   2815    )
   2816    cursor(0, 5, state)
   2817    push('\x1b[40GAB', vt)
   2818    expect('putglyph 41 1 0,39 dwl\nputglyph 42 1 1,0')
   2819    cursor(1, 1, state)
   2820 
   2821    -- Double Height
   2822    reset(state, nil)
   2823    push('\x1b#3', vt)
   2824    push('Hello', vt)
   2825    expect(
   2826      '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'
   2827    )
   2828    cursor(0, 5, state)
   2829    push('\r\n\x1b#4', vt)
   2830    push('Hello', vt)
   2831    expect(
   2832      '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'
   2833    )
   2834    cursor(1, 5, state)
   2835 
   2836    -- Double Width scrolling
   2837    reset(state, nil)
   2838    push('\x1b[20H\x1b#6ABC', vt)
   2839    expect('putglyph 41 1 19,0 dwl\nputglyph 42 1 19,1 dwl\nputglyph 43 1 19,2 dwl')
   2840    push('\x1b[25H\n', vt)
   2841    push('\x1b[19;4HDE', vt)
   2842    expect('putglyph 44 1 18,3 dwl\nputglyph 45 1 18,4 dwl')
   2843    push('\x1b[H\x1bM', vt)
   2844    push('\x1b[20;6HFG', vt)
   2845    expect('putglyph 46 1 19,5 dwl\nputglyph 47 1 19,6 dwl')
   2846  end)
   2847 
   2848  itp('29state_fallback', function()
   2849    local vt = init()
   2850    local state = wantstate(vt, { f = true })
   2851    reset(state, nil)
   2852 
   2853    -- Unrecognised control
   2854    push('\x03', vt)
   2855    expect('control 03')
   2856 
   2857    -- Unrecognised CSI
   2858    push('\x1b[?15;2z', vt)
   2859    expect('csi 7a L=3f 15,2')
   2860 
   2861    -- Unrecognised OSC
   2862    push('\x1b]27;Something\x1b\\', vt)
   2863    expect('osc [27;Something]')
   2864 
   2865    -- Unrecognised DCS
   2866    push('\x1bPz123\x1b\\', vt)
   2867    expect('dcs [z123]')
   2868 
   2869    -- Unrecognised APC
   2870    push('\x1b_z123\x1b\\', vt)
   2871    expect('apc [z123]')
   2872 
   2873    -- Unrecognised PM
   2874    push('\x1b^z123\x1b\\', vt)
   2875    expect('pm [z123]')
   2876 
   2877    -- Unrecognised SOS
   2878    push('\x1bXz123\x1b\\', vt)
   2879    expect('sos [z123]')
   2880  end)
   2881 
   2882  itp('30state_pen', function()
   2883    local vt = init()
   2884    local state = wantstate(vt)
   2885 
   2886    -- Reset
   2887    push('\x1b[m', vt)
   2888    pen('bold', false, state)
   2889    pen('underline', 0, state)
   2890    pen('italic', false, state)
   2891    pen('blink', false, state)
   2892    pen('reverse', false, state)
   2893    pen('font', 0, state)
   2894    -- TODO(dundargoc): fix
   2895    -- ?pen foreground = rgb(240,240,240,is_default_fg)
   2896    -- ?pen background = rgb(0,0,0,is_default_bg)
   2897 
   2898    -- Bold
   2899    push('\x1b[1m', vt)
   2900    pen('bold', true, state)
   2901    push('\x1b[22m', vt)
   2902    pen('bold', false, state)
   2903    push('\x1b[1m\x1b[m', vt)
   2904    pen('bold', false, state)
   2905 
   2906    -- Underline
   2907    push('\x1b[4m', vt)
   2908    pen('underline', 1, state)
   2909    push('\x1b[21m', vt)
   2910    pen('underline', 2, state)
   2911    push('\x1b[24m', vt)
   2912    pen('underline', 0, state)
   2913    push('\x1b[4m\x1b[4:0m', vt)
   2914    pen('underline', 0, state)
   2915    push('\x1b[4:1m', vt)
   2916    pen('underline', 1, state)
   2917    push('\x1b[4:2m', vt)
   2918    pen('underline', 2, state)
   2919    push('\x1b[4:3m', vt)
   2920    pen('underline', 3, state)
   2921    push('\x1b[4m\x1b[m', vt)
   2922    pen('underline', 0, state)
   2923 
   2924    -- Italic
   2925    push('\x1b[3m', vt)
   2926    pen('italic', true, state)
   2927    push('\x1b[23m', vt)
   2928    pen('italic', false, state)
   2929    push('\x1b[3m\x1b[m', vt)
   2930    pen('italic', false, state)
   2931 
   2932    -- Blink
   2933    push('\x1b[5m', vt)
   2934    pen('blink', true, state)
   2935    push('\x1b[25m', vt)
   2936    pen('blink', false, state)
   2937    push('\x1b[5m\x1b[m', vt)
   2938    pen('blink', false, state)
   2939 
   2940    -- Reverse
   2941    push('\x1b[7m', vt)
   2942    pen('reverse', true, state)
   2943    push('\x1b[27m', vt)
   2944    pen('reverse', false, state)
   2945    push('\x1b[7m\x1b[m', vt)
   2946    pen('reverse', false, state)
   2947 
   2948    -- Font Selection
   2949    push('\x1b[11m', vt)
   2950    pen('font', 1, state)
   2951    push('\x1b[19m', vt)
   2952    pen('font', 9, state)
   2953    push('\x1b[10m', vt)
   2954    pen('font', 0, state)
   2955    push('\x1b[11m\x1b[m', vt)
   2956    pen('font', 0, state)
   2957 
   2958    -- Dim
   2959    push('\x1b[2m', vt)
   2960    pen('dim', true, state)
   2961    push('\x1b[22m', vt)
   2962    pen('dim', false, state)
   2963    push('\x1b[2m\x1b[m', vt)
   2964    pen('dim', false, state)
   2965 
   2966    -- Bold and Dim interaction (SGR 22 turns off both)
   2967    push('\x1b[1;2m', vt)
   2968    pen('bold', true, state)
   2969    pen('dim', true, state)
   2970    push('\x1b[22m', vt)
   2971    pen('bold', false, state)
   2972    pen('dim', false, state)
   2973 
   2974    -- Overline
   2975    push('\x1b[53m', vt)
   2976    pen('overline', true, state)
   2977    push('\x1b[55m', vt)
   2978    pen('overline', false, state)
   2979    push('\x1b[53m\x1b[m', vt)
   2980    pen('overline', false, state)
   2981 
   2982    -- TODO(dundargoc): fix
   2983    -- Foreground
   2984    -- push "\x1b[31m"
   2985    --   ?pen foreground = idx(1)
   2986    -- push "\x1b[32m"
   2987    --   ?pen foreground = idx(2)
   2988    -- push "\x1b[34m"
   2989    --   ?pen foreground = idx(4)
   2990    -- push "\x1b[91m"
   2991    --   ?pen foreground = idx(9)
   2992    -- push "\x1b[38:2:10:20:30m"
   2993    --   ?pen foreground = rgb(10,20,30)
   2994    -- push "\x1b[38:5:1m"
   2995    --   ?pen foreground = idx(1)
   2996    -- push "\x1b[39m"
   2997    --   ?pen foreground = rgb(240,240,240,is_default_fg)
   2998    --
   2999    -- Background
   3000    -- push "\x1b[41m"
   3001    --   ?pen background = idx(1)
   3002    -- push "\x1b[42m"
   3003    --   ?pen background = idx(2)
   3004    -- push "\x1b[44m"
   3005    --   ?pen background = idx(4)
   3006    -- push "\x1b[101m"
   3007    --   ?pen background = idx(9)
   3008    -- push "\x1b[48:2:10:20:30m"
   3009    --   ?pen background = rgb(10,20,30)
   3010    -- push "\x1b[48:5:1m"
   3011    --   ?pen background = idx(1)
   3012    -- push "\x1b[49m"
   3013    --   ?pen background = rgb(0,0,0,is_default_bg)
   3014    --
   3015    -- Bold+ANSI colour == highbright
   3016    -- push "\x1b[m\x1b[1;37m"
   3017    --   ?pen bold = on
   3018    --   ?pen foreground = idx(15)
   3019    -- push "\x1b[m\x1b[37;1m"
   3020    --   ?pen bold = on
   3021    --   ?pen foreground = idx(15)
   3022    --
   3023    -- Super/Subscript
   3024    -- push "\x1b[73m"
   3025    --   ?pen small = on
   3026    --   ?pen baseline = raise
   3027    -- push "\x1b[74m"
   3028    --   ?pen small = on
   3029    --   ?pen baseline = lower
   3030    -- push "\x1b[75m"
   3031    --   ?pen small = off
   3032    --   ?pen baseline = normal
   3033    --
   3034    -- DECSTR resets pen attributes
   3035    -- push "\x1b[1;4m"
   3036    --   ?pen bold = on
   3037    --   ?pen underline = 1
   3038    -- push "\x1b[!p"
   3039    --   ?pen bold = off
   3040    --   ?pen underline = 0
   3041  end)
   3042 
   3043  itp('31state_rep', function()
   3044    local vt = init()
   3045    local state = wantstate(vt, { g = true })
   3046 
   3047    -- REP no argument
   3048    reset(state, nil)
   3049    push('a\x1b[b', vt)
   3050    expect('putglyph 61 1 0,0\nputglyph 61 1 0,1')
   3051 
   3052    -- REP zero (zero should be interpreted as one)
   3053    reset(state, nil)
   3054    push('a\x1b[0b', vt)
   3055    expect('putglyph 61 1 0,0\nputglyph 61 1 0,1')
   3056 
   3057    -- REP lowercase a times two
   3058    reset(state, nil)
   3059    push('a\x1b[2b', vt)
   3060    expect('putglyph 61 1 0,0\nputglyph 61 1 0,1\nputglyph 61 1 0,2')
   3061 
   3062    -- REP with UTF-8 1 char
   3063    -- U+00E9 = C3 A9  name: LATIN SMALL LETTER E WITH ACUTE
   3064    reset(state, nil)
   3065    push('\xC3\xA9\x1b[b', vt)
   3066    expect('putglyph e9 1 0,0\nputglyph e9 1 0,1')
   3067 
   3068    -- REP with UTF-8 wide char
   3069    -- U+00E9 = C3 A9  name: LATIN SMALL LETTER E WITH ACUTE
   3070    reset(state, nil)
   3071    push('\xEF\xBC\x90\x1b[b', vt)
   3072    expect('putglyph ff10 2 0,0\nputglyph ff10 2 0,2')
   3073 
   3074    -- REP with UTF-8 combining character
   3075    reset(state, nil)
   3076    push('e\xCC\x81\x1b[b', vt)
   3077    expect('putglyph 65,301 1 0,0\nputglyph 65,301 1 0,1')
   3078 
   3079    -- REP till end of line
   3080    reset(state, nil)
   3081    push('a\x1b[1000bb', vt)
   3082    expect(
   3083      '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'
   3084    )
   3085  end)
   3086 
   3087  itp('32state_flow', function()
   3088    local vt = init()
   3089    local state = wantstate(vt)
   3090 
   3091    -- Many of these test cases inspired by
   3092    -- https://blueprints.launchpad.net/libvterm/+spec/reflow-cases
   3093 
   3094    -- Spillover text marks continuation on second line
   3095    reset(state, nil)
   3096    push(string.rep('A', 100), vt)
   3097    push('\r\n', vt)
   3098    lineinfo(0, {}, state)
   3099    lineinfo(1, { cont = true }, state)
   3100 
   3101    -- CRLF in column 80 does not mark continuation
   3102    reset(state, nil)
   3103    push(string.rep('B', 80), vt)
   3104    push('\r\n', vt)
   3105    push(string.rep('B', 20), vt)
   3106    push('\r\n', vt)
   3107    lineinfo(0, {}, state)
   3108    lineinfo(1, {}, state)
   3109 
   3110    -- EL cancels continuation of following line
   3111    reset(state, nil)
   3112    push(string.rep('D', 100), vt)
   3113    lineinfo(1, { cont = true }, state)
   3114    push('\x1bM\x1b[79G\x1b[K', vt)
   3115    lineinfo(1, {}, state)
   3116  end)
   3117 
   3118  itp('40state_selection', function()
   3119    local vt = init()
   3120    wantstate(vt)
   3121 
   3122    -- Set clipboard; final chunk len 4
   3123    push('\x1b]52;c;SGVsbG8s\x1b\\', vt)
   3124    expect('selection-set mask=0001 [Hello,]')
   3125 
   3126    -- Set clipboard; final chunk len 3
   3127    push('\x1b]52;c;SGVsbG8sIHc=\x1b\\', vt)
   3128    expect('selection-set mask=0001 [Hello, w]')
   3129 
   3130    -- Set clipboard; final chunk len 2
   3131    push('\x1b]52;c;SGVsbG8sIHdvcmxkCg==\x1b\\', vt)
   3132    expect('selection-set mask=0001 [Hello, world\n]')
   3133 
   3134    -- Set clipboard; split between chunks
   3135    push('\x1b]52;c;SGVs', vt)
   3136    expect('selection-set mask=0001 [Hel')
   3137    push('bG8s\x1b\\', vt)
   3138    expect('selection-set mask=0001 lo,]')
   3139 
   3140    -- Set clipboard; split within chunk
   3141    push('\x1b]52;c;SGVsbG', vt)
   3142    expect('selection-set mask=0001 [Hel')
   3143    push('8s\x1b\\', vt)
   3144    expect('selection-set mask=0001 lo,]')
   3145 
   3146    -- Set clipboard; empty first chunk
   3147    push('\x1b]52;c;', vt)
   3148    push('SGVsbG8s\x1b\\', vt)
   3149    expect('selection-set mask=0001 [Hello,]')
   3150 
   3151    -- Set clipboard; empty final chunk
   3152    push('\x1b]52;c;SGVsbG8s', vt)
   3153    expect('selection-set mask=0001 [Hello,')
   3154    push('\x1b\\', vt)
   3155    expect('selection-set mask=0001 ]')
   3156 
   3157    -- Set clipboard; longer than buffer
   3158    push('\x1b]52;c;' .. string.rep('LS0t', 10) .. '\x1b\\', vt)
   3159    expect('selection-set mask=0001 [---------------\nselection-set mask=0001 ---------------]')
   3160 
   3161    -- Clear clipboard
   3162    push('\x1b]52;c;\x1b\\', vt)
   3163    expect('selection-set mask=0001 []')
   3164 
   3165    -- Set invalid data clears and ignores
   3166    push('\x1b]52;c;SGVs*SGVsbG8s\x1b\\', vt)
   3167    expect('selection-set mask=0001 []')
   3168 
   3169    -- Query clipboard
   3170    push('\x1b]52;c;?\x1b\\', vt)
   3171    expect('selection-query mask=0001')
   3172 
   3173    -- TODO(dundargoc): fix
   3174    -- Send clipboard; final chunk len 4
   3175    -- SELECTION 1 ["Hello,"]
   3176    --   output "\x1b]52;c;"
   3177    --   output "SGVsbG8s"
   3178    --   output "\x1b\\"
   3179    --
   3180    -- Send clipboard; final chunk len 3
   3181    -- SELECTION 1 ["Hello, w"]
   3182    --   output "\x1b]52;c;"
   3183    --   output "SGVsbG8s"
   3184    --   output "IHc=\x1b\\"
   3185    --
   3186    -- Send clipboard; final chunk len 2
   3187    -- SELECTION 1 ["Hello, world\n"]
   3188    --   output "\x1b]52;c;"
   3189    --   output "SGVsbG8sIHdvcmxk"
   3190    --   output "Cg==\x1b\\"
   3191    --
   3192    -- Send clipboard; split between chunks
   3193    -- SELECTION 1 ["Hel"
   3194    --   output "\x1b]52;c;"
   3195    --   output "SGVs"
   3196    -- SELECTION 1  "lo,"]
   3197    --   output "bG8s"
   3198    --   output "\x1b\\"
   3199    --
   3200    -- Send clipboard; split within chunk
   3201    -- SELECTION 1 ["Hello"
   3202    --   output "\x1b]52;c;"
   3203    --   output "SGVs"
   3204    -- SELECTION 1 ","]
   3205    --   output "bG8s"
   3206    --   output "\x1b\\"
   3207  end)
   3208 
   3209  itp('60screen_ascii', function()
   3210    local vt = init()
   3211    local screen = wantscreen(vt, { a = true, c = true })
   3212 
   3213    -- Get
   3214    reset(nil, screen)
   3215    push('ABC', vt)
   3216    expect('movecursor 0,3')
   3217    screen_chars(0, 0, 1, 3, 'ABC', screen)
   3218    screen_chars(0, 0, 1, 80, 'ABC', screen)
   3219    screen_text(0, 0, 1, 3, '41,42,43', screen)
   3220    screen_text(0, 0, 1, 80, '41,42,43', screen)
   3221    screen_cell(0, 0, '{41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3222    screen_cell(0, 1, '{42} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3223    screen_cell(0, 2, '{43} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3224    screen_row(0, 'ABC', screen)
   3225    screen_eol(0, 0, 0, screen)
   3226    screen_eol(0, 2, 0, screen)
   3227    screen_eol(0, 3, 1, screen)
   3228    push('\x1b[H', vt)
   3229    expect('movecursor 0,0')
   3230    screen_row(0, 'ABC', screen)
   3231    screen_text(0, 0, 1, 80, '41,42,43', screen)
   3232    push('E', vt)
   3233    expect('movecursor 0,1')
   3234    screen_row(0, 'EBC', screen)
   3235    screen_text(0, 0, 1, 80, '45,42,43', screen)
   3236 
   3237    screen = wantscreen(vt, { a = true })
   3238 
   3239    -- Erase
   3240    reset(nil, screen)
   3241    push('ABCDE\x1b[H\x1b[K', vt)
   3242    -- TODO(dundargoc): fix
   3243    -- screen_row(0, '', screen)
   3244    screen_text(0, 0, 1, 80, '', screen)
   3245 
   3246    -- Copycell
   3247    reset(nil, screen)
   3248    push('ABC\x1b[H\x1b[@', vt)
   3249    push('1', vt)
   3250    screen_row(0, '1ABC', screen)
   3251 
   3252    reset(nil, screen)
   3253    push('ABC\x1b[H\x1b[P', vt)
   3254    screen_chars(0, 0, 1, 1, 'B', screen)
   3255    screen_chars(0, 1, 1, 2, 'C', screen)
   3256    screen_chars(0, 0, 1, 80, 'BC', screen)
   3257 
   3258    -- Space padding
   3259    reset(nil, screen)
   3260    push('Hello\x1b[CWorld', vt)
   3261    screen_row(0, 'Hello World', screen)
   3262    screen_text(0, 0, 1, 80, '48,65,6c,6c,6f,20,57,6f,72,6c,64', screen)
   3263 
   3264    -- Linefeed padding
   3265    reset(nil, screen)
   3266    push('Hello\r\nWorld', vt)
   3267    screen_chars(0, 0, 2, 80, 'Hello\nWorld', screen)
   3268    screen_text(0, 0, 2, 80, '48,65,6c,6c,6f,0a,57,6f,72,6c,64', screen)
   3269 
   3270    -- Altscreen
   3271    reset(nil, screen)
   3272    push('P', vt)
   3273    screen_row(0, 'P', screen)
   3274    -- TODO(dundargoc): fix
   3275    -- push('\x1b[?1049h', vt)
   3276    -- screen_row(0, '', screen)
   3277    -- push('\x1b[2K\x1b[HA', vt)
   3278    -- screen_row(0, 'A', screen)
   3279    -- push('\x1b[?1049l', vt)
   3280    -- screen_row(0, 'P', screen)
   3281  end)
   3282 
   3283  itp('61screen_unicode', function()
   3284    local vt = init()
   3285    local screen = wantscreen(vt)
   3286 
   3287    -- Single width UTF-8
   3288    -- U+00C1 = C3 81  name: LATIN CAPITAL LETTER A WITH ACUTE
   3289    -- U+00E9 = C3 A9  name: LATIN SMALL LETTER E WITH ACUTE
   3290    reset(nil, screen)
   3291    push('\xC3\x81\xC3\xA9', vt)
   3292    screen_row(0, 'Áé', screen)
   3293    screen_text(0, 0, 1, 80, 'c3,81,c3,a9', screen)
   3294    screen_cell(0, 0, '{c1} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3295 
   3296    -- Wide char
   3297    -- U+FF10 = EF BC 90  name: FULLWIDTH DIGIT ZERO
   3298    reset(nil, screen)
   3299    push('0123\x1b[H', vt)
   3300    push('\xEF\xBC\x90', vt)
   3301    screen_row(0, '023', screen)
   3302    screen_text(0, 0, 1, 80, 'ef,bc,90,32,33', screen)
   3303    screen_cell(0, 0, '{ff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3304 
   3305    -- Combining char
   3306    -- U+0301 = CC 81  name: COMBINING ACUTE
   3307    reset(nil, screen)
   3308    push('0123\x1b[H', vt)
   3309    push('e\xCC\x81', vt)
   3310    screen_row(0, 'é123', screen)
   3311    screen_text(0, 0, 1, 80, '65,cc,81,31,32,33', screen)
   3312    screen_cell(0, 0, '{65,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3313 
   3314    -- 10 combining accents should not crash
   3315    reset(nil, screen)
   3316    push('e\xCC\x81\xCC\x82\xCC\x83\xCC\x84\xCC\x85\xCC\x86\xCC\x87\xCC\x88\xCC\x89\xCC\x8A', vt)
   3317    screen_cell(
   3318      0,
   3319      0,
   3320      '{65,301,302,303,304,305,306,307,308,309,30a} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
   3321      screen
   3322    )
   3323 
   3324    -- 40 combining accents in two split writes of 20 should not crash
   3325    reset(nil, screen)
   3326    push(
   3327      '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',
   3328      vt
   3329    )
   3330    push(
   3331      '\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',
   3332      vt
   3333    )
   3334    screen_cell(
   3335      0,
   3336      0,
   3337      '{65,301,301,301,301,301,301,301,301,301,301,301,301,301,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
   3338      screen
   3339    )
   3340 
   3341    -- Outputting CJK doublewidth in 80th column should wraparound to next line and not crash"
   3342    reset(nil, screen)
   3343    push('\x1b[80G\xEF\xBC\x90', vt)
   3344    screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3345    screen_cell(1, 0, '{ff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3346 
   3347    -- Outputting emoji with ZWJ and variant selectors
   3348    reset(nil, screen)
   3349    push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt)
   3350 
   3351    -- stylua: ignore start
   3352    screen_cell(0, 0, '{1f3f3,fe0f,200d,1f308} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3353    screen_cell(0, 2, '{1f3f3,fe0f,200d,26a7,fe0f} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3354    screen_cell(0, 4, '{1f3f4,200d,2620,fe0f} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3355    -- stylua: ignore end
   3356  end)
   3357 
   3358  pending('62screen_damage', function() end)
   3359 
   3360  itp('63screen_resize', function()
   3361    local vt = init()
   3362    local state = wantstate(vt)
   3363    local screen = wantscreen(vt)
   3364 
   3365    -- Resize wider preserves cells
   3366    reset(state, screen)
   3367    resize(25, 80, vt)
   3368    push('AB\r\nCD', vt)
   3369    screen_chars(0, 0, 1, 80, 'AB', screen)
   3370    screen_chars(1, 0, 2, 80, 'CD', screen)
   3371    resize(25, 100, vt)
   3372    screen_chars(0, 0, 1, 100, 'AB', screen)
   3373    screen_chars(1, 0, 2, 100, 'CD', screen)
   3374 
   3375    -- Resize wider allows print in new area
   3376    reset(state, screen)
   3377    resize(25, 80, vt)
   3378    push('AB\x1b[79GCD', vt)
   3379    screen_chars(0, 0, 1, 2, 'AB', screen)
   3380    screen_chars(0, 78, 1, 80, 'CD', screen)
   3381    resize(25, 100, vt)
   3382    screen_chars(0, 0, 1, 2, 'AB', screen)
   3383    screen_chars(0, 78, 1, 80, 'CD', screen)
   3384    push('E', vt)
   3385    screen_chars(0, 78, 1, 81, 'CDE', screen)
   3386 
   3387    -- Resize shorter with blanks just truncates
   3388    reset(state, screen)
   3389    resize(25, 80, vt)
   3390    push('Top\x1b[10HLine 10', vt)
   3391    screen_row(0, 'Top', screen)
   3392    screen_row(9, 'Line 10', screen)
   3393    cursor(9, 7, state)
   3394    resize(20, 80, vt)
   3395    screen_row(0, 'Top', screen)
   3396    screen_row(9, 'Line 10', screen)
   3397    cursor(9, 7, state)
   3398 
   3399    -- Resize shorter with content must scroll
   3400    reset(state, screen)
   3401    resize(25, 80, vt)
   3402    push('Top\x1b[25HLine 25\x1b[15H', vt)
   3403    screen_row(0, 'Top', screen)
   3404    screen_row(24, 'Line 25', screen)
   3405    cursor(14, 0, state)
   3406    screen = wantscreen(vt, { b = true })
   3407    resize(20, 80, vt)
   3408    expect(
   3409      'sb_pushline 80 = 54 6f 70\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 ='
   3410    )
   3411    -- TODO(dundargoc): fix or remove
   3412    -- screen_row( 0  , "",screen)
   3413    screen_row(19, 'Line 25', screen)
   3414    cursor(9, 0, state)
   3415 
   3416    -- Resize shorter does not lose line with cursor
   3417    -- See also https://github.com/neovim/libvterm/commit/1b745d29d45623aa8d22a7b9288c7b0e331c7088
   3418    reset(state, screen)
   3419    wantscreen(vt)
   3420    resize(25, 80, vt)
   3421    screen = wantscreen(vt, { b = true })
   3422    push('\x1b[24HLine 24\r\nLine 25\r\n', vt)
   3423    expect('sb_pushline 80 =')
   3424    screen_row(23, 'Line 25', screen)
   3425    cursor(24, 0, state)
   3426    resize(24, 80, vt)
   3427    expect('sb_pushline 80 =')
   3428    screen_row(22, 'Line 25', screen)
   3429    cursor(23, 0, state)
   3430 
   3431    -- Resize shorter does not send the cursor to a negative row
   3432    -- See also https://github.com/vim/vim/pull/6141
   3433    reset(state, screen)
   3434    wantscreen(vt)
   3435    resize(25, 80, vt)
   3436    screen = wantscreen(vt, { b = true })
   3437    push('\x1b[24HLine 24\r\nLine 25\x1b[H', vt)
   3438    cursor(0, 0, state)
   3439    resize(20, 80, vt)
   3440    expect(
   3441      'sb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 ='
   3442    )
   3443    cursor(0, 0, state)
   3444 
   3445    -- Resize taller attempts to pop scrollback
   3446    reset(state, screen)
   3447    screen = wantscreen(vt)
   3448    resize(25, 80, vt)
   3449    push('Line 1\x1b[25HBottom\x1b[15H', vt)
   3450    screen_row(0, 'Line 1', screen)
   3451    screen_row(24, 'Bottom', screen)
   3452    cursor(14, 0, state)
   3453    screen = wantscreen(vt, { b = true })
   3454    resize(30, 80, vt)
   3455    expect('sb_popline 80\nsb_popline 80\nsb_popline 80\nsb_popline 80\nsb_popline 80')
   3456    screen_row(0, 'ABCDE', screen)
   3457    screen_row(5, 'Line 1', screen)
   3458    screen_row(29, 'Bottom', screen)
   3459    cursor(19, 0, state)
   3460    screen = wantscreen(vt)
   3461 
   3462    -- Resize can operate on altscreen
   3463    reset(state, screen)
   3464    screen = wantscreen(vt, { a = true })
   3465    resize(25, 80, vt)
   3466    push('Main screen\x1b[?1049h\x1b[HAlt screen', vt)
   3467    resize(30, 80, vt)
   3468    screen_row(0, 'Alt screen', screen)
   3469    push('\x1b[?1049l', vt)
   3470    screen_row(0, 'Main screen', screen)
   3471  end)
   3472 
   3473  itp('64screen_pen', function()
   3474    local vt = init()
   3475    local screen = wantscreen(vt)
   3476 
   3477    reset(nil, screen)
   3478 
   3479    -- Plain
   3480    push('A', vt)
   3481    screen_cell(0, 0, '{41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3482 
   3483    -- Bold
   3484    push('\x1b[1mB', vt)
   3485    screen_cell(0, 1, '{42} width=1 attrs={B} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3486 
   3487    -- Italic
   3488    push('\x1b[3mC', vt)
   3489    screen_cell(0, 2, '{43} width=1 attrs={BI} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3490 
   3491    -- Underline
   3492    push('\x1b[4mD', vt)
   3493    screen_cell(0, 3, '{44} width=1 attrs={BU1I} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3494 
   3495    -- Reset
   3496    push('\x1b[mE', vt)
   3497    screen_cell(0, 4, '{45} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3498 
   3499    -- Font
   3500    push('\x1b[11mF\x1b[m', vt)
   3501    screen_cell(0, 5, '{46} width=1 attrs={F1} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3502 
   3503    -- Foreground
   3504    push('\x1b[31mG\x1b[m', vt)
   3505    screen_cell(0, 6, '{47} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)', screen)
   3506 
   3507    -- Background
   3508    push('\x1b[42mH\x1b[m', vt)
   3509    screen_cell(0, 7, '{48} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,224,0)', screen)
   3510 
   3511    -- Super/subscript
   3512    push('x\x1b[74m0\x1b[73m2\x1b[m', vt)
   3513    screen_cell(0, 8, '{78} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3514    screen_cell(0, 9, '{30} width=1 attrs={S_} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3515    screen_cell(0, 10, '{32} width=1 attrs={S^} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3516 
   3517    -- Dim
   3518    push('\x1b[2mI\x1b[m', vt)
   3519    screen_cell(0, 11, '{49} width=1 attrs={D} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3520 
   3521    -- Overline
   3522    push('\x1b[53mJ\x1b[m', vt)
   3523    screen_cell(0, 12, '{4a} width=1 attrs={O} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3524 
   3525    -- EL sets only colours to end of line, not other attrs
   3526    push('\x1b[H\x1b[7;33;44m\x1b[K', vt)
   3527    screen_cell(0, 0, '{} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
   3528    screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
   3529 
   3530    -- DECSCNM xors reverse for entire screen
   3531    push('R\x1b[?5h', vt)
   3532    screen_cell(0, 0, '{52} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
   3533    screen_cell(1, 0, '{} width=1 attrs={R} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3534    push('\x1b[?5$p', vt)
   3535    expect_output('\x1b[?5;1$y')
   3536    push('\x1b[?5l', vt)
   3537    screen_cell(0, 0, '{52} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)
   3538    screen_cell(1, 0, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3539    -- TODO(dundargoc): fix
   3540    -- push('\x1b[?5$p')
   3541    -- expect_output('\x1b[?5;2$y')
   3542 
   3543    -- Set default colours
   3544    reset(nil, screen)
   3545    push('ABC\x1b[31mDEF\x1b[m', vt)
   3546    screen_cell(0, 0, '{41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3547    screen_cell(0, 3, '{44} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)', screen)
   3548    -- TODO(dundargoc): fix
   3549    -- SETDEFAULTCOL rgb(252,253,254)
   3550    --   ?screen_cell 0,0  = {41} width=1 attrs={} fg=rgb(252,253,254) bg=rgb(0,0,0)
   3551    --   ?screen_cell 0,3  = {44} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)
   3552    -- SETDEFAULTCOL rgb(250,250,250) rgb(10,20,30)
   3553    --   ?screen_cell 0,0  = {41} width=1 attrs={} fg=rgb(250,250,250) bg=rgb(10,20,30)
   3554    --   ?screen_cell 0,3  = {44} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(10,20,30)
   3555  end)
   3556 
   3557  itp('65screen_protect', function()
   3558    local vt = init()
   3559    local screen = wantscreen(vt)
   3560 
   3561    -- Selective erase
   3562    reset(nil, screen)
   3563    push('A\x1b[1"qB\x1b["qC', vt)
   3564    screen_row(0, 'ABC', screen)
   3565    push('\x1b[G\x1b[?J', vt)
   3566    screen_row(0, ' B', screen)
   3567 
   3568    -- Non-selective erase
   3569    reset(nil, screen)
   3570    push('A\x1b[1"qB\x1b["qC', vt)
   3571    screen_row(0, 'ABC', screen)
   3572    -- TODO(dundargoc): fix
   3573    -- push('\x1b[G\x1b[J', vt)
   3574    -- screen_row(0, '', screen)
   3575  end)
   3576 
   3577  itp('66screen_extent', function()
   3578    local vt = init()
   3579    local screen = wantscreen(vt)
   3580 
   3581    -- Bold extent
   3582    reset(nil, screen)
   3583    push('AB\x1b[1mCD\x1b[mE', vt)
   3584    screen_attrs_extent(0, 0, '0,0-1,1', screen)
   3585    screen_attrs_extent(0, 1, '0,0-1,1', screen)
   3586    screen_attrs_extent(0, 2, '0,2-1,3', screen)
   3587    screen_attrs_extent(0, 3, '0,2-1,3', screen)
   3588    screen_attrs_extent(0, 4, '0,4-1,79', screen)
   3589  end)
   3590 
   3591  itp('67screen_dbl_wh', function()
   3592    local vt = init()
   3593    local screen = wantscreen(vt)
   3594 
   3595    reset(nil, screen)
   3596 
   3597    -- Single Width, Single Height
   3598    reset(nil, screen)
   3599    push('\x1b#5', vt)
   3600    push('abcde', vt)
   3601    screen_cell(0, 0, '{61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3602 
   3603    -- Double Width, Single Height
   3604    reset(nil, screen)
   3605    push('\x1b#6', vt)
   3606    push('abcde', vt)
   3607    screen_cell(0, 0, '{61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3608 
   3609    -- Double Height
   3610    reset(nil, screen)
   3611    push('\x1b#3', vt)
   3612    push('abcde', vt)
   3613    push('\r\n\x1b#4', vt)
   3614    push('abcde', vt)
   3615    screen_cell(0, 0, '{61} width=1 attrs={} dwl dhl-top fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3616    screen_cell(
   3617      1,
   3618      0,
   3619      '{61} width=1 attrs={} dwl dhl-bottom fg=rgb(240,240,240) bg=rgb(0,0,0)',
   3620      screen
   3621    )
   3622 
   3623    -- Late change
   3624    reset(nil, screen)
   3625    push('abcde', vt)
   3626    screen_cell(0, 0, '{61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3627    push('\x1b#6', vt)
   3628    screen_cell(0, 0, '{61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3629 
   3630    -- DWL doesn't spill over on scroll
   3631    reset(nil, screen)
   3632    push('\x1b[25H\x1b#6Final\r\n', vt)
   3633    screen_cell(23, 0, '{46} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3634    screen_cell(24, 0, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
   3635  end)
   3636 
   3637  itp('68screen_termprops', function()
   3638    local vt = init()
   3639    local screen = wantscreen(vt, { p = true })
   3640 
   3641    reset(nil, screen)
   3642    expect('settermprop 1 true\nsettermprop 2 true\nsettermprop 7 1')
   3643 
   3644    -- Cursor visibility
   3645    push('\x1b[?25h', vt)
   3646    expect('settermprop 1 true')
   3647    push('\x1b[?25l', vt)
   3648    expect('settermprop 1 false')
   3649 
   3650    -- Title
   3651    push('\x1b]2;Here is my title\a', vt)
   3652    expect('settermprop 4 ["Here is my title"]')
   3653  end)
   3654 
   3655  itp('69screen_pushline', function()
   3656    local vt = init()
   3657    -- Run these tests on a much smaller default screen, so debug output is nowhere near as noisy
   3658    resize(5, 10, vt)
   3659    local state = wantstate(vt)
   3660    local screen = wantscreen(vt, { r = true })
   3661    reset(state, screen)
   3662 
   3663    -- Resize wider reflows wide lines
   3664    reset(state, screen)
   3665    push(string.rep('A', 12), vt)
   3666    screen_row(0, 'AAAAAAAAAA', screen, vt.cols)
   3667    screen_row(1, 'AA', screen, vt.cols)
   3668    lineinfo(1, { cont = true }, state)
   3669    cursor(1, 2, state)
   3670    resize(5, 15, vt)
   3671    screen_row(0, 'AAAAAAAAAAAA', screen, vt.cols)
   3672    -- TODO(dundargoc): fix
   3673    -- screen_row(1, '', screen, vt.cols)
   3674    lineinfo(1, {}, state)
   3675    cursor(0, 12, state)
   3676    resize(5, 20, vt)
   3677    screen_row(0, 'AAAAAAAAAAAA', screen, vt.cols)
   3678    -- TODO(dundargoc): fix
   3679    -- screen_row( 1 ,'',screen, vt.cols)
   3680    lineinfo(1, {}, state)
   3681    cursor(0, 12, state)
   3682 
   3683    -- Resize narrower can create continuation lines
   3684    reset(state, screen)
   3685    resize(5, 10, vt)
   3686    push('ABCDEFGHI', vt)
   3687    screen_row(0, 'ABCDEFGHI', screen, vt.cols)
   3688    -- TODO(dundargoc): fix
   3689    -- screen_row( 1 , "",screen, vt.cols)
   3690    lineinfo(1, {}, state)
   3691    cursor(0, 9, state)
   3692    resize(5, 8, vt)
   3693    -- TODO(dundargoc): fix
   3694    -- screen_row( 0 , "ABCDEFGH",screen,vt.cols)
   3695    screen_row(1, 'I', screen, vt.cols)
   3696    lineinfo(1, { cont = true }, state)
   3697    cursor(1, 1, state)
   3698    resize(5, 6, vt)
   3699    screen_row(0, 'ABCDEF', screen, vt.cols)
   3700    screen_row(1, 'GHI', screen, vt.cols)
   3701    lineinfo(1, { cont = true }, state)
   3702    cursor(1, 3, state)
   3703 
   3704    -- Shell wrapped prompt behaviour
   3705    reset(state, screen)
   3706    resize(5, 10, vt)
   3707    push('PROMPT GOES HERE\r\n> \r\n\r\nPROMPT GOES HERE\r\n> ', vt)
   3708    screen_row(0, '> ', screen, vt.cols)
   3709    -- TODO(dundargoc): fix
   3710    -- screen_row( 1 , "",screen,vt.cols)
   3711    screen_row(2, 'PROMPT GOE', screen, vt.cols)
   3712    screen_row(3, 'S HERE', screen, vt.cols)
   3713    lineinfo(3, { cont = true }, state)
   3714    screen_row(4, '> ', screen, vt.cols)
   3715    cursor(4, 2, state)
   3716    resize(5, 11, vt)
   3717    screen_row(0, '> ', screen, vt.cols)
   3718    -- TODO(dundargoc): fix
   3719    -- screen_row( 1 , "",screen,vt.cols)
   3720    screen_row(2, 'PROMPT GOES', screen, vt.cols)
   3721    screen_row(3, ' HERE', screen, vt.cols)
   3722    lineinfo(3, { cont = true }, state)
   3723    screen_row(4, '> ', screen, vt.cols)
   3724    cursor(4, 2, state)
   3725    resize(5, 12, vt)
   3726    screen_row(0, '> ', screen, vt.cols)
   3727    -- TODO(dundargoc): fix
   3728    -- screen_row( 1 , "",screen,vt.cols)
   3729    screen_row(2, 'PROMPT GOES ', screen, vt.cols)
   3730    screen_row(3, 'HERE', screen, vt.cols)
   3731    lineinfo(3, { cont = true }, state)
   3732    screen_row(4, '> ', screen, vt.cols)
   3733    cursor(4, 2, state)
   3734    resize(5, 16, vt)
   3735    screen_row(0, '> ', screen, vt.cols)
   3736    -- TODO(dundargoc): fix
   3737    -- screen_row( 1 , "",screen,vt.cols)
   3738    -- screen_row( 2 , "PROMPT GOES HERE",screen,vt.cols)
   3739    lineinfo(3, {}, state)
   3740    screen_row(3, '> ', screen, vt.cols)
   3741    cursor(3, 2, state)
   3742 
   3743    -- Cursor goes missing
   3744    -- For more context: https://github.com/neovim/neovim/pull/21124
   3745    reset(state, screen)
   3746    resize(5, 5, vt)
   3747    resize(3, 1, vt)
   3748    push('\x1b[2;1Habc\r\n\x1b[H', vt)
   3749    resize(1, 1, vt)
   3750    cursor(0, 0, state)
   3751  end)
   3752 
   3753  pending('90vttest_01-movement-1', function() end)
   3754  pending('90vttest_01-movement-2', function() end)
   3755 
   3756  itp('90vttest_01-movement-3', function()
   3757    -- Test of cursor-control characters inside ESC sequences
   3758    local vt = init()
   3759    local state = wantstate(vt)
   3760    local screen = wantscreen(vt)
   3761 
   3762    reset(state, screen)
   3763 
   3764    push('A B C D E F G H I', vt)
   3765    push('\x0d\x0a', vt)
   3766    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)
   3767    push('\x0d\x0a', vt)
   3768    push(
   3769      'A \x1b[\x0d2CB\x1b[\x0d4CC\x1b[\x0d6CD\x1b[\x0d8CE\x1b[\x0d10CF\x1b[\x0d12CG\x1b[\x0d14CH\x1b[\x0d16CI',
   3770      vt
   3771    )
   3772    push('\x0d\x0a', vt)
   3773    push(
   3774      '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',
   3775      vt
   3776    )
   3777 
   3778    -- Output
   3779 
   3780    for i = 0, 2 do
   3781      screen_row(i, 'A B C D E F G H I', screen)
   3782    end
   3783    screen_row(3, 'A B C D E F G H I ', screen)
   3784 
   3785    cursor(3, 18, state)
   3786  end)
   3787 
   3788  itp('90vttest_01-movement-4', function()
   3789    -- Test of leading zeroes in ESC sequences
   3790    local vt = init()
   3791    local screen = wantscreen(vt)
   3792 
   3793    reset(nil, screen)
   3794 
   3795    push('\x1b[00000000004;000000001HT', vt)
   3796    push('\x1b[00000000004;000000002Hh', vt)
   3797    push('\x1b[00000000004;000000003Hi', vt)
   3798    push('\x1b[00000000004;000000004Hs', vt)
   3799    push('\x1b[00000000004;000000005H ', vt)
   3800    push('\x1b[00000000004;000000006Hi', vt)
   3801    push('\x1b[00000000004;000000007Hs', vt)
   3802    push('\x1b[00000000004;000000008H ', vt)
   3803    push('\x1b[00000000004;000000009Ha', vt)
   3804    push('\x1b[00000000004;0000000010H ', vt)
   3805    push('\x1b[00000000004;0000000011Hc', vt)
   3806    push('\x1b[00000000004;0000000012Ho', vt)
   3807    push('\x1b[00000000004;0000000013Hr', vt)
   3808    push('\x1b[00000000004;0000000014Hr', vt)
   3809    push('\x1b[00000000004;0000000015He', vt)
   3810    push('\x1b[00000000004;0000000016Hc', vt)
   3811    push('\x1b[00000000004;0000000017Ht', vt)
   3812    push('\x1b[00000000004;0000000018H ', vt)
   3813    push('\x1b[00000000004;0000000019Hs', vt)
   3814    push('\x1b[00000000004;0000000020He', vt)
   3815    push('\x1b[00000000004;0000000021Hn', vt)
   3816    push('\x1b[00000000004;0000000022Ht', vt)
   3817    push('\x1b[00000000004;0000000023He', vt)
   3818    push('\x1b[00000000004;0000000024Hn', vt)
   3819    push('\x1b[00000000004;0000000025Hc', vt)
   3820    push('\x1b[00000000004;0000000026He', vt)
   3821 
   3822    -- Output
   3823 
   3824    screen_row(3, 'This is a correct sentence', screen)
   3825  end)
   3826 
   3827  pending('90vttest_02-screen-1', function() end)
   3828  pending('90vttest_02-screen-2', function() end)
   3829 
   3830  itp('90vttest_02-screen-3', function()
   3831    -- Origin mode
   3832    local vt = init()
   3833    local screen = wantscreen(vt)
   3834 
   3835    reset(nil, screen)
   3836 
   3837    push('\x1b[?6h', vt)
   3838    push('\x1b[23;24r', vt)
   3839    push('\n', vt)
   3840    push('Bottom', vt)
   3841    push('\x1b[1;1H', vt)
   3842    push('Above', vt)
   3843 
   3844    -- Output
   3845    screen_row(22, 'Above', screen)
   3846    screen_row(23, 'Bottom', screen)
   3847  end)
   3848 
   3849  itp('90vttest_02-screen-4', function()
   3850    -- Origin mode (2)
   3851    local vt = init()
   3852    local screen = wantscreen(vt)
   3853 
   3854    reset(nil, screen)
   3855 
   3856    push('\x1b[?6l', vt)
   3857    push('\x1b[23;24r', vt)
   3858    push('\x1b[24;1H', vt)
   3859    push('Bottom', vt)
   3860    push('\x1b[1;1H', vt)
   3861    push('Top', vt)
   3862 
   3863    -- Output
   3864    screen_row(23, 'Bottom', screen)
   3865    screen_row(0, 'Top', screen)
   3866  end)
   3867 
   3868  itp('Mouse reporting should not break by idempotent DECSM 1002', function()
   3869    -- Regression test for https://bugs.launchpad.net/libvterm/+bug/1640917
   3870    -- Related: https://github.com/neovim/neovim/issues/5583
   3871    local vt = init()
   3872    wantstate(vt, {})
   3873 
   3874    push('\x1b[?1002h', vt)
   3875    mousemove(0, 0, vt)
   3876    mousebtn('d', 1, vt)
   3877    expect_output('\x1b[M\x20\x21\x21')
   3878    mousemove(1, 0, vt)
   3879    expect_output('\x1b[M\x40\x21\x22')
   3880    push('\x1b[?1002h', vt)
   3881    mousemove(2, 0, vt)
   3882    expect_output('\x1b[M\x40\x21\x23')
   3883  end)
   3884 end)