Consider the following interaction:
This is the root page You have visited 1 time. 0:foo 1:bar $ 1 Please visit foo first. Any input to return. $ x This is the root page. You have visited 2 times. 0:foo 1:bar $ 0 Welcome to page foo. Please write seomthing. $ blah blah blah This is the root page. You have visited 3 times. 0:foo 1:bar $ 1 Thanks for visiting foo and bar. You have written: blah blah blah
The simple interface above involves four different menu nodes.
In order to engineer these using vise, three types of components are involved:
The nodes are:
root
The first page.
foo
The "foo" page.
bar
The "bar" page after "foo" has been visited.
ouch
The "bar" page before "foo" has been visited.
Each page has a template that may or may not contain dynamic elements.
In this example the root
and bar
nodes contains dynamic content.
This is the root page You have visited {{.count}}.
Welcome to page foo. Please write something.
Thanks for visiting foo and bar. You wrote "{{.something}}" in foo.
Please visit foo first. Any input to return.
The scripts are responsible for defining menus, handling navigation flow control, and triggering external code handlers.
LOAD count 8 # trigger external code handler "count" LOAD something 0 # trigger external code handler "something" RELOAD count # explicitly trigger "count" every time this code is executed. MAP count # make the result from "count" available to the template renderer MOUT foo 0 # menu item MOUT bar 1 # menu item HALT # render template and wait for input INCMP foo 0 # match menu selection 0, move to node "foo" on match INCMP bar 1 # match menu selection 1, move to node "bar" on match
HALT # render template and wait for input RELOAD something # pass input to the "something" external code handler. # The input will be appended to the stored value. # The "HAVESOMETHING" flag (8) will be set. MOVE _ # move up one level
CATCH ouch 8 0 # if the "HAVESOMETHING" (8) flag has NOT (0) been set, move to "ouch" MNEXT next 11 # menu choice to display for advancing one page MPREV back 22 # menu choice to display for going back to the previous page MAP something # make the result from "something" available to the template renderer HALT # render template and wait for input INCMP > 11 # handle the "next" menu choice INCMP < 22 # handle to "back" menu choice INCMP _ * # move to the root node on any input
HALT # render template and wait for input INCMP ^ * # move to the root node on any input
The script code contains LOAD
instructions for two different methods.
import ( "context" "fmt" "path" "strings" testdataloader "github.com/peteole/testdata-loader" "git.defalsify.org/vise.git/state" "git.defalsify.org/vise.git/resource" ) const ( USERFLAG_HAVESOMETHING = iota + state.FLAG_USERSTART ) var ( baseDir = testdataloader.GetBasePath() scriptDir = path.Join(baseDir, "examples", "intro") ) type introResource struct { *resource.FsResource c int64 v []string } func newintroResource() introResource { fs := resource.NewFsResource(scriptDir) return introResource{fs, 0, []string{}} } // increment counter. // return a string representing the current value of the counter. func(c *introResource) count(ctx context.Context, sym string, input []byte) (resource.Result, error) { s := "%v time" if c.c != 1 { s += "s" } r := resource.Result{ Content: fmt.Sprintf(s, c.c), } c.c += 1 return r, nil } // if input is suppled, append it to the stored string vector and set the HAVESOMETHING flag. // return the stored string vector value, one string per line. func(c *introResource) something(ctx context.Context, sym string, input []byte) (resource.Result, error) { c.v = append(c.v, string(input)) r := resource.Result{ Content: strings.Join(c.v, "\n"), } if len(input) > 0 { r.FlagSet = []uint32{USERFLAG_HAVESOMETHING} } return r, nil }
In the above example, the more times the foo
page is supplied with a value, the longer the vector of values that need to be displayed by the bar
page will be.
A core feature of vise
is to magically create browseable pages from these values, from a pre-defined maximum output capacity for each page.
Consider the case where the contents of the something
symbol has become:
foo bar baz bazbaz inky pinky blinky clyde
Given a size constaint of 90 characters, the display will be split into two pages:
Thanks for visiting foo and bar. You have written: foo bar baz bazbaz 11:next
Thanks for visiting foo and bar. You have written: inky pinky blinky clyde 22:back
In the source code repository, a full working example of this menu can be found in examples/intro.
To run it:
make -B intro go run ./examples/intro
Use go run -tags logtrace ...
to peek at what is going on under the hood.
To play the Long Values case above, limit the output size by adding -s 90
.