From d7a6782ff02d2f581599254034e0343f45b9ddff Mon Sep 17 00:00:00 2001 From: Mike Pelletier Date: Tue, 27 Apr 2021 12:56:49 -0400 Subject: [PATCH] Hy version kind of works --- livesort_hy/__init__.py | 4 +++ livesort_hy/livesort.hy | 58 ++++++++++++++++++++++++++++++++++++ livesort_hy/test_livesort.hy | 53 ++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 livesort_hy/__init__.py create mode 100644 livesort_hy/livesort.hy create mode 100644 livesort_hy/test_livesort.hy diff --git a/livesort_hy/__init__.py b/livesort_hy/__init__.py new file mode 100644 index 0000000..0465f80 --- /dev/null +++ b/livesort_hy/__init__.py @@ -0,0 +1,4 @@ +import hy +from livesort_hy.livesort import Livesort +# This allows `unittest.main` to discover these tests +from livesort_hy.test_livesort import TestLivesort diff --git a/livesort_hy/livesort.hy b/livesort_hy/livesort.hy new file mode 100644 index 0000000..b6d41c4 --- /dev/null +++ b/livesort_hy/livesort.hy @@ -0,0 +1,58 @@ +(import [bisect [bisect]]) + +(defclass Livesort [] + (defn __init__ [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 + ] + (setv self.next-line next-line + self.print print + self.height height + self.width width + self.lines (list) + self.screen (* height [None]))) + (defn sortline [self line] + "Insort a line" + (when (line.strip) + ;; Normally would use `insort` but there's going to be more + (self.lines.insert (bisect self.lines line) line))) + (defn render [self] + "Produce a list of the lines which should be visible, cropped to `width`" + (list (map (fn [line] (cut line 0 self.width)) + (cut self.lines 0 self.height)))) + (defn display [self] + "Compare `(self.render)` to `self.screen` and return a string that +will update the display." + (setv output (list) + lines (self.render) + changed? False) + (output.append "") ; Home cursor + (for [lineno (range self.height)] + (setv line (if (< lineno (len lines)) (get lines lineno) "")) + (unless (= line (get self.screen lineno)) + (setv (get self.screen lineno) line + changed? True) + ;; ^[[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 + ;; if something changed, don't bother outputting the last cursor + ;; movement code. If nothing changed, don't bother outputting + ;; anything. + (if changed? (.join "" (cut output 0 -1)) "")) + (defn livesort [self] + "Process every input line, updating screen each line" + (for [line self.next-line] + (.sortline self line) + (.print self (.display self))))) + +(defmain + () + (import sys os) + (.livesort + (Livesort sys.stdin + (fn [data] (sys.stdout.write data) (sys.stdout.flush)) + (int (or (os.getenv "LINES") "24")) + (int (or (os.getenv "COLUMNS") "80"))))) diff --git a/livesort_hy/test_livesort.hy b/livesort_hy/test_livesort.hy new file mode 100644 index 0000000..182b74e --- /dev/null +++ b/livesort_hy/test_livesort.hy @@ -0,0 +1,53 @@ +(import [livesort_hy [Livesort]]) +(import unittest) + + +(defclass TestLivesort [unittest.TestCase] + (defn setUp [self] + (setv self.input-buf (list) + self.output-buf (list) + self.ls (Livesort self.input-buf self.output-buf.append 4 8))) + + (defn tearDown [self] + (del self.ls + self.output-buf + self.input-buf)) + + (defn test_Livesort [self] + (self.assertEqual self.ls.next-line self.input-buf) + (self.assertEqual self.ls.print self.output-buf.append) + (self.assertEqual self.ls.height 4) + (self.assertEqual self.ls.width 8) + (self.assertFalse self.ls.lines) + (self.assertTrue self.ls.screen) + (for [line self.ls.screen] + (self.assertEqual None line))) + + (defn test_sortline [self] + (self.assertFalse self.ls.lines) + (self.ls.sortline "line 2") + (self.assertEqual self.ls.lines ["line 2"]) + (self.ls.sortline "line 1") + (self.assertEqual self.ls.lines ["line 1" "line 2"])) + + (defn test_render [self] + (self.ls.sortline "line 1") + (self.assertEqual (self.ls.render) + ["line 1"])) + + (defn test_display [self] + ;; I got fed up trying to write this sensibly, will go away after refacor + (self.assertEqual " + + +" (self.ls.display)) + (self.ls.sortline "line 1") + (self.assertEqual "line 1 + + +" (self.ls.display))) + + ;; note (unfortunate) capitalization + (defn test_livesort [self] + (self.input-buf.append "line 1") + (self.ls.livesort)))