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  }