A terminal app
Chapter 4’s program moves the cursor by itself. In this chapter you move
it, with the keys of a terminal. Write it in app/hello/main.hf, in place
of chapter 4’s program:
module app.hello.main
import coimport std.ioimport std.terminal as Terminalimport std.terminal.widget as Widgetimport hello.cursorimport hello.todoimport hello.zipperKeys
toggle : Todo -> Todotoggle t = if pending t.status then finish t else { t | status = Open }
-- | what a key does at the cursoract : {e : HFunctor} -> {{cur : cursor <: e}} -> Widget.Key -> Co e Unitact ArrowDown = do _ <- next pure MkUnitact ArrowUp = do _ <- previous pure MkUnitact (Char ' ') = change toggleact _ = pure MkUnit
step : Widget.Key -> Zipper Todo -> Widget.Step (Zipper Todo) (List Todo)step (Char 'q') z = Done (toList z)step k z = Continue (runCursor (act k) z)act says what each key does with the cursor: the arrows move it, and
space marks the todo under it done, or open again. A key is written by
its name alone, ArrowDown or Char ' ', without Widget..
step runs a key’s action on the zipper by runCursor. q is Done:
the app ends, with the list as it is then.
The screen
-- (a todo's row)row : (String -> Widget.Span) -> Todo -> Widget.Rowrow shown t = [shown (show t)]
view : Widget.Size -> Zipper Todo -> Widget.Frameview size z = { rows = Widget.fill (size.rows - 1) (List.map (row Widget.plain) (List.reverse z.before) ++ (row Widget.inverse z.focus :: List.map (row Widget.plain) z.after)) ++ [Widget.status size.columns "↑↓ move space done q quit"] , cursor = Nothing }
app : Widget.App (Zipper Todo) (List Todo)app = { start = { before = [] , focus = todo "write the tutorial" , after = [todo "read the reference", todo "test the examples"] } , step = step , view = view }view draws a zipper: one row for each todo, the one under the cursor
in inverse, and a line of help at the bottom. app puts the start, the
step and the view together.
Running it
main : Co (Terminal.console <+> stdio) Unitmain = do r <- Terminal.interact app case r of Just ts -> print (joinWith "\n" (List.map show ts)) Nothing -> pure MkUnitTerminal.interact runs the app on the terminal: it draws the view,
reads a key, steps, and draws again, until the app is Done. main
then prints the todos.
$ heff run app/hello/main.hf