# A New Listener

The basic listener is very minimalistic. There are a few reasons
for this.

- I don't use it often (I generally code in files or blocks,
  using the listener only for brief tests)
- A complex listener adds surface for bugs and incompatibilites
  due to terminal and host configurations
- It's harder to pipe data in and out of a more complex listener

This can be worked around in a number of ways. Tools like
`rlwrap` can be used to provide a line editor and history, and
one can always write their own custom listener...

But sometimes it's nice to experiment a bit. This is a modestly
expanded version of the listener, adding some new functionality
and providing a more flexible base to build on.

# Current Features

- character breaking input
- suggestions on hitting TAB
- show stack on hitting ESC
- use `retro-describe` to get help on CTRL+K

# Loading

    retro -i -f alternate-listener.forth
    new-listener

# The Code

The RETRO image has a 512 cell buffer between the kernel and the
high level code. This is used for the Text Input Buffer, or TIB.

    +------------------+
    | kernel           | 0-1023
    | input buffer     | 1024-1536
    | standard library | 1537+
    | ...              |
    | system buffers   | EOM - ???
    +------------------+

~~~
#1024 'TIB const
~~~

The listener needs to determine what to treat as the end of the
token. I define `end-of-token?` for this.

~~~
:end-of-token? (c-f) hook
  { ASCII:CR ASCII:LF ASCII:SPACE } a:contains? ;
~~~

It's possible to get an empty string as an input. This isn't
any good, so I define `s:blank?` for this.

~~~
:s:blank? (s-sf) dup s:length n:zero? ;
~~~

There are certain keys I want to handle differently from others.
The initial ones are backspace, tab, escape, and CTRL+K. I am
defining handlers for these.

First is backspace. I trap ASCII:BS and ASCII:DEL for this.

~~~
:handle:backspace (c-c) hook
  dup { ASCII:BS ASCII:DEL } a:contains?
  [ buffer:get buffer:get drop-pair ] if ;
~~~

Tab is used to display suggestions, based on the token input so
far.

~~~
{{
  :prefix?
    TIB fetch 'prefix:_ [ #7 + store ] sip d:lookup n:-zero? ; 
  :hint
    nl TIB prefix? [ n:inc ] if d:words-beginning-with nl
    TIB s:put ;
---reveal---
  :handle:tab (c-c) hook
    dup ASCII:HT eq? [ buffer:get drop hint ] if ;
}}
~~~

Control + k (`ASCII:VT`) will display help for the word being typed.
This assumes that `retro-describe` is in your $PATH and that the
typed text is a complete word name (without a prefix).

~~~
:handle:CTRL+K (c-c) hook
  dup ASCII:VT eq? [ buffer:get drop
                     TIB 'retro-describe_"%s" s:format nl
                     unix:system TIB s:put ] if ;
~~~

The escape key will be used to display the stack.

~~~
:handle:escape (c-c) hook
  dup ASCII:ESC eq?
  [ buffer:get drop nl &dump-stack dip nl TIB s:put ] if ;
~~~

To control the checks, I define two words. The first returns an
array of handlers, the second processes them.

~~~
:special-keys (-a) hook
  { &handle:backspace &handle:tab &handle:escape &handle:CTRL+K } ;

:check (q-) hook
  &call a:for-each ;
~~~

And with these, I can quickly implement the `new-listener`.

~~~
{{
  :guard   (-)  buffer:end TIB lt? [ TIB buffer:set ] if ;
  :c:get   (-c) as{ 'liii.... i #1 d }as dup buffer:add ;
  :s:get   (-s) [ TIB buffer:set
                  [ guard c:get special-keys check end-of-token? ] until
                  buffer:start s:chop ] buffer:preserve ;
  :forever (q-) repeat &call sip again ;
---reveal---
  :new-listener (-) hook
    'stty_cbreak unix:system
    [ s:get s:blank? &drop &interpret choose ] forever ;

  [ 'stty_cbreak unix:system
    #0 unix:exit ] &bye set-hook
}}
~~~

All of the exposed words are hooks: your code can patch in and
replace them as you see fit, making this much more mallable at
runtime.

Future things to (maybe) explore:

- tab completion
- line editing
- input history
