Files

86 lines
3.6 KiB
Plaintext

(hissp.basic.._macro_.prelude)
;; Lissp's class definition syntax is somewhat unlike Python's class
;; definition syntax even though it expresses the same model. Python
;; will nearly never imply a dict from a sequence of uncoupled pairs
;; but this is a common way to express an association list in lisps.
;; It's interesting that `deftype` is a macro around a `type` call in
;; its rarely used form with three arguments. Quoting the
;; member/method (it does not distinguist) and class names for you in
;; the macro makes it read a little bit more like Python than it would
;; if you had to quote them.
(deftype Livesort ()
__init__
(lambda (self
next-line ; iterable function yielding lines of text
print ; function to write output
height ; window/screen height in lines
width ; window/screen width in characters
)
(attach self next-line print height width :
lines (list) ; sorted input lines
screen (list (mul height '(None))))
None) ; __init__ required to return `None`
sortline
(lambda (self line)
"Insort a line and update the cursor position"
(when (.strip line)
(let (lineno (bisect..bisect self.lines line))
;; Normally would use `..insort` but there's going to be more
(.insert self.lines lineno line))))
render
(lambda (self)
"Produce a list of the lines which should be visible, cropped to `width`"
(list (map (lambda (line) (getitem line (slice None self.width)))
(getitem self.lines (slice None self.height)))))
display
(lambda (self)
"Compare `(self.render)` to `self.screen` and return a string that
will update the display."
(let (output (list)
lines (self.render)
changed? (list))
(.append output "") ; Home cursor
(any-for lineno (range self.height)
(let (line (if-else (lt lineno (len lines))
(getitem lines lineno)
""))
(unless (eq line (getitem self.screen lineno))
(setitem self.screen lineno line)
;; `changed?` might have just been an explicit
;; boolean value, but Hissp would prefer that we
;; mutate values and not reassign variables. Since
;; a `list` is an implicit boolean value, appending
;; (or not) to it works fine.
(.append changed? line)
;; ^[[K to erase any characters that follow on this line
(.append output (.format "{}"
(.rstrip line (chr 0x0a)))))
(.append output (chr 0x0a))) ; Move to beginning of next line
False)
;; If something changed, don't bother outputting the last cursor
;; movement code. If nothing changed, don't bother outputting
;; anything.
(if-else changed?
(.join "" (getitem output (slice None -1)))
"")))
livesort
(lambda (self)
"Process every input line, updating screen each line"
(any-for line self.next-line
(.sortline self line)
(.print self (.display self))
False)))
(when (eq __name__ '__main__)
(.livesort (Livesort sys..stdin
(lambda (data)
(sys..stdout.write data)
(sys..stdout.flush))
(int (|| (os..getenv 'LINES) "24"))
(int (|| (os..getenv 'COLUMNS) "80")))))