github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/examples/sites/syntaxviewer/app.go (about) 1 // Template generated by reactGen 2 3 package main 4 5 import ( 6 "bytes" 7 "fmt" 8 "go/ast" 9 "go/parser" 10 "go/token" 11 "strings" 12 13 "github.com/gopherjs/gopherjs/js" 14 "mvdan.cc/sh/syntax" 15 16 "honnef.co/go/js/dom" 17 "myitcv.io/react" 18 ) 19 20 type AppDef struct { 21 react.ComponentDef 22 } 23 24 //go:generate immutableGen 25 26 type lang string 27 28 const ( 29 langGo lang = "Go" 30 langShell lang = "Shell" 31 ) 32 33 type _Imm_langState struct { 34 Code string 35 Ast string 36 Error bool 37 } 38 39 type AppState struct { 40 Go *langState 41 Shell *langState 42 Choice lang 43 44 listener *hashListener 45 } 46 47 type hashListener struct { 48 o *js.Object 49 50 a AppDef 51 handleEvent func() `js:"handleEvent"` 52 } 53 54 func newHashListenener(a AppDef) *hashListener { 55 res := &hashListener{o: js.Global.Get("Object").New()} 56 res.a = a 57 res.handleEvent = res.handleEventImpl 58 return res 59 } 60 61 func (h *hashListener) handleEventImpl() { 62 hash := lang(strings.TrimPrefix(js.Global.Get("location").Get("hash").String(), "#")) 63 64 switch hash { 65 case langGo, langShell: 66 default: 67 hash = langGo 68 } 69 70 st := h.a.State() 71 st.Choice = hash 72 h.a.SetState(st) 73 } 74 75 func (h *hashListener) attach() { 76 h.handleEventImpl() 77 js.Global.Get("window").Call("addEventListener", "hashchange", h) 78 } 79 80 func (h *hashListener) detach() { 81 js.Global.Get("window").Call("removeEventListener", "hashchange", h) 82 } 83 84 func (a AppState) currLangState() *langState { 85 switch a.Choice { 86 case langGo: 87 return a.Go 88 case langShell: 89 return a.Shell 90 default: 91 panic(fmt.Errorf("unable to handle language %v", a.Choice)) 92 } 93 } 94 95 func (a AppState) setCurrLangState(ls *langState) AppState { 96 switch a.Choice { 97 case langGo: 98 a.Go = ls 99 case langShell: 100 a.Shell = ls 101 default: 102 panic(fmt.Errorf("unable to handle language %v", a.Choice)) 103 } 104 105 return a 106 } 107 108 func App() *AppElem { 109 return buildAppElem() 110 } 111 112 func (a AppDef) GetInitialState() AppState { 113 return AppState{ 114 Go: new(langState), 115 Shell: new(langState), 116 Choice: langGo, 117 118 listener: newHashListenener(a), 119 } 120 } 121 122 func (a AppDef) ComponentDidMount() { 123 a.State().listener.attach() 124 } 125 126 func (a AppDef) ComponentDidUmount() { 127 a.State().listener.detach() 128 } 129 130 func (a AppDef) Render() react.Element { 131 s := a.State() 132 curr := s.currLangState() 133 134 outputClass := "ast" 135 if curr.Error() { 136 outputClass += " asterror" 137 } 138 139 buildLi := func(l lang) *react.LiElem { 140 return react.Li(nil, 141 react.A( 142 &react.AProps{ 143 Href: fmt.Sprintf("#%v", l), 144 }, 145 react.S(l), 146 ), 147 ) 148 } 149 150 return react.Div( 151 &react.DivProps{ClassName: "grid-container"}, 152 react.Div( 153 &react.DivProps{ClassName: "header"}, 154 react.S("Syntax Viewer"), 155 react.Div( 156 &react.DivProps{ClassName: "dropdown", Style: &react.CSS{Float: "right"}}, 157 react.Button( 158 &react.ButtonProps{ 159 ClassName: "btn btn-default dropdown-toggle", 160 Type: "button", 161 ID: "dropdownMenu1", 162 DataSet: react.DataSet{"toggle": "dropdown"}, 163 AriaHasPopup: true, 164 AriaExpanded: true, 165 }, 166 react.Sprintf("%v ", s.Choice), 167 react.Span(&react.SpanProps{ClassName: "caret"}), 168 ), 169 react.Ul( 170 &react.UlProps{ 171 ClassName: "dropdown-menu dropdown-menu-right", 172 AriaLabelledBy: "dropdownMenu1", 173 }, 174 buildLi(langGo), 175 buildLi(langShell), 176 ), 177 ), 178 ), 179 react.Div( 180 &react.DivProps{ClassName: "left"}, 181 react.TextArea( 182 &react.TextAreaProps{ 183 ClassName: "codeinput", 184 Placeholder: fmt.Sprintf("Your %v code here...", s.Choice), 185 Value: curr.Code(), 186 OnChange: inputChange(a), 187 }, 188 ), 189 ), 190 react.Div( 191 &react.DivProps{ClassName: "right"}, 192 react.Pre( 193 &react.PreProps{ClassName: outputClass}, 194 react.S(curr.Ast()), 195 ), 196 ), 197 ) 198 } 199 200 func (a AppDef) handleEvent() { 201 st := a.State().currLangState().AsMutable() 202 defer func() { 203 st.AsImmutable(nil) 204 a.SetState(a.State().setCurrLangState(st)) 205 }() 206 207 st.SetError(true) 208 st.SetAst("") 209 210 if st.Code() == "" { 211 return 212 } 213 214 b := new(bytes.Buffer) 215 216 switch a.State().Choice { 217 case langGo: 218 fset := token.NewFileSet() 219 f, err := parser.ParseFile(fset, "", st.Code(), parser.ParseComments) 220 if err != nil { 221 st.SetAst(err.Error()) 222 return 223 } 224 225 if err := ast.Fprint(b, fset, f, nil); err != nil { 226 st.SetAst(err.Error()) 227 return 228 } 229 230 case langShell: 231 in := strings.NewReader(st.Code()) 232 f, err := syntax.NewParser().Parse(in, "stdin") 233 if err != nil { 234 st.SetAst(err.Error()) 235 return 236 } 237 238 if err := syntax.DebugPrint(b, f); err != nil { 239 st.SetAst(err.Error()) 240 return 241 } 242 243 default: 244 panic(fmt.Errorf("don't know how to handleEvent for %v", a.State().Choice)) 245 } 246 247 st.SetAst(b.String()) 248 st.SetError(false) 249 } 250 251 type changeEvent struct { 252 a AppDef 253 } 254 255 func languageChange(a AppDef, l lang) languageChangeEvent { 256 return languageChangeEvent{ 257 changeEvent: changeEvent{ 258 a: a, 259 }, 260 l: l, 261 } 262 } 263 264 type languageChangeEvent struct { 265 changeEvent 266 l lang 267 } 268 269 func (l languageChangeEvent) OnClick(se *react.SyntheticMouseEvent) { 270 se.PreventDefault() 271 272 st := l.a.State() 273 st.Choice = l.l 274 l.a.SetState(st) 275 276 l.a.handleEvent() 277 } 278 279 func inputChange(a AppDef) inputChangeEvent { 280 return inputChangeEvent{ 281 changeEvent: changeEvent{a: a}, 282 } 283 } 284 285 type inputChangeEvent struct { 286 changeEvent 287 } 288 289 func (i inputChangeEvent) OnChange(se *react.SyntheticEvent) { 290 target := se.Target().(*dom.HTMLTextAreaElement) 291 292 st := i.a.State() 293 st = st.setCurrLangState(st.currLangState().SetCode(target.Value)) 294 i.a.SetState(st) 295 296 i.a.handleEvent() 297 }