No, my conclusion in the previous commit was wrong. When you print a
character on the right margin, the cursor coordinates always wrap around
to the left margin on the next row. It's just that if you're at the
bottom of the screen, scrolling gives the impression that the row didn't
change.
This commit is contained in:
Kartik K. Agaram 2017-06-23 19:47:09 -07:00
parent b1e558cfe4
commit b2eff9f97e
5 changed files with 106 additions and 38 deletions

View File

@ -1,5 +1,16 @@
# Wrappers around print primitives that take a 'screen' object and are thus
# easier to test.
#
# Screen objects are intended to exactly mimic the behavior of traditional
# terminals. Moving a cursor too far write wraps it to the next line,
# scrolling if necessary. The details are subtle:
#
# a) Rows can take unbounded values, and such values are preserved rather than
# clamped or wrapped around.
#
# b) If you print to a square (row, right) on the right margin, the cursor
# position depends on whether 'row' is in range. If it is, the new cursor
# position is (row+1, 0). If it isn't, the new cursor position is (row, 0).
container screen [
num-rows:num
@ -7,6 +18,7 @@ container screen [
cursor-row:num
cursor-column:num
data:&:@:screen-cell # capacity num-rows*num-columns
pending-scroll?:bool
top-idx:num # index inside data that corresponds to top-left of screen
# modified on scroll, wrapping around to the top of data
]
@ -16,6 +28,32 @@ container screen-cell [
color:num
]
def main [
local-scope
open-console
clear-screen
print 0/screen [a]
#? a:char <- copy 97
#? i:num <- copy 0
#? {
#? done?:bool <- greater-or-equal i, 6
#? break-if done?
#? move-cursor 0/screen i, 2527
#? print 0/screen a
#? a <- add a, 1
#? i <- add i, 1
#? loop
#? }
move-cursor 0/screen 5, 2527
a:num b:num <- cursor-position 0/screen
print 0/screen [f]
c:num d:num <- cursor-position 0/screen
wait-for-some-interaction
close-console
$print a [ ] b 10/newline
$print c [ ] d 10/newline
]
def new-fake-screen w:num, h:num -> result:&:screen [
local-scope
load-ingredients
@ -26,14 +64,14 @@ def new-fake-screen w:num, h:num -> result:&:screen [
assert non-zero-height?, [screen can't have zero height]
bufsize:num <- multiply w, h
data:&:@:screen-cell <- new screen-cell:type, bufsize
*result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, 0/top-idx
*result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, 0/pending-scroll?, 0/top-idx
result <- clear-screen result
]
def clear-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [clear-screen]
#? stash [clear-screen]
{
break-if screen
# real screen
@ -61,7 +99,7 @@ def clear-screen screen:&:screen -> screen:&:screen [
def fake-screen-is-empty? screen:&:screen -> result:bool [
local-scope
load-ingredients
stash [fake-screen-is-empty?]
#? stash [fake-screen-is-empty?]
return-unless screen, 1/true # do nothing for real screens
buf:&:@:screen-cell <- get *screen, data:offset
i:num <- copy 0
@ -150,9 +188,13 @@ def print screen:&:screen, c:char -> screen:&:screen [
{
at-bottom?:bool <- equal row, height
break-unless at-bottom?
$dump-screen
$print [scroll1] 10/newline
scroll-fake-screen screen
row <- subtract height, 1
*screen <- put *screen, cursor-row:offset, row
*screen <- put *screen, pending-scroll?:offset, 0/false
$dump-screen
}
# if row is below bottom margin (error), reset to bottom margin
{
@ -162,6 +204,17 @@ def print screen:&:screen, c:char -> screen:&:screen [
*screen <- put *screen, cursor-row:offset, row
}
# }
# if there's a pending scroll, perform it
{
pending-scroll?:bool <- get *screen, pending-scroll?:offset
#? $print pending-scroll? 10/newline
break-unless pending-scroll?
$print [scroll2] 10/newline
$dump-screen
scroll-fake-screen screen
*screen <- put *screen, pending-scroll?:offset, 0/false
$dump-screen
}
#? stash [print] row column
#? $print [print-character (], row, [, ], column, [): ], c, 10/newline
# special-case: newline
@ -195,16 +248,30 @@ def print screen:&:screen, c:char -> screen:&:screen [
index:num <- data-index row, column, width, height, top-idx
cursor:screen-cell <- merge c, color
*buf <- put-index *buf, index, cursor
# move cursor to next character
# move cursor to next character, wrapping as necessary
# however, don't scroll just yet
# (but don't bother making it valid; we'll do that before the next print)
column <- add column, 1
{
past-right?:bool <- greater-or-equal column, width
break-unless past-right?
column <- copy 0
row <- add row, 1
past-bottom?:bool <- greater-or-equal row, height
break-unless past-bottom?
# queue up a scroll
$print [pending scroll] 10/newline
*screen <- put *screen, pending-scroll?:offset, 1/true
row <- subtract row, 1 # update cursor as if scroll already happened
}
*screen <- put *screen, cursor-row:offset, row
*screen <- put *screen, cursor-column:offset, column
]
def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-down]
#? stash [cursor-down]
row:num <- get *screen, cursor-row:offset
height:num <- get *screen, num-rows:offset
bottom:num <- subtract height, 1
@ -223,7 +290,7 @@ def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [
def scroll-fake-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [scroll-fake-screen]
#? stash [scroll-fake-screen]
width:num <- get *screen, num-columns:offset
height:num <- get *screen, num-rows:offset
buf:&:@:screen-cell <- get *screen, data:offset
@ -515,7 +582,7 @@ def assert-no-scroll screen:&:screen, old-top-idx:num [
def clear-line screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [clear-line]
#? stash [clear-line]
space:char <- copy 0/nul
{
break-if screen
@ -545,7 +612,7 @@ def clear-line-until screen:&:screen, right:num/inclusive -> screen:&:screen [
local-scope
load-ingredients
row:num, column:num <- cursor-position screen
stash [clear-line-until] row column
#? stash [clear-line-until] row column
height:num <- screen-height screen
past-bottom?:bool <- greater-or-equal row, height
return-if past-bottom?
@ -582,7 +649,7 @@ def cursor-position screen:&:screen -> row:num, column:num [
def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen [
local-scope
load-ingredients
stash [move-cursor]
#? stash [move-cursor]
{
break-if screen
# real screen
@ -592,6 +659,8 @@ def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen
# fake screen
*screen <- put *screen, cursor-row:offset, new-row
*screen <- put *screen, cursor-column:offset, new-column
$print [move cursor ] new-row [ ] new-column 10/newline
*screen <- put *screen, pending-scroll?:offset, 0/false
]
scenario clear-line-erases-printed-characters [
@ -628,7 +697,7 @@ scenario clear-line-erases-printed-characters [
def cursor-down screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-down]
#? stash [cursor-down]
{
break-if screen
# real screen
@ -671,7 +740,7 @@ scenario cursor-down-scrolls [
def cursor-up screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-up]
#? stash [cursor-up]
{
break-if screen
# real screen
@ -689,7 +758,7 @@ def cursor-up screen:&:screen -> screen:&:screen [
def cursor-right screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-right]
#? stash [cursor-right]
{
break-if screen
# real screen
@ -709,7 +778,7 @@ def cursor-right screen:&:screen -> screen:&:screen [
def cursor-left screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-left]
#? stash [cursor-left]
{
break-if screen
# real screen
@ -727,16 +796,15 @@ def cursor-left screen:&:screen -> screen:&:screen [
def cursor-to-start-of-line screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-to-start-of-line]
#? stash [cursor-to-start-of-line]
row:num <- cursor-position screen
column:num <- copy 0
screen <- move-cursor screen, row, column
screen <- move-cursor screen, row, 0/column
]
def cursor-to-next-line screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
stash [cursor-to-next-line]
#? stash [cursor-to-next-line]
screen <- cursor-down screen
screen <- cursor-to-start-of-line screen
]
@ -745,14 +813,14 @@ def move-cursor-to-column screen:&:screen, column:num -> screen:&:screen [
local-scope
load-ingredients
row:num, _ <- cursor-position screen
stash [move-cursor-to-column] row
#? stash [move-cursor-to-column] row
move-cursor screen, row, column
]
def screen-width screen:&:screen -> width:num [
local-scope
load-ingredients
stash [screen-width]
#? stash [screen-width]
{
break-unless screen
# fake screen
@ -766,7 +834,7 @@ def screen-width screen:&:screen -> width:num [
def screen-height screen:&:screen -> height:num [
local-scope
load-ingredients
stash [screen-height]
#? stash [screen-height]
{
break-unless screen
# fake screen

View File

@ -207,7 +207,7 @@ def render screen:&:screen, editor:&:editor -> last-row:num, last-column:num, sc
def clear-screen-from screen:&:screen, row:num, column:num, left:num, right:num -> screen:&:screen [
local-scope
load-ingredients
stash [clear-screen-from] row column [between] left [and] right
#? stash [clear-screen-from] row column [between] left [and] right
# if it's the real screen, use the optimized primitive
{
break-if screen
@ -218,7 +218,7 @@ def clear-screen-from screen:&:screen, row:num, column:num, left:num, right:num
screen <- move-cursor screen, row, column
clear-line-until screen, right
clear-rest-of-screen screen, row, left, right
screen <- move-cursor screen, row, column
#? screen <- move-cursor screen, row, column
]
def clear-rest-of-screen screen:&:screen, row:num, left:num, right:num -> screen:&:screen [

View File

@ -1116,11 +1116,11 @@ after <handle-special-key> [
def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [
local-scope
load-ingredients
a:num, b:num <- cursor-position screen
stash [draw-horizontal] row [--] a b
#? a:num, b:num <- cursor-position screen
#? stash [draw-horizontal] row [--] a b
height:num <- screen-height screen
past-bottom?:bool <- greater-or-equal row, height
stash [ past-bottom?] past-bottom?
#? stash [ past-bottom?] past-bottom?
return-if past-bottom?
style:char, style-found?:bool <- next-ingredient
{
@ -1138,18 +1138,18 @@ def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:scree
break-if bg-color-found?
bg-color <- copy 0/black
}
stash [aa] x
#? stash [aa] x
screen <- move-cursor screen, row, x
{
continue?:bool <- lesser-or-equal x, right # right is inclusive, to match editor semantics
break-unless continue?
a b <- cursor-position screen
stash [bb] x [--] a b
#? a b <- cursor-position screen
#? stash [bb] x [--] a b
print screen, style, color, bg-color
a b <- cursor-position screen
stash [cc] x [--] a b
#? a b <- cursor-position screen
#? stash [cc] x [--] a b
x <- add x, 1
loop
}
stash [draw-horizontal done]
#? stash [draw-horizontal done]
]

View File

@ -447,7 +447,7 @@ def render-recipes screen:&:screen, env:&:environment, render-editor:render-reci
clear-screen-from screen, row, left, left, right
#
assert-no-scroll screen, old-top-idx
stash [render recipes done]
#? stash [render recipes done]
]
# replaced in a later layer
@ -468,7 +468,7 @@ def render-sandbox-side screen:&:screen, env:&:environment, render-editor:render
clear-screen-from screen, row, left, left, right
#
assert-no-scroll screen, old-top-idx
stash [render sandbox side0 done]
#? stash [render sandbox side0 done]
]
def update-cursor screen:&:screen, recipes:&:editor, current-sandbox:&:editor, sandbox-in-focus?:bool, env:&:environment -> screen:&:screen [

View File

@ -270,15 +270,15 @@ def! render-sandbox-side screen:&:screen, env:&:environment, render-editor:rende
clear-rest-of-screen screen, row, left, right
#
assert-no-scroll screen, old-top-idx
stash [render sandbox side done]
#? stash [render sandbox side done]
]
def render-sandboxes screen:&:screen, sandbox:&:sandbox, left:num, right:num, row:num, render-from:num, idx:num -> row:num, screen:&:screen, sandbox:&:sandbox [
local-scope
load-ingredients
return-unless sandbox
a:num b:num <- cursor-position screen
stash [render-sandboxes] idx [:] row [--] a b
#? a:num b:num <- cursor-position screen
#? stash [render-sandboxes] idx [:] row [--] a b
screen-height:num <- screen-height screen
at-bottom?:bool <- greater-or-equal row, screen-height
return-if at-bottom?
@ -361,11 +361,11 @@ scenario skip-rendering-sandbox-menu-past-bottom-row [
# create two sandboxes such that the top one just barely fills the screen
env:&:environment <- new-programming-environment resources, screen, []
env <- restore-sandboxes env, resources
$clear-trace
#? $clear-trace
run [
render-all screen, env, render
]
$dump-trace [app]
#? $dump-trace [app]
screen-should-contain [
. run (F4) .
. ┊ .