mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-15 06:40:24 +01:00
0d4e10f5ab
Most notably: - Reflow is now working. Terminal size will fill the window and cols/rows will be resized/calculated based on window and font size. - Added support for different fonts (i.e. bold, italic, bolditalic). - Enabled blinking characters. - Adde more tests and caught a few subtle bugs. - Removed renderer code (which was part of xterm.js) and just doing naive rendering in terminal.gd, but it seems to perform a lot faster. Still not working completely: - vim (some weirdness going on). - vttest (more weirdness). Todo: - Fix the above. - Draw the cursor! - Improve performance. Performance is still not great. The terminal becomes unusable when running `yes` or `cmatrix -r`.
362 lines
14 KiB
GDScript
362 lines
14 KiB
GDScript
# Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
# Ported to GDScript by the GodotXterm authors.
|
|
# License MIT
|
|
extends "res://addons/gut/test.gd"
|
|
|
|
|
|
const Buffer = preload("res://addons/godot_xterm/buffer/buffer.gd")
|
|
const CircularList = preload("res://addons/godot_xterm/circular_list.gd")
|
|
const CellData = preload("res://addons/godot_xterm/buffer/cell_data.gd")
|
|
const AttributeData = preload("res://addons/godot_xterm/buffer/attribute_data.gd")
|
|
const TestUtils = preload("res://test/test_utils.gd")
|
|
|
|
|
|
class BaseBufferTest:
|
|
extends "res://addons/gut/test.gd"
|
|
|
|
|
|
const INIT_COLS = 80
|
|
const INIT_ROWS = 24
|
|
const INIT_SCROLLBACK = 1000
|
|
|
|
var options_service
|
|
var buffer_service
|
|
var buffer
|
|
|
|
|
|
func before_each():
|
|
options_service = TestUtils.MockOptionsService.new({'scrollback': INIT_SCROLLBACK})
|
|
buffer_service = TestUtils.MockBufferService.new(INIT_COLS, INIT_ROWS)
|
|
buffer = Buffer.new(true, options_service, buffer_service)
|
|
|
|
|
|
class TestInit:
|
|
extends BaseBufferTest
|
|
|
|
|
|
func test_buffer_lines():
|
|
assert_eq(buffer.lines.get_script(), CircularList)
|
|
assert_eq(buffer.lines.max_length, buffer_service.rows + INIT_SCROLLBACK)
|
|
|
|
|
|
func test_buffer_scroll_bottom():
|
|
assert_eq(buffer.scroll_bottom, buffer_service.rows - 1)
|
|
|
|
|
|
func test_fill_viewport_rows():
|
|
# It should fill the buffer with blank lines based on the size of the viewport
|
|
var blank_line_char = buffer.get_blank_line(AttributeData.new()).load_cell(0, CellData.new()).get_as_char_data()
|
|
buffer.fill_viewport_rows()
|
|
assert_eq(buffer.lines.length, INIT_ROWS)
|
|
for y in range(INIT_ROWS):
|
|
assert_eq(buffer.lines.get_el(y).length, INIT_COLS)
|
|
for x in range(INIT_COLS):
|
|
assert_eq(buffer.lines.get_el(y).load_cell(x, CellData.new()).get_as_char_data(), blank_line_char)
|
|
|
|
|
|
class TestGetWrappedRangeForLine:
|
|
extends BaseBufferTest
|
|
|
|
|
|
func before_each():
|
|
.before_each()
|
|
buffer.fill_viewport_rows()
|
|
|
|
|
|
func test_non_wrapped_returns_a_single_row_for_the_first_row():
|
|
assert_eq(buffer.get_wrapped_range_for_line(0).first, 0)
|
|
assert_eq(buffer.get_wrapped_range_for_line(0).last, 0)
|
|
|
|
|
|
func test_non_wrapped_returns_a_single_row_for_a_middle_row():
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).first, 12)
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).last, 12)
|
|
|
|
|
|
func test_non_wrapped_returns_a_single_row_for_the_last_row():
|
|
assert_eq(buffer.get_wrapped_range_for_line(buffer.lines.length - 1).first, INIT_ROWS - 1)
|
|
assert_eq(buffer.get_wrapped_range_for_line(buffer.lines.length - 1).last, INIT_ROWS - 1)
|
|
|
|
|
|
func test_wrapped_returns_a_range_for_first_row():
|
|
buffer.lines.get_el(1).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(0).first, 0)
|
|
assert_eq(buffer.get_wrapped_range_for_line(0).last, 1)
|
|
|
|
|
|
func test_wrapped_range_for_middle_row_wrapping_upwards():
|
|
buffer.fill_viewport_rows()
|
|
buffer.lines.get_el(12).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).first, 11)
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).last, 12)
|
|
|
|
|
|
func test_wrapped_range_for_middle_row_wrapping_downwards():
|
|
buffer.lines.get_el(13).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).first, 12)
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).last, 13)
|
|
|
|
|
|
func test_wrapped_range_for_middle_row_wrapping_both_ways():
|
|
buffer.lines.get_el(11).is_wrapped = true
|
|
buffer.lines.get_el(12).is_wrapped = true
|
|
buffer.lines.get_el(13).is_wrapped = true
|
|
buffer.lines.get_el(14).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).first, 10)
|
|
assert_eq(buffer.get_wrapped_range_for_line(12).last, 14)
|
|
|
|
|
|
func test_wrapped_range_for_last_row():
|
|
buffer.lines.get_el(INIT_ROWS - 1).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(buffer.lines.length - 1).first, INIT_ROWS - 2)
|
|
assert_eq(buffer.get_wrapped_range_for_line(buffer.lines.length - 1).last, INIT_ROWS - 1)
|
|
|
|
|
|
func test_wrapped_range_for_row_that_wraps_upward_to_first_row():
|
|
buffer.lines.get_el(1).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(1).first, 0)
|
|
assert_eq(buffer.get_wrapped_range_for_line(1).last, 1)
|
|
|
|
|
|
func test_wrapped_range_for_row_that_wraps_downward_to_last_row():
|
|
buffer.lines.get_el(buffer.lines.length - 1).is_wrapped = true
|
|
assert_eq(buffer.get_wrapped_range_for_line(buffer.lines.length - 2).first, INIT_ROWS - 2)
|
|
assert_eq(buffer.get_wrapped_range_for_line(buffer.lines.length - 2).last, INIT_ROWS - 1)
|
|
|
|
|
|
class TestResize:
|
|
extends BaseBufferTest
|
|
|
|
|
|
func before_each():
|
|
.before_each()
|
|
buffer.fill_viewport_rows()
|
|
|
|
|
|
func test_column_size_reduction_trims_data_in_the_buffer():
|
|
buffer.resize(INIT_COLS / 2, INIT_ROWS)
|
|
assert_eq(buffer.lines.length, INIT_ROWS)
|
|
for i in range(INIT_ROWS):
|
|
assert_eq(buffer.lines.get_line(i).length, INIT_COLS / 2)
|
|
|
|
|
|
func test_column_size_increase_adds_pad_columns():
|
|
buffer.resize(INIT_COLS + 10, INIT_ROWS)
|
|
assert_eq(buffer.lines.length, INIT_ROWS)
|
|
for i in range(INIT_ROWS):
|
|
assert_eq(buffer.lines.get_line(i).length, INIT_COLS + 10)
|
|
|
|
|
|
func test_row_size_reduction_trims_blank_lines_from_the_end():
|
|
buffer.resize(INIT_COLS, INIT_ROWS - 10)
|
|
assert_eq(buffer.lines.length, INIT_ROWS - 10)
|
|
|
|
|
|
func test_row_size_reduction_moves_viewport_down_when_it_is_at_the_end():
|
|
# Set cursor y to have 5 blank lines below it
|
|
buffer.y = INIT_ROWS - 5 - 1
|
|
buffer.resize(INIT_COLS, INIT_ROWS - 10)
|
|
# Trim 5 rows
|
|
assert_eq(buffer.lines.length, INIT_ROWS - 5)
|
|
# Shift the viewport down 5 rows
|
|
assert_eq(buffer.ydisp, 5)
|
|
assert_eq(buffer.ybase, 5)
|
|
|
|
|
|
func test_no_scrollback_trims_from_the_top_of_the_buffer_when_the_cursor_reaches_the_bottom():
|
|
buffer = Buffer.new(true, TestUtils.MockOptionsService.new({"scrollback": 0}), buffer_service)
|
|
assert_eq(buffer.lines.max_length, INIT_ROWS)
|
|
buffer.y = INIT_ROWS - 1
|
|
buffer.fill_viewport_rows()
|
|
var ch_data = buffer.lines.get_line(5).load_cell(0, CellData.new()).get_as_char_data()
|
|
ch_data[1] = "a"
|
|
buffer.lines.get_line(5).set_cell(0, CellData.from_char_data(ch_data))
|
|
ch_data = buffer.lines.get_line(INIT_ROWS - 1).load_cell(0, CellData.new()).get_as_char_data()
|
|
ch_data[1] = "b"
|
|
buffer.lines.get_line(INIT_ROWS - 1).set_cell(0, CellData.from_char_data(ch_data))
|
|
buffer.resize(INIT_COLS, INIT_ROWS - 5)
|
|
assert_eq(buffer.lines.get_line(0).load_cell(0, CellData.new()).get_as_char_data()[1], "a")
|
|
assert_eq(buffer.lines.get_line(INIT_ROWS - 1 - 5).load_cell(0, CellData.new()).get_as_char_data()[1], "b")
|
|
|
|
|
|
func test_row_size_increase_adds_blank_lines_to_empty_buffer():
|
|
assert_eq(buffer.ydisp, 0)
|
|
buffer.resize(INIT_COLS, INIT_ROWS + 10)
|
|
assert_eq(buffer.ydisp, 0)
|
|
assert_eq(buffer.lines.length, INIT_ROWS + 10)
|
|
|
|
func test_row_size_increase_shows_more_of_the_buffer_above():
|
|
# Create 10 extra blank lines
|
|
for i in range(10):
|
|
buffer.lines.push(buffer.get_blank_line(AttributeData.new()))
|
|
# Set cursor to the bottom of the buffer
|
|
buffer.y = INIT_ROWS - 1
|
|
# Scroll down 10 lines
|
|
buffer.ybase = 10
|
|
buffer.ydisp = 10
|
|
assert_eq(buffer.lines.length, INIT_ROWS + 10)
|
|
buffer.resize(INIT_COLS, INIT_ROWS + 5)
|
|
# Should be 5 more lines
|
|
assert_eq(buffer.ydisp, 5)
|
|
assert_eq(buffer.ybase, 5)
|
|
# Should not trim the buffer
|
|
assert_eq(buffer.lines.length, INIT_ROWS + 10)
|
|
|
|
|
|
func test_row_size_increase_shows_more_of_the_buffer_below_when_the_viewort_is_at_the_top_of_the_buffer():
|
|
# Create 10 extra blank lines
|
|
for i in range(10):
|
|
buffer.lines.push(buffer.get_blank_line(AttributeData.new()))
|
|
# Set cursor to the bottom of the buffer
|
|
buffer.y = INIT_ROWS - 1
|
|
# Scroll down 10 lines
|
|
buffer.ybase = 10
|
|
buffer.ydisp = 0
|
|
assert_eq(buffer.lines.length, INIT_ROWS + 10)
|
|
buffer.resize(INIT_COLS, INIT_ROWS + 5)
|
|
# The viewport should remain at the top
|
|
assert_eq(buffer.ydisp, 0)
|
|
# The buffer ybase should move up 5 lines
|
|
assert_eq(buffer.ybase, 5)
|
|
# Should not trim the buffer
|
|
assert_eq(buffer.lines.length, INIT_ROWS + 10)
|
|
|
|
|
|
func test_row_and_column_increase_resizes_properly():
|
|
buffer.resize(INIT_COLS + 5, INIT_ROWS + 5)
|
|
assert_eq(buffer.lines.length, INIT_ROWS + 5)
|
|
buffer.resize(INIT_COLS - 5, INIT_ROWS)
|
|
assert_eq(buffer.lines.length, INIT_ROWS)
|
|
|
|
|
|
func test_reflow_does_not_wrap_empty_lines():
|
|
assert_eq(buffer.lines.length, INIT_ROWS)
|
|
buffer.resize(INIT_COLS - 5, INIT_ROWS)
|
|
assert_eq(buffer.lines.length, INIT_ROWS)
|
|
|
|
|
|
func test_reflow_shrinks_row_length():
|
|
buffer.resize(5, 10)
|
|
assert_eq(buffer.lines.length, 10)
|
|
for i in range(10):
|
|
assert_eq(buffer.lines.get_line(i).length, 5)
|
|
|
|
|
|
func test_reflow_wraps_and_unwraps_lines():
|
|
buffer.resize(5, 10)
|
|
var first_line = buffer.lines.get_line(0)
|
|
for i in range(5):
|
|
var code = "a".ord_at(0) + i
|
|
var ch = char(code)
|
|
first_line.set_cell(i, CellData.from_char_data([0, ch, 1, code]))
|
|
buffer.y = 1
|
|
assert_eq(buffer.lines.get_line(0).length, 5)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "abcde")
|
|
buffer.resize(1, 10)
|
|
assert_eq(buffer.lines.length, 10)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "a")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), "b")
|
|
assert_eq(buffer.lines.get_line(2).translate_to_string(), "c")
|
|
assert_eq(buffer.lines.get_line(3).translate_to_string(), "d")
|
|
assert_eq(buffer.lines.get_line(4).translate_to_string(), "e")
|
|
assert_eq(buffer.lines.get_line(5).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(6).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(7).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(8).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(9).translate_to_string(), " ")
|
|
buffer.resize(5, 10)
|
|
assert_eq(buffer.lines.length, 10)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "abcde")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(2).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(3).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(4).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(5).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(6).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(7).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(8).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(9).translate_to_string(), " ")
|
|
|
|
|
|
func test_discards_parts_of_wrapped_lines_that_go_out_of_the_scrollback():
|
|
options_service.options.scrollback = 1
|
|
buffer.resize(10, 5)
|
|
var last_line = buffer.lines.get_line(3)
|
|
for i in range(10):
|
|
var code = "a".ord_at(0) + i
|
|
var ch = char(code)
|
|
last_line.set_cell(i, CellData.from_char_data([0, ch, 1, code]))
|
|
assert_eq(buffer.lines.length, 5)
|
|
buffer.y = 4
|
|
buffer.resize(2, 5)
|
|
assert_eq(buffer.y, 4)
|
|
assert_eq(buffer.ybase, 1)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "ab")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), "cd")
|
|
assert_eq(buffer.lines.get_line(2).translate_to_string(), "ef")
|
|
assert_eq(buffer.lines.get_line(3).translate_to_string(), "gh")
|
|
assert_eq(buffer.lines.get_line(4).translate_to_string(), "ij")
|
|
assert_eq(buffer.lines.get_line(5).translate_to_string(), " ")
|
|
buffer.resize(1, 5)
|
|
assert_eq(buffer.y, 4)
|
|
assert_eq(buffer.ybase, 1)
|
|
assert_eq(buffer.lines.length, 6)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "f")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), "g")
|
|
assert_eq(buffer.lines.get_line(2).translate_to_string(), "h")
|
|
assert_eq(buffer.lines.get_line(3).translate_to_string(), "i")
|
|
assert_eq(buffer.lines.get_line(4).translate_to_string(), "j")
|
|
assert_eq(buffer.lines.get_line(5).translate_to_string(), " ")
|
|
buffer.resize(10, 5)
|
|
assert_eq(buffer.y, 1)
|
|
assert_eq(buffer.ybase, 0)
|
|
assert_eq(buffer.lines.length, 5)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "fghij ")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(2).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(3).translate_to_string(), " ")
|
|
assert_eq(buffer.lines.get_line(4).translate_to_string(), " ")
|
|
|
|
|
|
func test_removes_the_correct_amount_of_rows_when_reflowing_larger():
|
|
# This is a regression test to ensure that successive wrapped lines that are getting
|
|
# 3+ lines removed on a reflow actually remove the right lines
|
|
buffer.resize(10, 10)
|
|
buffer.y = 2
|
|
var first_line = buffer.lines.get_line(0)
|
|
var second_line = buffer.lines.get_line(1)
|
|
for i in range(10):
|
|
var code = "a".ord_at(0) + i
|
|
var ch = char(code)
|
|
first_line.set_cell(i, CellData.from_char_data([0, ch, 1, code]))
|
|
for i in range(10):
|
|
var code = "0".ord_at(0) + i
|
|
var ch = char(code)
|
|
second_line.set_cell(i, CellData.from_char_data([0, ch, 1, code]))
|
|
assert_eq(buffer.lines.length, 10)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "abcdefghij")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), "0123456789")
|
|
for i in range(2, 10):
|
|
assert_eq(buffer.lines.get_line(i).translate_to_string(), " ")
|
|
buffer.resize(2, 10)
|
|
assert_eq(buffer.ybase, 1)
|
|
assert_eq(buffer.lines.length, 11)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "ab")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), "cd")
|
|
assert_eq(buffer.lines.get_line(2).translate_to_string(), "ef")
|
|
assert_eq(buffer.lines.get_line(3).translate_to_string(), "gh")
|
|
assert_eq(buffer.lines.get_line(4).translate_to_string(), "ij")
|
|
assert_eq(buffer.lines.get_line(5).translate_to_string(), "01")
|
|
assert_eq(buffer.lines.get_line(6).translate_to_string(), "23")
|
|
assert_eq(buffer.lines.get_line(7).translate_to_string(), "45")
|
|
assert_eq(buffer.lines.get_line(8).translate_to_string(), "67")
|
|
assert_eq(buffer.lines.get_line(9).translate_to_string(), "89")
|
|
assert_eq(buffer.lines.get_line(10).translate_to_string(), " ")
|
|
buffer.resize(10, 10)
|
|
assert_eq(buffer.ybase, 0)
|
|
assert_eq(buffer.lines.length, 10)
|
|
assert_eq(buffer.lines.get_line(0).translate_to_string(), "abcdefghij")
|
|
assert_eq(buffer.lines.get_line(1).translate_to_string(), "0123456789")
|
|
for i in range(2, 10):
|
|
assert_eq(buffer.lines.get_line(i).translate_to_string(), " ",
|
|
"line %d is incorrect" % i)
|