Add more features, bug fixes and bugs ;-)

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`.
This commit is contained in:
Leroy Hopson 2020-05-19 18:45:18 +07:00
parent 0769592a1b
commit 0d4e10f5ab
30 changed files with 2640 additions and 1157 deletions

View file

@ -122,7 +122,241 @@ class TestGetWrappedRangeForLine:
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)

View file

@ -144,7 +144,6 @@ class TestCellData:
func before_each():
cell = CellData.new()
func test_char_data_cell_data_equality():
@ -242,8 +241,97 @@ class TestBufferLine:
[6, 'f', 0, 'f'.ord_at(0)],
[5, 'e', 0, 'e'.ord_at(0)],
])
func test_copy_from():
var line = BufferLineTest.new(5)
line.set_cell(0, CellData.from_char_data([1, 'a', 0, 'a'.ord_at(0)]))
line.set_cell(0, CellData.from_char_data([2, 'b', 0, 'b'.ord_at(0)]))
line.set_cell(0, CellData.from_char_data([3, 'c', 0, 'c'.ord_at(0)]))
line.set_cell(0, CellData.from_char_data([4, 'd', 0, 'd'.ord_at(0)]))
line.set_cell(0, CellData.from_char_data([5, 'e', 0, 'e'.ord_at(0)]))
var line2 = BufferLineTest.new(5, CellData.from_char_data([1, 'a', 0, 'a'.ord_at(0)]), true)
line2.copy_from(line)
assert_eq(line2.to_array(), line.to_array())
assert_eq(line2.length, line.length)
assert_eq(line2.is_wrapped, line.is_wrapped)
class TestResize:
extends "res://addons/gut/test.gd"
var CHAR_DATA = [1, 'a', 0, 'a'.ord_at(0)]
var line
func repeat(el, times: int) -> Array:
var result = []
result.resize(times)
for i in range(times):
result[i] = el
return result
func test_enlarge():
line = BufferLineTest.new(5, CellData.from_char_data(CHAR_DATA), false)
line.resize(10, CellData.from_char_data(CHAR_DATA))
assert_eq(line.to_array(), repeat(CHAR_DATA, 10))
func test_shrink():
line = BufferLineTest.new(10, CellData.from_char_data(CHAR_DATA), false)
line.resize(5, CellData.from_char_data(CHAR_DATA))
assert_eq(line.to_array(), repeat(CHAR_DATA, 5))
func test_shrink_to_0_length():
line = BufferLineTest.new(10, CellData.from_char_data(CHAR_DATA), false)
line.resize(0, CellData.from_char_data(CHAR_DATA))
assert_eq(line.to_array(), repeat(CHAR_DATA, 0))
func shrink_then_enlarge():
line = BufferLineTest.new(10, CellData.from_char_data(CHAR_DATA), false);
line.set_cell(2, CellData.from_char_data([0, '😁', 1, '😁'.ord_at(0)]))
line.set_cell(9, CellData.from_char_data([0, '😁', 1, '😁'.ord_at(0)]))
assert_eq(line.translate_to_string(), 'aa😁aaaaaa😁')
line.resize(5, CellData.from_char_data(CHAR_DATA))
assert_eq(line.translate_to_string(), 'aa😁aa')
line.resize(10, CellData.from_char_data(CHAR_DATA))
assert_eq(line.translate_to_string(), 'aa😁aaaaaaa')
class TestTrimLength:
extends "res://addons/gut/test.gd"
var line
func before_each():
line = BufferLineTest.new(3, CellData.from_char_data([Constants.DEFAULT_ATTR,
Constants.NULL_CELL_CHAR, Constants.NULL_CELL_WIDTH, Constants.NULL_CELL_CODE]))
func test_empty_line():
assert_eq(line.get_trimmed_length(), 0)
func test_ascii():
line.set_cell(0, CellData.from_char_data([1, "a", 1, "a".ord_at(0)]))
line.set_cell(2, CellData.from_char_data([1, "a", 1, "a".ord_at(0)]))
assert_eq(line.get_trimmed_length(), 3)
func test_unicode():
line.set_cell(0, CellData.from_char_data([1, "\u1f914", 1, "\u1f914".ord_at(0)]))
line.set_cell(2, CellData.from_char_data([1, "\u1f914", 1, "\u1f914".ord_at(0)]))
assert_eq(line.get_trimmed_length(), 3)
func test_one_cell():
line.set_cell(0, CellData.from_char_data([1, "a", 1, "a".ord_at(0)]))
assert_eq(line.get_trimmed_length(), 1)
# Skipped a bunch of tests here...
class TestAddCharToCell:
extends "res://addons/gut/test.gd"
@ -293,7 +381,7 @@ class TestAddCharToCell:
assert_eq(cell.is_combined(), Content.IS_COMBINED_MASK)
class Testtranslate_to_string:
class TestTranslateToString:
extends "res://addons/gut/test.gd"