README.md (5617B)
1 # vise: A Constrained Size Output Virtual Machine 2 3 Consider the following interaction: 4 5 6 ``` 7 This is the root page 8 You have visited 1 time. 9 0:foo 10 1:bar 11 12 $ 1 13 Please visit foo first. 14 Any input to return. 15 16 $ x 17 This is the root page. 18 You have visited 2 times. 19 0:foo 20 1:bar 21 22 $ 0 23 Welcome to page foo. 24 Please write seomthing. 25 26 $ blah blah blah 27 This is the root page. 28 You have visited 3 times. 29 0:foo 30 1:bar 31 32 $ 1 33 Thanks for visiting foo and bar. 34 You have written: 35 blah blah blah 36 ``` 37 38 ## Components 39 40 This simple interface above involves four different menu nodes. 41 42 In order to engineer these using vise, three types of components are involved: 43 44 * An assembly-like menu handling script. 45 * A display template. 46 * External code handlers for the counter and the "something" input. 47 48 49 ## Nodes 50 51 * `root` - The first page 52 * `foo` - The "foo" page 53 * `bar` - The "bar" page after "foo" has been visited 54 * `ouch` - The "bar" page before "foo" has been visited 55 56 57 ## Templates 58 Each page has a template that may or may not contain dynamic elements. 59 60 In this example the root and bar nodes contains dynamic content. 61 62 ### root 63 64 ``` 65 This is the root page 66 You have visited {{.count}}. 67 ``` 68 69 70 ### foo 71 72 ``` 73 Welcome to page foo. 74 Please write something. 75 ``` 76 77 78 ### bar 79 80 ``` 81 Thanks for visiting foo and bar. 82 You have written: 83 {{.something}} 84 ``` 85 86 87 ### ouch 88 89 ``` 90 Please visit foo first. 91 Any input to return. 92 ``` 93 94 95 ## Scripts 96 97 The scripts are responsible for defining menus, handling navigation flow control, and triggering external code handlers. 98 99 ### root 100 101 ``` 102 LOAD count 8 # trigger external code handler "count" 103 LOAD something 0 # trigger external code handler "something" 104 RELOAD count # explicitly trigger "count" every time this code is executed. 105 MAP count # make the result from "count" available to the template renderer 106 MOUT foo 0 # menu item 107 MOUT bar 1 # menu item 108 HALT # render template and wait for input 109 INCMP foo 0 # match menu selection 0, move to node "foo" on match 110 INCMP bar 1 # match menu selection 1, move to node "bar" on match 111 ``` 112 113 ### foo 114 115 ``` 116 HALT # render template and wait for input 117 RELOAD something # pass input to the "something" external code handler. 118 # The input will be appended to the stored value. 119 # The "HAVESOMETHING" flag (8) will be set. 120 MOVE _ # move up one level 121 ``` 122 123 124 ### bar 125 126 ``` 127 CATCH ouch 8 0 # if the "HAVESOMETHING" (8) flag has NOT (0) been set, move to "ouch" 128 MNEXT next 11 # menu choice to display for advancing one page 129 MPREV back 22 # menu choice to display for going back to the previous page 130 MAP something # make the result from "something" available to the template renderer 131 HALT # render template and wait for input 132 INCMP > 11 # handle the "next" menu choice 133 INCMP < 22 # handle to "back" menu choice 134 INCMP _ * # move to the root node on any input 135 ``` 136 137 138 ### ouch 139 140 ``` 141 HALT # render template and wait for input 142 INCMP ^ * # move to the root node on any input 143 ``` 144 145 ## External code handlers 146 147 The script code contains `LOAD` instructions for two different methods. 148 149 ``` 150 import ( 151 "context" 152 "fmt" 153 "path" 154 "strings" 155 156 testdataloader "github.com/peteole/testdata-loader" 157 158 "git.defalsify.org/vise.git/state" 159 "git.defalsify.org/vise.git/resource" 160 ) 161 162 const ( 163 USERFLAG_HAVESOMETHING = iota + state.FLAG_USERSTART 164 ) 165 166 var ( 167 baseDir = testdataloader.GetBasePath() 168 scriptDir = path.Join(baseDir, "examples", "intro") 169 ) 170 171 type introResource struct { 172 *resource.FsResource 173 c int64 174 v []string 175 } 176 177 func newintroResource() introResource { 178 fs := resource.NewFsResource(scriptDir) 179 return introResource{fs, 0, []string{}} 180 } 181 182 // increment counter. 183 // return a string representing the current value of the counter. 184 func(c *introResource) count(ctx context.Context, sym string, input []byte) (resource.Result, error) { 185 s := "%v time" 186 if c.c != 1 { 187 s += "s" 188 } 189 r := resource.Result{ 190 Content: fmt.Sprintf(s, c.c), 191 } 192 c.c += 1 193 return r, nil 194 } 195 196 // if input is suppled, append it to the stored string vector and set the HAVESOMETHING flag. 197 // return the stored string vector value, one string per line. 198 func(c *introResource) something(ctx context.Context, sym string, input []byte) (resource.Result, error) { 199 c.v = append(c.v, string(input)) 200 r := resource.Result{ 201 Content: strings.Join(c.v, "\n"), 202 } 203 if len(input) > 0 { 204 r.FlagSet = []uint32{USERFLAG_HAVESOMETHING} 205 } 206 return r, nil 207 } 208 ``` 209 210 ## Handling long values 211 212 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. 213 214 A core feature of `vise` is to magically create browseable pages from these values from a pre-defined maximum output capacity for each page. 215 216 Consider the case where the contents of the `something` symbol has become: 217 218 ``` 219 foo bar 220 baz bazbaz 221 inky pinky 222 blinky 223 clyde 224 ``` 225 226 Given a size constaint of 90 characters, the display will be split into two pages: 227 228 ``` 229 Thanks for visiting foo and bar. 230 You have written: 231 foo bar 232 baz bazbaz 233 11:next 234 ``` 235 236 ``` 237 Thanks for visiting foo and bar. 238 You have written: 239 inky pinky 240 blinky 241 clyde 242 22:back 243 ``` 244 245 246 ## Working example 247 248 In the source code repository, a full working example of this menu can be found in `examples/intro`. 249 250 To run it: 251 252 ``` 253 make -B intro 254 go run ./examples/intro 255 ``` 256 257 Use `go run -tags logtrace ...` to peek at what is going on under the hood. 258 259 To play the "Handling long values" case above, limit the output size by adding `-s 90`.