1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 04:00:30 +00:00

Fix numerous off-by-one errors in help program

We clamped various values to the height of the screen, rather than the
height of the content box (height-1). We didn't notice this most of the
time as the last line of a file is empty - it only really mattered when
a file was the same height as the computer's screen.

We now do the following:
 - Strip the trailing new line from a file when reading.
 - Replace most usages of height with height-1.
This commit is contained in:
Jonathan Coates 2022-09-11 14:57:31 +01:00
parent ba64e06ca7
commit d22e138413
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
3 changed files with 66 additions and 9 deletions

View File

@ -146,14 +146,17 @@ end
local contents = file:read("*a")
file:close()
-- Trim trailing newlines from the file to avoid displaying a blank line.
if contents:sub(-1) == "\n" then contents:sub(1, -2) end
local word_wrap = sFile:sub(-3) == ".md" and word_wrap_markdown or word_wrap_basic
local width, height = term.getSize()
local content_height = height - 1 -- Height of the content box.
local lines, fg, bg, sections = word_wrap(contents, width)
local print_height = #lines
-- If we fit within the screen, just display without pagination.
if print_height <= height then
if print_height <= content_height then
local _, y = term.getCursorPos()
for i = 1, print_height do
if y + i - 1 > height then
@ -201,7 +204,7 @@ end
local function draw()
for y = 1, height - 1 do
for y = 1, content_height do
term.setCursorPos(1, y)
if y + offset > print_height then
-- Should only happen if we resize the terminal to a larger one
@ -228,14 +231,14 @@ while true do
if param == keys.up and offset > 0 then
offset = offset - 1
draw()
elseif param == keys.down and offset < print_height - height then
elseif param == keys.down and offset < print_height - content_height then
offset = offset + 1
draw()
elseif param == keys.pageUp and offset > 0 then
offset = math.max(offset - height + 2, 0)
offset = math.max(offset - content_height + 1, 0)
draw()
elseif param == keys.pageDown and offset < print_height - height then
offset = math.min(offset + height - 2, print_height - height)
elseif param == keys.pageDown and offset < print_height - content_height then
offset = math.min(offset + content_height - 1, print_height - content_height)
draw()
elseif param == keys.home then
offset = 0
@ -247,7 +250,7 @@ while true do
offset = sections[current_section + 1].offset
draw()
elseif param == keys["end"] then
offset = print_height - height
offset = print_height - content_height
draw()
elseif param == keys.q then
sleep(0) -- Super janky, but consumes stray "char" events.
@ -257,7 +260,7 @@ while true do
if param < 0 and offset > 0 then
offset = offset - 1
draw()
elseif param > 0 and offset < print_height - height then
elseif param > 0 and offset <= print_height - content_height then
offset = offset + 1
draw()
end
@ -270,7 +273,8 @@ while true do
end
width, height = new_width, new_height
offset = math.max(math.min(offset, print_height - height), 0)
content_height = height - 1
offset = math.max(math.min(offset, print_height - content_height), 0)
draw()
draw_menu()
elseif event == "terminate" then

View File

@ -1,8 +1,46 @@
local capture = require "test_helpers".capture_program
local with_window_lines = require "test_helpers".with_window_lines
describe("The help program", function()
local function stub_help(content)
local name = "/help_file.txt"
io.open(name, "wb"):write(content):close()
stub(help, "lookup", function() return name end)
end
local function capture_help(width, height, content)
stub_help(content)
local co = coroutine.create(shell.run)
local window = with_window_lines(width, height, function()
local ok, err = coroutine.resume(co, "help topic")
if not ok then error(err, 0) end
end)
return coroutine.status(co) == "dead", window
end
it("errors when there is no such help file", function()
expect(capture(stub, "help nothing"))
:matches { ok = true, error = "No help available\n", output = "" }
end)
it("prints a short file directly", function()
local dead, output = capture_help(10, 3, "a short\nfile")
expect(dead):eq(true)
expect(output):same {
"a short ",
"file ",
" ",
}
end)
it("launches the viewer for a longer file", function()
local dead, output = capture_help(10, 3, "a longer\nfile\nwith content")
expect(dead):eq(false)
expect(output):same {
"a longer ",
"file ",
"Help: topi",
}
end)
end)

View File

@ -56,7 +56,22 @@ local function with_window(width, height, fn)
return redirect
end
--- Run a function redirecting to a new window with the given dimensions,
-- returning the content of the window.
--
-- @tparam number width The window's width
-- @tparam number height The window's height
-- @tparam function() fn The action to run
-- @treturn {string...} The content of the window.
local function with_window_lines(width, height, fn)
local window = with_window(width, height, fn)
local out = {}
for i = 1, height do out[i] = window.getLine(i) end
return out
end
return {
capture_program = capture_program,
with_window = with_window,
with_window_lines = with_window_lines,
}