├── docs ├── CNAME ├── _config.yml ├── assets │ └── css │ │ └── style.scss ├── snippets.md ├── _layouts │ └── default.html └── README.md ├── _config.yml ├── parser ├── testdata │ ├── packages │ │ ├── one.go │ │ └── one_test.go │ ├── files │ │ ├── file.go │ │ └── file2.go │ ├── otherpackage │ │ └── struct.go │ ├── types │ │ ├── consts.go │ │ ├── interfaces.go │ │ ├── structs.go │ │ ├── funcs.go │ │ └── vars.go │ └── samepackage │ │ └── main.go ├── consts_test.go ├── interfaces_test.go ├── parser_test.go ├── structs_test.go ├── vars_test.go ├── different_package_test.go ├── funcs_test.go ├── importer.go └── parser.go ├── query ├── README.md ├── model_test.go ├── query_test.go └── query.go ├── source ├── testdata │ └── source.go ├── README.md ├── goget_test.go ├── default │ └── source.go ├── goget.go ├── source_test.go └── source.go ├── editor ├── static │ ├── js │ │ └── app.js │ ├── lib │ │ ├── themes │ │ │ └── default │ │ │ │ └── assets │ │ │ │ ├── fonts │ │ │ │ ├── icons.eot │ │ │ │ ├── icons.otf │ │ │ │ ├── icons.ttf │ │ │ │ ├── icons.woff │ │ │ │ └── icons.woff2 │ │ │ │ └── images │ │ │ │ └── flags.png │ │ ├── e-clipboard.v1.5.16.min.js │ │ └── d-riot+compiler.v3.0.5.min.js │ ├── css │ │ └── app.css │ └── tags │ │ ├── codebox.tag │ │ └── editor.tag ├── app.yaml ├── routes.go ├── README.md ├── templates │ ├── editor │ │ └── content.tpl.html │ └── layout.tpl.html ├── code │ ├── default-template.tpl │ └── default-source.go ├── build.sh ├── main.go ├── templates.go ├── preview.go ├── way_test.go └── way.go ├── internal └── version │ └── version.go ├── model ├── README.md └── model.go ├── render ├── testdata │ └── types │ │ ├── consts.go │ │ ├── vars.go │ │ ├── interfaces.go │ │ ├── structs.go │ │ └── funcs.go ├── render_test.go ├── funcs_test.go └── render.go ├── tutorial ├── greeter │ └── greeter.go └── templates │ └── methods.tpl ├── .travis.yml ├── examples └── mocking │ ├── greeter.go │ ├── templates │ └── mock.tpl │ └── greeter_mock.go ├── .gitignore ├── tool ├── tool_test.go └── tool.go ├── README.md ├── main.go └── LICENSE /docs/CNAME: -------------------------------------------------------------------------------- 1 | codeform.in -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal 2 | gems: 3 | - jekyll-sitemap -------------------------------------------------------------------------------- /parser/testdata/packages/one.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | func init() {} 4 | -------------------------------------------------------------------------------- /docs/assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | -------------------------------------------------------------------------------- /parser/testdata/packages/one_test.go: -------------------------------------------------------------------------------- 1 | package pkgname_test 2 | 3 | func init() {} 4 | -------------------------------------------------------------------------------- /parser/testdata/files/file.go: -------------------------------------------------------------------------------- 1 | package files 2 | 3 | type Struct struct { 4 | Field string 5 | } 6 | 7 | var egg int = 1 8 | -------------------------------------------------------------------------------- /query/README.md: -------------------------------------------------------------------------------- 1 | # Codeform Querying 2 | 3 | The `query` package allows you to select specific items from `model.Code` by name. 4 | -------------------------------------------------------------------------------- /source/testdata/source.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import "log" 4 | 5 | func init() { 6 | log.Println("This is a local source file.") 7 | } 8 | -------------------------------------------------------------------------------- /editor/static/js/app.js: -------------------------------------------------------------------------------- 1 | var app = riot.observable() 2 | 3 | app.counter = 1; 4 | 5 | app.uniqueValue = function() { 6 | return app.counter++ 7 | } -------------------------------------------------------------------------------- /editor/static/lib/themes/default/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matryer/codeform/HEAD/editor/static/lib/themes/default/assets/fonts/icons.eot -------------------------------------------------------------------------------- /editor/static/lib/themes/default/assets/fonts/icons.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matryer/codeform/HEAD/editor/static/lib/themes/default/assets/fonts/icons.otf -------------------------------------------------------------------------------- /editor/static/lib/themes/default/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matryer/codeform/HEAD/editor/static/lib/themes/default/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /editor/static/lib/themes/default/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matryer/codeform/HEAD/editor/static/lib/themes/default/assets/fonts/icons.woff -------------------------------------------------------------------------------- /editor/static/lib/themes/default/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matryer/codeform/HEAD/editor/static/lib/themes/default/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /editor/static/lib/themes/default/assets/images/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matryer/codeform/HEAD/editor/static/lib/themes/default/assets/images/flags.png -------------------------------------------------------------------------------- /parser/testdata/otherpackage/struct.go: -------------------------------------------------------------------------------- 1 | package otherpackage 2 | 3 | // ExternalStruct is an exported structure used for testing. 4 | type ExternalStruct struct { 5 | Field int 6 | } 7 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | // Package version holds the version number of the codeform project. 2 | package version 3 | 4 | // Current is the current version. 5 | const Current = "0.6.4" 6 | -------------------------------------------------------------------------------- /parser/testdata/files/file2.go: -------------------------------------------------------------------------------- 1 | package files 2 | 3 | // this file should be ignored because only file.go is 4 | // specified in the test. 5 | 6 | type Struct2 struct{} 7 | 8 | var nope int 9 | -------------------------------------------------------------------------------- /model/README.md: -------------------------------------------------------------------------------- 1 | # Codeform Model 2 | 3 | The Model represents the source code for entire Go projects. 4 | 5 | * View the [Model API documentation](https://godoc.org/github.com/matryer/codeform/model) -------------------------------------------------------------------------------- /editor/app.yaml: -------------------------------------------------------------------------------- 1 | application: codeformapp 2 | version: 1 3 | runtime: go 4 | api_version: go1 5 | 6 | handlers: 7 | - url: /static 8 | static_dir: static 9 | - url: /.* 10 | script: _go_app 11 | -------------------------------------------------------------------------------- /parser/testdata/types/consts.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | const pi float64 = 3.14 4 | 5 | const constantName string = "codeform" 6 | 7 | const ( 8 | c1 int = iota 9 | c2 10 | c3 11 | c4 12 | ) 13 | -------------------------------------------------------------------------------- /render/testdata/types/consts.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | const pi float64 = 3.14 4 | 5 | const constantName string = "codeform" 6 | 7 | const ( 8 | c1 int = iota 9 | c2 10 | c3 11 | c4 12 | ) 13 | -------------------------------------------------------------------------------- /tutorial/greeter/greeter.go: -------------------------------------------------------------------------------- 1 | package greeter 2 | 3 | type Greeter interface { 4 | Greet(name string) (string, error) 5 | Reset() 6 | } 7 | 8 | type Signoff interface { 9 | Signoff(name string) string 10 | } 11 | -------------------------------------------------------------------------------- /tutorial/templates/methods.tpl: -------------------------------------------------------------------------------- 1 | {{- range .Packages }} 2 | {{- range .Interfaces }}{{ $interface := . }} 3 | {{- range .Methods }} 4 | {{ $interface.Name }}.{{ .Name }}{{ . | Signature }} 5 | {{- end }} 6 | {{- end }} 7 | {{- end }} 8 | -------------------------------------------------------------------------------- /parser/testdata/samepackage/main.go: -------------------------------------------------------------------------------- 1 | package samepackage 2 | 3 | import ( 4 | "github.com/matryer/codeform/parser/testdata/otherpackage" 5 | ) 6 | 7 | // Something refers to an external type. 8 | func Something(val *otherpackage.ExternalStruct) {} 9 | -------------------------------------------------------------------------------- /parser/testdata/types/interfaces.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | type Interface1 interface{} 4 | 5 | type Interface2 interface { 6 | Method1() 7 | Method2() 8 | } 9 | 10 | type Interface3 interface { 11 | TheMethod(arg1, arg2 string) error 12 | } 13 | -------------------------------------------------------------------------------- /render/testdata/types/vars.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | var number int 4 | 5 | var name string 6 | 7 | var ( 8 | var1 int 9 | var2 string 10 | var3 bool 11 | ) 12 | 13 | var preset int = 123 14 | 15 | var channel chan []byte 16 | 17 | var amap map[string]int 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | go: 6 | - 1.6.x 7 | - 1.7.x 8 | - 1.8.x 9 | - tip 10 | 11 | before_install: 12 | - go get github.com/golang/lint/golint 13 | 14 | before_script: 15 | - go vet ./... 16 | - golint ./... 17 | 18 | script: 19 | - go test -v ./... 20 | -------------------------------------------------------------------------------- /editor/routes.go: -------------------------------------------------------------------------------- 1 | package editor 2 | 3 | func routes(r *Router) { 4 | r.HandleFunc("POST", "/preview", previewHandler) 5 | r.HandleFunc("GET", "/default-source", defaultSourceHandler) 6 | r.HandleFunc("GET", "/default-template", defaultTemplateHandler) 7 | r.Handle("GET", "/", templateHandler("editor")) 8 | } 9 | -------------------------------------------------------------------------------- /render/testdata/types/interfaces.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | type Interface1 interface{} 4 | 5 | type Person interface { 6 | Greet(name string) (string, error) 7 | ShakeHand(level int) error 8 | Whisper(messages ...string) 9 | } 10 | 11 | type Interface3 interface { 12 | TheMethod(arg1, arg2 string) error 13 | } 14 | -------------------------------------------------------------------------------- /examples/mocking/greeter.go: -------------------------------------------------------------------------------- 1 | package mocking 2 | 3 | //go:generate codeform -src . -out ./greeter_mock.go -templatesrc ./templates/mock.tpl -interface Greeter,Signoff 4 | 5 | type Greeter interface { 6 | Greet(name string) (string, error) 7 | Reset() 8 | } 9 | 10 | type Signoff interface { 11 | Signoff(name string) string 12 | } 13 | -------------------------------------------------------------------------------- /editor/README.md: -------------------------------------------------------------------------------- 1 | # Codeform editor 2 | 3 | The editor is a Google App Engine app. To run it, download the 4 | [Google App Engine SDK for Go](https://cloud.google.com/appengine/docs/go/download) and 5 | in a terminal, run `goapp serve`. 6 | 7 | NOTE: This might change to the `gcloud` command soon - if you find this has happened, please 8 | submit a PR correcting this notice. -------------------------------------------------------------------------------- /editor/templates/editor/content.tpl.html: -------------------------------------------------------------------------------- 1 | {{ define "title" }}Codeform Editor{{ end }} 2 | {{ define "content" }} 3 | 4 | {{ end }} 5 | {{ define "scripts" }} 6 | 7 | 8 | {{ end }} 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | editor/static/dist -------------------------------------------------------------------------------- /editor/code/default-template.tpl: -------------------------------------------------------------------------------- 1 | {{- range .Packages -}} 2 | // Interfaces 3 | {{ range .Interfaces }} 4 | type {{.Name}} interface { 5 | {{- range .Methods }} 6 | {{ .Name }}Func func({{ .Args | ArgList }}) {{ .ReturnArgs | ArgListTypes }} 7 | {{- end }} 8 | } 9 | {{ end }} 10 | 11 | // Structs 12 | {{ range .Structs }} 13 | type {{ .Name }} struct { 14 | {{- range .Fields }} 15 | {{ .Name }} {{ .Type.Name }} 16 | {{- end }} 17 | } 18 | {{ end }} 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /parser/testdata/types/structs.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | type Struct1 struct{} 4 | 5 | type Struct2 struct { 6 | Field1 int `json:"field_one"` 7 | Field2 bool `something:"else"` 8 | Field3 string 9 | } 10 | 11 | func (s *Struct2) Method1(a, b int) error { 12 | return nil 13 | } 14 | 15 | func (Struct2) Method2(a, b, c, d int) error { 16 | return nil 17 | } 18 | 19 | func (*Struct2) Method3(a int) error { 20 | return nil 21 | } 22 | 23 | type Struct3 struct { 24 | other1 Struct1 25 | other2 Struct2 26 | } 27 | -------------------------------------------------------------------------------- /render/testdata/types/structs.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | type Struct1 struct{} 4 | 5 | type Struct2 struct { 6 | Field1 int `json:"field_one"` 7 | Field2 bool `something:"else"` 8 | Field3 string 9 | } 10 | 11 | func (s *Struct2) Method1(a, b int) error { 12 | return nil 13 | } 14 | 15 | func (Struct2) Method2(a, b, c, d int) error { 16 | return nil 17 | } 18 | 19 | func (*Struct2) Method3(a int) error { 20 | return nil 21 | } 22 | 23 | type Struct3 struct { 24 | other1 Struct1 25 | other2 Struct2 26 | } 27 | -------------------------------------------------------------------------------- /editor/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # build.sh 4 | # Packages all files in static/lib into static/dist/all.* files 5 | 6 | echo "Remove old files..." 7 | rm -rf static/dist 8 | mkdir static/dist 9 | 10 | echo "Building all.js..." 11 | cat static/lib/*.js > static/dist/all.js 12 | cat static/js/*.js >> static/dist/all.js 13 | echo "Building all.css..." 14 | cat static/lib/*.css > static/dist/all.css 15 | cat static/css/*.css >> static/dist/all.css 16 | echo "Copying themes..." 17 | cp -R static/lib/themes static/dist/themes/ 18 | echo "Done." 19 | -------------------------------------------------------------------------------- /editor/static/css/app.css: -------------------------------------------------------------------------------- 1 | .page { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-around; 5 | flex-wrap: nowrap; 6 | height: 100%; 7 | padding: 0px 20px 20px 20px; 8 | } 9 | header { 10 | order: 1; 11 | } 12 | .content { 13 | order: 50; 14 | display: flex; 15 | flex: 1 auto; 16 | align-self: stretch; 17 | } 18 | footer { 19 | order: 100; 20 | text-align: right; 21 | margin-top: 20px; 22 | } 23 | 24 | .editor { 25 | display: flex; 26 | flex: 1 auto; 27 | } 28 | 29 | .ui.form textarea:not([rows]) { 30 | max-height: unset; 31 | } -------------------------------------------------------------------------------- /parser/testdata/types/funcs.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | // Func1 is a function. 4 | func Func1() {} 5 | 6 | // Func2 is a function. 7 | func Func2(a int) {} 8 | 9 | // Func3 is a function. 10 | func Func3(a int) int { 11 | return a 12 | } 13 | 14 | // Func4 is a function. 15 | func Func4(a, b int) int { 16 | return a 17 | } 18 | 19 | // Func5 is a function. 20 | func Func5(a, b int) (c int, d int) { 21 | return a, b 22 | } 23 | 24 | // Func6 is a function. 25 | func Func6(int) error { 26 | return nil 27 | } 28 | 29 | // Func7 is a function with a viradic argument. 30 | func Func7(names ...string) {} 31 | -------------------------------------------------------------------------------- /render/testdata/types/funcs.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | // Func1 is a function. 4 | func Func1() {} 5 | 6 | // Func2 is a function. 7 | func Func2(a int) {} 8 | 9 | // Func3 is a function. 10 | func Func3(a int) int { 11 | return a 12 | } 13 | 14 | // Func4 is a function. 15 | func Func4(a, b int) int { 16 | return a 17 | } 18 | 19 | // Func5 is a function. 20 | func Func5(a, b int) (c int, d int) { 21 | return a, b 22 | } 23 | 24 | // Func6 is a function. 25 | func Func6(int) error { 26 | return nil 27 | } 28 | 29 | // Func7 is a function with a viradic argument. 30 | func Func7(names ...string) {} 31 | -------------------------------------------------------------------------------- /parser/testdata/types/vars.go: -------------------------------------------------------------------------------- 1 | package pkgname 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/matryer/codeform/parser/testdata/otherpackage" 7 | ) 8 | 9 | type StructInSameFile struct { 10 | Field int 11 | } 12 | 13 | var number int 14 | 15 | var name string 16 | 17 | var ( 18 | var1 int 19 | var2 string 20 | var3 bool 21 | ) 22 | 23 | var preset int = 123 24 | 25 | var channel chan []byte 26 | 27 | var amap map[string]int 28 | 29 | var customTypePointer *Struct1 30 | 31 | var customType StructInSameFile 32 | 33 | var externalTypePointer *otherpackage.ExternalStruct 34 | 35 | var externalType otherpackage.ExternalStruct 36 | 37 | var r io.Reader 38 | -------------------------------------------------------------------------------- /editor/main.go: -------------------------------------------------------------------------------- 1 | // Package editor provides the editor web tool. 2 | // To run with Google App Engine, install the Google App Engine Go SDK 3 | // and go `goapp serve` in this directory. 4 | package editor 5 | 6 | import ( 7 | "net/http" 8 | 9 | "golang.org/x/net/context" 10 | "google.golang.org/appengine" 11 | ) 12 | 13 | func init() { 14 | http.Handle("/", New()) 15 | } 16 | 17 | // New makes a new http.Handler that provides the editor. 18 | func New() http.Handler { 19 | router := NewRouter() 20 | router.contexter = ContexterFunc(func(r *http.Request) context.Context { 21 | return appengine.NewContext(r) 22 | }) 23 | routes(router) 24 | return router 25 | } 26 | -------------------------------------------------------------------------------- /docs/snippets.md: -------------------------------------------------------------------------------- 1 | # Snippets 2 | 3 | ## Create a struct implementation for each interface 4 | 5 | {% raw %} 6 | ```liquid 7 | {{ range .Packages }}package {{.Name}} 8 | {{- range .Interfaces }} 9 | {{ $interface := . }} 10 | type My{{.Name}} struct{} 11 | {{ range .Methods }} 12 | func (m *My{{.Name}}) {{.Name}}{{ . | Signature}} { 13 | panic("TODO: implement") 14 | } 15 | {{ end }} 16 | {{- end }} 17 | {{ end }} 18 | ``` 19 | {% endraw %} 20 | 21 | ## Package imports 22 | 23 | To import packages mentioned in the source code, use the `.Imports` field on 24 | the package: 25 | 26 | {% raw %} 27 | ```liquid 28 | {{ range .Packages }}package {{.Name}} 29 | import ( 30 | "github.com/explicit/import1" 31 | "github.com/explicit/import2" 32 | {{- range .Imports }} 33 | "{{ .Name }}" 34 | {{ end -}} 35 | ) 36 | {{ end }} 37 | ``` 38 | {% endraw %} 39 | -------------------------------------------------------------------------------- /parser/consts_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/source" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestConsts(t *testing.T) { 11 | is := is.New(t) 12 | code, err := New(source.MustLocal("./testdata/types")).Parse() 13 | is.NoErr(err) // Parse() 14 | is.True(code != nil) 15 | is.Equal(len(code.Packages), 1) 16 | pkg := code.Packages[0] 17 | 18 | is.Equal(len(pkg.Consts), 6) 19 | for _, v := range pkg.Consts { 20 | switch v.Name { 21 | case "pi": 22 | is.Equal(v.Type.Name, "float64") 23 | case "constantName": 24 | is.Equal(v.Type.Name, "string") 25 | case "c1": 26 | is.Equal(v.Type.Name, "int") 27 | case "c2": 28 | is.Equal(v.Type.Name, "int") 29 | case "c3": 30 | is.Equal(v.Type.Name, "int") 31 | case "c4": 32 | is.Equal(v.Type.Name, "int") 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /editor/code/default-source.go: -------------------------------------------------------------------------------- 1 | // Package defaultsource provides a staple source from which templates 2 | // may be written. 3 | // Accessible via a source string of "default". 4 | // This file should contain a mixture of all possible templatable 5 | // features. 6 | package defaultsource 7 | 8 | type Interface1 interface { 9 | Method1() 10 | } 11 | 12 | type Interface2 interface { 13 | Method2A(int) 14 | Method2B(a, b int) 15 | } 16 | 17 | type Interface3 interface { 18 | Method3A() error 19 | Method3B(int) error 20 | Method3C(a, b int) (bool, error) 21 | } 22 | 23 | type Struct1 struct { 24 | Field1 int 25 | } 26 | 27 | type Struct2 struct { 28 | Field1 int 29 | Field2 bool 30 | Field3 string 31 | } 32 | 33 | type Struct3 struct { 34 | Field1 string 35 | } 36 | 37 | func Func1() {} 38 | 39 | func Func2(a, b int) int { 40 | return a 41 | } 42 | 43 | func Func3(a, b int) (bool, error) { 44 | return true, nil 45 | } 46 | -------------------------------------------------------------------------------- /tool/tool_test.go: -------------------------------------------------------------------------------- 1 | package tool_test 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/matryer/codeform/source" 9 | "github.com/matryer/codeform/tool" 10 | "github.com/matryer/is" 11 | ) 12 | 13 | func TestExecute(t *testing.T) { 14 | is := is.New(t) 15 | 16 | srcCode := source.Reader("source.go", strings.NewReader(`package something 17 | 18 | type Inter1 interface { 19 | Inter1Method1(a, b int) error 20 | Inter1Method2(c, d int) error 21 | } 22 | 23 | type Inter2 interface { 24 | Inter2Method1(a, b int) error 25 | Inter2Method2(c, d int) error 26 | }`)) 27 | srcTmpl := source.Reader("template.tpl", strings.NewReader( 28 | `{{ range .Packages }}{{ range .Interfaces }}{{ .Name }} {{ end }}{{ end }}`, 29 | )) 30 | 31 | j := tool.Job{ 32 | Code: srcCode, 33 | Template: srcTmpl, 34 | } 35 | 36 | var buf bytes.Buffer 37 | err := j.Execute(&buf) 38 | is.NoErr(err) 39 | is.Equal(buf.String(), `Inter1 Inter2 `) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /source/README.md: -------------------------------------------------------------------------------- 1 | # Codeform sources 2 | 3 | Sources are how Codeform reads the Go code (and templates) in order to generate output. 4 | 5 | A source is a string that contains a reference to a valid local or online source. 6 | 7 | ## Valid sources 8 | 9 | A source may be: 10 | 11 | 1. A local path to a file 12 | 1. A local path to a package (directory) 13 | 1. A `go get` style path to a package (e.g. `github.com/matryer/codeform`) 14 | 1. A `go get` style path to a nested package (e.g. `github.com/matryer/codeform/render/testdata/types`) 15 | 1. A `go get` style path to a file (e.g. `github.com/matryer/codeform/render/testdata/types/interfaces.go`) 16 | 1. A URL to a file (e.g. `https://raw.githubusercontent.com/matryer/codeform/master/render/testdata/types/interfaces.go`) 17 | 1. `"default"` string indicating the `default/source.go` file 18 | 1. `template:{path}` template from the [github.com/matryer/codeform-templates](https://github.com/matryer/codeform-templates) repository 19 | -------------------------------------------------------------------------------- /editor/templates.go: -------------------------------------------------------------------------------- 1 | package editor 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | 7 | "github.com/matryer/codeform/internal/version" 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | var generalTemplates = []string{"layout"} 12 | 13 | func templateHandler(name string) Handler { 14 | tmplFiles := make([]string, len(generalTemplates)+1) 15 | for i, t := range generalTemplates { 16 | tmplFiles[i] = "./templates/" + t + ".tpl.html" 17 | } 18 | tmplFiles[len(tmplFiles)-1] = "./templates/" + name + "/content.tpl.html" 19 | tmpl, err := template.ParseFiles(tmplFiles...) 20 | return HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 21 | if err != nil { 22 | return err 23 | } 24 | info := pageinfo{ 25 | Version: version.Current, 26 | } 27 | if err := tmpl.ExecuteTemplate(w, "layout", info); err != nil { 28 | return err 29 | } 30 | return nil 31 | }) 32 | } 33 | 34 | type pageinfo struct { 35 | Version string 36 | } 37 | -------------------------------------------------------------------------------- /source/goget_test.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestGoGet(t *testing.T) { 11 | is := is.New(t) 12 | repo, done, err := goget("github.com/matryer/drop-test/explicit") 13 | is.NoErr(err) // goget 14 | defer done() 15 | is.True(strings.HasSuffix(repo, "src/github.com/matryer/drop-test/explicit")) 16 | } 17 | 18 | func TestSplit(t *testing.T) { 19 | is := is.New(t) 20 | var repo, path string 21 | 22 | repo, path = split("github.com/matryer") 23 | is.Equal(repo, "github.com/matryer") 24 | is.Equal(path, "") 25 | 26 | repo, path = split("github.com/matryer/codeform") 27 | is.Equal(repo, "github.com/matryer/codeform") 28 | is.Equal(path, "") 29 | 30 | repo, path = split("github.com/matryer/codeform/source") 31 | is.Equal(repo, "github.com/matryer/codeform") 32 | is.Equal(path, "source") 33 | 34 | repo, path = split("github.com/matryer/codeform/source/testdata") 35 | is.Equal(repo, "github.com/matryer/codeform") 36 | is.Equal(path, "source/testdata") 37 | 38 | } 39 | -------------------------------------------------------------------------------- /parser/interfaces_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/source" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestInterfaces(t *testing.T) { 11 | is := is.New(t) 12 | p := New(source.MustLocal("./testdata/types")) 13 | p.TargetPackage = "another" 14 | code, err := p.Parse() 15 | is.NoErr(err) // Parse() 16 | is.True(code != nil) 17 | is.Equal(len(code.Packages), 1) // should be one package 18 | pkg := code.Packages[0] 19 | 20 | is.Equal(len(pkg.Interfaces), 3) 21 | is.Equal(pkg.Interfaces[0].Name, "Interface1") 22 | is.Equal(pkg.Interfaces[0].Fullname, "pkgname.Interface1") 23 | is.Equal(len(pkg.Interfaces[0].Methods), 0) 24 | 25 | is.Equal(pkg.Interfaces[1].Name, "Interface2") 26 | is.Equal(len(pkg.Interfaces[1].Methods), 2) 27 | is.Equal(pkg.Interfaces[1].Methods[0].Name, "Method1") 28 | is.Equal(pkg.Interfaces[1].Methods[1].Name, "Method2") 29 | 30 | is.Equal(pkg.Interfaces[2].Name, "Interface3") 31 | is.Equal(len(pkg.Interfaces[2].Methods), 1) 32 | is.Equal(pkg.Interfaces[2].Methods[0].Name, "TheMethod") 33 | 34 | } 35 | -------------------------------------------------------------------------------- /examples/mocking/templates/mock.tpl: -------------------------------------------------------------------------------- 1 | // This file was automatically generated by codeform. 2 | // DO NOT EDIT MANUALLY. 3 | // Regenerate with `go generate` 4 | 5 | {{range .Packages}}package {{.Name}} 6 | {{range .Interfaces}}{{$interface := .}} 7 | // Mocked{{$interface.Name}} is a mock implementation of {{$interface.Name}}. 8 | type Mocked{{$interface.Name}} struct { 9 | {{- range .Methods}} 10 | // {{.Name}}Func provides a mock implementation of {{.Name}}. 11 | {{.Name}}Func func{{. | Signature}} 12 | // {{.Name}}Calls represents the number of times {{.Name}} was called. 13 | {{.Name}}Calls int 14 | {{- end}} 15 | } 16 | {{range .Methods}} 17 | // {{.Name}} calls {{.Name}}Func. 18 | func (m *Mocked{{$interface.Name}}) {{.Name}}{{. | Signature}} { 19 | if m.{{.Name}}Func == nil { 20 | panic("call to Mocked{{$interface.Name}}.{{.Name}} with nil {{.Name}}Func") 21 | } 22 | m.{{.Name}}Calls++ 23 | {{- if .ReturnArgs }} 24 | return m.{{.Name}}Func({{ .Args | ArgListNames }}) 25 | {{- else }} 26 | m.{{.Name}}Func({{ .Args | ArgListNames }}) 27 | {{- end }} 28 | } 29 | {{end}} 30 | {{- end}} 31 | {{- end}} -------------------------------------------------------------------------------- /parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/matryer/codeform/parser" 8 | "github.com/matryer/codeform/source" 9 | "github.com/matryer/is" 10 | ) 11 | 12 | func TestFile(t *testing.T) { 13 | is := is.New(t) 14 | code, err := parser.New(source.MustLocal("./testdata/files/file.go")).Parse() 15 | is.NoErr(err) // Parse 16 | is.Equal(len(code.Packages), 1) // should be one package 17 | is.Equal(code.Packages[0].Name, "files") 18 | is.Equal(len(code.Packages[0].Structs), 1) 19 | is.Equal(len(code.Packages[0].Vars), 1) 20 | } 21 | 22 | func TestByteSource(t *testing.T) { 23 | is := is.New(t) 24 | src := source.Reader("./testdata/files/no-such-file.go", strings.NewReader(`package files 25 | 26 | type Struct struct { 27 | Field string 28 | } 29 | 30 | var egg int = 1 31 | `)) 32 | code, err := parser.New(src).Parse() 33 | is.NoErr(err) // Parse 34 | is.Equal(len(code.Packages), 1) 35 | is.Equal(code.Packages[0].Name, "files") 36 | is.Equal(len(code.Packages[0].Structs), 1) 37 | is.Equal(len(code.Packages[0].Vars), 1) 38 | } 39 | -------------------------------------------------------------------------------- /source/default/source.go: -------------------------------------------------------------------------------- 1 | // Package defaultsource provides a staple source from which templates 2 | // may be written. 3 | // Accessible via a source string of "default". 4 | // This file should contain a mixture of all possible templatable 5 | // features. 6 | package defaultsource 7 | 8 | // Interface1 is an interface. 9 | type Interface1 interface { 10 | Method1() 11 | } 12 | 13 | // Interface2 is an interface. 14 | type Interface2 interface { 15 | Method2A(int) 16 | Method2B(a, b int) 17 | } 18 | 19 | // Interface3 is an interface. 20 | type Interface3 interface { 21 | Method3A() error 22 | Method3B(int) error 23 | Method3C(a, b int) (bool, error) 24 | } 25 | 26 | // Struct1 is a struct. 27 | type Struct1 struct { 28 | Field1 int 29 | } 30 | 31 | // Struct2 is a struct. 32 | type Struct2 struct { 33 | Field1 int 34 | Field2 bool 35 | Field3 string 36 | } 37 | 38 | // Struct3 is a struct. 39 | type Struct3 struct { 40 | Field1 string 41 | } 42 | 43 | // Func1 is a function. 44 | func Func1() {} 45 | 46 | // Func2 is a function. 47 | func Func2(a, b int) int { 48 | return a 49 | } 50 | 51 | // Func3 is a function. 52 | func Func3(a, b int) (bool, error) { 53 | return true, nil 54 | } 55 | -------------------------------------------------------------------------------- /query/model_test.go: -------------------------------------------------------------------------------- 1 | package query_test 2 | 3 | import "github.com/matryer/codeform/model" 4 | 5 | var interface1 = model.Interface{Name: "interface1"} 6 | var interface2 = model.Interface{Name: "interface2"} 7 | var interface3 = model.Interface{Name: "interface3"} 8 | var struct1 = model.Struct{Name: "struct1"} 9 | var struct2 = model.Struct{Name: "struct2"} 10 | var struct3 = model.Struct{Name: "struct3"} 11 | var struct4 = model.Struct{Name: "struct4"} 12 | var struct5 = model.Struct{Name: "struct5"} 13 | var struct6 = model.Struct{Name: "struct6"} 14 | var func1 = model.Func{Name: "func1"} 15 | var func2 = model.Func{Name: "func2"} 16 | var func3 = model.Func{Name: "func3"} 17 | 18 | var package1 = model.Package{ 19 | Name: "package1", 20 | Interfaces: []model.Interface{ 21 | interface1, interface2, interface3, 22 | }, 23 | Structs: []model.Struct{ 24 | struct1, struct2, struct3, 25 | }, 26 | Funcs: []model.Func{ 27 | func1, func2, func3, 28 | }, 29 | } 30 | var package2 = model.Package{ 31 | Name: "package2", 32 | Structs: []model.Struct{ 33 | struct4, struct5, struct6, 34 | }, 35 | } 36 | var package3 = model.Package{ 37 | Name: "package3", 38 | Interfaces: []model.Interface{ 39 | interface1, interface2, interface3, 40 | }, 41 | } 42 | 43 | var testcode = &model.Code{ 44 | Packages: []model.Package{ 45 | package1, package2, package3, 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /examples/mocking/greeter_mock.go: -------------------------------------------------------------------------------- 1 | // This file was automatically generated by codeform. 2 | // DO NOT EDIT MANUALLY. 3 | // Regenerate with `go generate` 4 | 5 | package mocking 6 | 7 | // MockedGreeter is a mock implementation of Greeter. 8 | type MockedGreeter struct { 9 | // GreetFunc provides a mock implementation of Greet. 10 | GreetFunc func(name string) (string, error) 11 | // GreetCalls represents the number of times Greet was called. 12 | GreetCalls int 13 | // ResetFunc provides a mock implementation of Reset. 14 | ResetFunc func() 15 | // ResetCalls represents the number of times Reset was called. 16 | ResetCalls int 17 | } 18 | 19 | // Greet calls GreetFunc. 20 | func (m *MockedGreeter) Greet(name string) (string, error) { 21 | if m.GreetFunc == nil { 22 | panic("call to MockedGreeter.Greet with nil GreetFunc") 23 | } 24 | m.GreetCalls++ 25 | return m.GreetFunc(name) 26 | } 27 | 28 | // Reset calls ResetFunc. 29 | func (m *MockedGreeter) Reset() { 30 | if m.ResetFunc == nil { 31 | panic("call to MockedGreeter.Reset with nil ResetFunc") 32 | } 33 | m.ResetCalls++ 34 | m.ResetFunc() 35 | } 36 | 37 | // MockedSignoff is a mock implementation of Signoff. 38 | type MockedSignoff struct { 39 | // SignoffFunc provides a mock implementation of Signoff. 40 | SignoffFunc func(name string) string 41 | // SignoffCalls represents the number of times Signoff was called. 42 | SignoffCalls int 43 | } 44 | 45 | // Signoff calls SignoffFunc. 46 | func (m *MockedSignoff) Signoff(name string) string { 47 | if m.SignoffFunc == nil { 48 | panic("call to MockedSignoff.Signoff with nil SignoffFunc") 49 | } 50 | m.SignoffCalls++ 51 | return m.SignoffFunc(name) 52 | } 53 | -------------------------------------------------------------------------------- /parser/structs_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/source" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestStructs(t *testing.T) { 11 | is := is.New(t) 12 | p := New(source.MustLocal("./testdata/types")) 13 | p.TargetPackage = "another" 14 | code, err := p.Parse() 15 | is.NoErr(err) // Parse() 16 | is.True(code != nil) 17 | is.Equal(len(code.Packages), 1) // should be one package 18 | pkg := code.Packages[0] 19 | 20 | is.Equal(len(pkg.Structs), 4) 21 | is.Equal(pkg.Structs[0].Name, "Struct1") 22 | is.Equal(pkg.Structs[0].Fullname, "pkgname.Struct1") 23 | is.Equal(pkg.Structs[1].Name, "Struct2") 24 | is.Equal(pkg.Structs[1].Fullname, "pkgname.Struct2") 25 | 26 | // fields 27 | is.Equal(len(pkg.Structs[0].Fields), 0) 28 | is.Equal(len(pkg.Structs[1].Fields), 3) 29 | is.Equal(pkg.Structs[1].Fields[0].Name, "Field1") 30 | is.Equal(pkg.Structs[1].Fields[0].Type.Name, "int") 31 | is.Equal(pkg.Structs[1].Fields[0].Tag, `json:"field_one"`) 32 | is.Equal(pkg.Structs[1].Fields[1].Name, "Field2") 33 | is.Equal(pkg.Structs[1].Fields[1].Type.Name, "bool") 34 | is.Equal(pkg.Structs[1].Fields[1].Tag, `something:"else"`) 35 | is.Equal(pkg.Structs[1].Fields[2].Name, "Field3") 36 | is.Equal(pkg.Structs[1].Fields[2].Type.Name, "string") 37 | is.Equal(pkg.Structs[1].Fields[2].Tag, ``) 38 | 39 | // methods 40 | is.Equal(len(pkg.Structs[1].Methods), 3) 41 | for _, method := range pkg.Structs[1].Methods { 42 | switch method.Name { 43 | case "Method1": 44 | is.Equal(len(method.Args), 2) 45 | case "Method2": 46 | is.Equal(len(method.Args), 4) 47 | case "Method3": 48 | is.Equal(len(method.Args), 1) 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /source/goget.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | // split breaks the repo and path out of the source. 12 | // So that github.com/matryer/codeform/source becomes 13 | // "github.com/matryer/codeform" and "source" 14 | func split(src string) (string, string) { 15 | segs := strings.Split(src, "/") 16 | if len(segs) > 3 { 17 | return strings.Join(segs[0:3], "/"), strings.Join(segs[3:], "/") 18 | } 19 | return src, "" 20 | } 21 | 22 | func goget(src string) (string, func(), error) { 23 | done := func() {} 24 | tmp, err := ioutil.TempDir(os.TempDir(), ".codeform-") 25 | if err != nil { 26 | return "", done, err 27 | } 28 | done = func() { 29 | os.RemoveAll(tmp) 30 | } 31 | gopath := filepath.Join(tmp, "codeform-gopath") 32 | gopath, err = filepath.Abs(gopath) 33 | if err != nil { 34 | return "", done, err 35 | } 36 | err = os.MkdirAll(gopath, 0777) 37 | if err != nil { 38 | return "", done, err 39 | } 40 | source, path := split(src) 41 | //info("go get -d", source) 42 | goget := exec.Command("go", "get", "-d", source) 43 | env := []string{"GOPATH=" + gopath} // control GOPATH for this command 44 | env = append(env, os.Environ()...) // but use rest of normal environment 45 | goget.Env = env 46 | 47 | // Omit error, `go get -d ` exits with status 1 if the package is not buildable 48 | _, _ = goget.CombinedOutput() 49 | fullpath := filepath.Join(gopath, "src", source, path) 50 | return fullpath, done, nil 51 | } 52 | 53 | type errGoGet struct { 54 | err error 55 | source string 56 | output []byte 57 | } 58 | 59 | func (e errGoGet) Error() string { 60 | return "go get " + e.source + ": " + e.err.Error() + ": " + string(e.output) 61 | } 62 | -------------------------------------------------------------------------------- /editor/templates/layout.tpl.html: -------------------------------------------------------------------------------- 1 | {{ define "layout" }} 2 | 3 | 4 | {{ template "title" . }} 5 | 6 | 7 | 8 |
9 |
10 | 30 |
31 |
32 | {{ template "content" . }} 33 |
34 | 47 |
48 | 49 | {{ template "scripts" . }} 50 | 55 | 56 | 57 | {{ end }} 58 | {{ define "content" }}{{ end }} 59 | {{ define "title" }}{{ end }} 60 | {{ define "scripts" }}{{ end }} 61 | -------------------------------------------------------------------------------- /editor/preview.go: -------------------------------------------------------------------------------- 1 | package editor 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "log" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/matryer/codeform/source" 11 | "github.com/matryer/codeform/tool" 12 | "golang.org/x/net/context" 13 | ) 14 | 15 | type previewRequest struct { 16 | Template string 17 | Source string 18 | } 19 | type previewResponse struct { 20 | Output string `json:"output"` 21 | } 22 | 23 | func previewHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 24 | var req previewRequest 25 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 26 | return err 27 | } 28 | codeSource := source.Reader("source.go", strings.NewReader(req.Source)) 29 | templateSource := source.Reader("template.tpl", strings.NewReader(req.Template)) 30 | j := tool.Job{ 31 | Code: codeSource, 32 | Template: templateSource, 33 | } 34 | var buf bytes.Buffer 35 | if err := j.Execute(&buf); err != nil { 36 | respond(ctx, w, r, err, http.StatusBadRequest) 37 | return nil 38 | } 39 | res := previewResponse{ 40 | Output: buf.String(), 41 | } 42 | return respond(ctx, w, r, res, http.StatusOK) 43 | } 44 | 45 | func defaultSourceHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 46 | http.ServeFile(w, r, "./code/default-source.go") 47 | return nil 48 | } 49 | 50 | func defaultTemplateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 51 | log.Println("serving:", "./code/default-source.go") 52 | http.ServeFile(w, r, "./code/default-template.tpl") 53 | return nil 54 | } 55 | 56 | func respond(ctx context.Context, w http.ResponseWriter, r *http.Request, data interface{}, code int) error { 57 | if err, ok := data.(error); ok { 58 | data = struct { 59 | Error string `json:"error"` 60 | }{Error: err.Error()} 61 | } 62 | w.Header().Set("Content-Type", "application/json") 63 | w.WriteHeader(code) 64 | return json.NewEncoder(w).Encode(data) 65 | } 66 | -------------------------------------------------------------------------------- /parser/vars_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/source" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestVars(t *testing.T) { 11 | is := is.New(t) 12 | code, err := New(source.MustLocal("./testdata/types")).Parse() 13 | is.NoErr(err) // Parse() 14 | is.True(code != nil) 15 | is.Equal(len(code.Packages), 1) // should be one package 16 | pkg := code.Packages[0] 17 | 18 | is.Equal(len(pkg.Vars), 13) 19 | for _, v := range pkg.Vars { 20 | switch v.Name { 21 | case "var1": 22 | is.Equal(v.Type.Fullname, "int") 23 | is.Equal(v.Type.Name, "int") 24 | case "var2": 25 | is.Equal(v.Type.Fullname, "string") 26 | is.Equal(v.Type.Name, "string") 27 | case "var3": 28 | is.Equal(v.Type.Fullname, "bool") 29 | is.Equal(v.Type.Name, "bool") 30 | case "number": 31 | is.Equal(v.Type.Fullname, "int") 32 | is.Equal(v.Type.Name, "int") 33 | case "name": 34 | is.Equal(v.Type.Fullname, "string") 35 | is.Equal(v.Type.Name, "string") 36 | case "preset": 37 | is.Equal(v.Type.Fullname, "int") 38 | is.Equal(v.Type.Name, "int") 39 | case "channel": 40 | is.Equal(v.Type.Fullname, "chan []byte") 41 | is.Equal(v.Type.Name, "chan []byte") 42 | case "amap": 43 | is.Equal(v.Type.Fullname, "map[string]int") 44 | is.Equal(v.Type.Name, "map[string]int") 45 | case "customType": 46 | is.Equal(v.Type.Fullname, "StructInSameFile") 47 | is.Equal(v.Type.Name, "StructInSameFile") 48 | case "customTypePointer": 49 | is.Equal(v.Type.Fullname, "*Struct1") 50 | is.Equal(v.Type.Name, "*Struct1") 51 | case "externalTypePointer": 52 | is.Equal(v.Type.Fullname, "*otherpackage.ExternalStruct") 53 | is.Equal(v.Type.Name, "ExternalStruct") 54 | case "externalType": 55 | is.Equal(v.Type.Fullname, "otherpackage.ExternalStruct") 56 | is.Equal(v.Type.Name, "ExternalStruct") 57 | case "r": 58 | is.Equal(v.Type.Fullname, "io.Reader") 59 | is.Equal(v.Type.Name, "Reader") 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /query/query_test.go: -------------------------------------------------------------------------------- 1 | package query_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/query" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestQueryNothing(t *testing.T) { 11 | is := is.New(t) 12 | code, err := query.New().Run(*testcode) 13 | is.NoErr(err) 14 | is.Equal(len(code.Packages), 3) 15 | } 16 | 17 | func TestQueryName(t *testing.T) { 18 | is := is.New(t) 19 | code, err := query.New().Package("package1", "package2").Name("interface1", "struct5").Run(*testcode) 20 | is.NoErr(err) 21 | is.Equal(len(code.Packages), 2) 22 | is.Equal(len(code.Packages[0].Interfaces), 1) 23 | is.Equal(code.Packages[0].Interfaces[0].Name, "interface1") 24 | is.Equal(len(code.Packages[1].Structs), 1) 25 | is.Equal(code.Packages[1].Structs[0].Name, "struct5") 26 | } 27 | 28 | func TestQueryPackage(t *testing.T) { 29 | is := is.New(t) 30 | code, err := query.New().Package("package1").Interface("interface1", "interface2").Run(*testcode) 31 | is.NoErr(err) 32 | is.Equal(len(code.Packages), 1) // should not find interfaces in package3 33 | is.Equal(len(code.Packages[0].Interfaces), 2) 34 | } 35 | 36 | func TestQueryInterface(t *testing.T) { 37 | is := is.New(t) 38 | code, err := query.New().Package("package1").Interface("interface1", "interface2").Run(*testcode) 39 | is.NoErr(err) 40 | is.Equal(len(code.Packages), 1) 41 | is.Equal(len(code.Packages[0].Interfaces), 2) 42 | is.Equal(code.Packages[0].Interfaces[0].Name, "interface1") 43 | is.Equal(code.Packages[0].Interfaces[1].Name, "interface2") 44 | } 45 | 46 | func TestQueryStruct(t *testing.T) { 47 | is := is.New(t) 48 | code, err := query.New().Struct("struct2", "struct3").Run(*testcode) 49 | is.NoErr(err) 50 | is.Equal(len(code.Packages), 1) 51 | is.Equal(len(code.Packages[0].Structs), 2) 52 | is.Equal(code.Packages[0].Structs[0].Name, "struct2") 53 | is.Equal(code.Packages[0].Structs[1].Name, "struct3") 54 | } 55 | 56 | func TestQueryFunc(t *testing.T) { 57 | is := is.New(t) 58 | code, err := query.New().Func("func2", "func3").Run(*testcode) 59 | is.NoErr(err) 60 | is.Equal(len(code.Packages), 1) 61 | is.Equal(len(code.Packages[0].Funcs), 2) 62 | is.Equal(code.Packages[0].Funcs[0].Name, "func2") 63 | is.Equal(code.Packages[0].Funcs[1].Name, "func3") 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codeform [![Build Status](https://travis-ci.org/matryer/codeform.svg?branch=master)](https://travis-ci.org/matryer/codeform) [![Go Report Card](https://goreportcard.com/badge/github.com/matryer/codeform)](https://goreportcard.com/report/github.com/matryer/codeform) 2 | Go code generation framework. 3 | 4 | * Generate output from Go code using templates 5 | * Interact with a simple model (see [Model documentation](https://godoc.org/github.com/matryer/codeform/model)) 6 | * Write templates using the [online editor](http://editor.codeform.in/) (with live preview) 7 | * Contribute to a [repository of shared templates](https://github.com/matryer/codeform-templates) 8 | 9 | ## Get started 10 | 11 | To install Codeform, get it: 12 | 13 | ```bash 14 | go get github.com/matryer/codeform/... 15 | ``` 16 | 17 | This will install the `codeform` tool, as well as make the codeform 18 | packages available to you. 19 | 20 | ## `codeform` comamnd line tool 21 | 22 | The `codeform` tool allows you to generate output using Go templates. 23 | 24 | ``` 25 | -src string 26 | code source (default ".") 27 | -srcin 28 | take source from standard in 29 | -template string 30 | template verbatim 31 | -templatesrc string 32 | template source 33 | -out string 34 | output file (defaults to standard out) 35 | 36 | -timeout duration 37 | timeout for HTTP requests (default 2s) 38 | -v verbose logging 39 | -n suppress final line feed 40 | -version 41 | print version and exit 42 | 43 | -func string 44 | comma separated list of funcs to include 45 | -interface string 46 | comma separated list of interfaces to include 47 | -name string 48 | comma separated list of things to include 49 | -package string 50 | comma separated list of packages to include 51 | -struct string 52 | comma separated list of structs to include 53 | ``` 54 | 55 | * See the [sources documentation](https://github.com/matryer/codeform/tree/master/source) for an overview of acceptable values for `src` and `templatesrc` 56 | 57 | # Advanced 58 | 59 | If you want to generate output using Go code instead of templates, you can 60 | import the `github.com/matryer/codeform/parser` package directly and interact 61 | with the model yourself (see [Model documentation](https://godoc.org/github.com/matryer/codeform/model)). 62 | -------------------------------------------------------------------------------- /render/render_test.go: -------------------------------------------------------------------------------- 1 | package render_test 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "html/template" 7 | "testing" 8 | 9 | "strings" 10 | 11 | "github.com/matryer/codeform/parser" 12 | "github.com/matryer/codeform/render" 13 | "github.com/matryer/codeform/source" 14 | "github.com/matryer/is" 15 | ) 16 | 17 | func TestRender(t *testing.T) { 18 | is := is.New(t) 19 | code, err := parser.New(source.MustLocal("./testdata/types")).Parse() 20 | is.NoErr(err) 21 | tmpl, err := template.New("Name").Funcs(render.TemplateFuncs).Parse(testTemplate) 22 | is.NoErr(err) 23 | var buf bytes.Buffer 24 | err = tmpl.Execute(&buf, code) 25 | is.NoErr(err) 26 | // check that each line of expected appears in the output 27 | actual := buf.String() 28 | s := bufio.NewScanner(strings.NewReader(expected)) 29 | for s.Scan() { 30 | is.True(strings.Contains(actual, s.Text())) 31 | } 32 | } 33 | 34 | var testTemplate = `{{ range .Packages }}package {{ .Name }} 35 | {{- range .Interfaces }} 36 | {{- $interface := . }} 37 | 38 | type {{.Name}}Mock struct { 39 | {{- range .Methods }} 40 | {{ .Name }}Func func({{ .Args | ArgList }}) {{ .ReturnArgs | ArgListTypes }} 41 | {{- end }} 42 | } 43 | 44 | {{- range .Methods }} 45 | func (m *{{$interface.Name}}Mock) {{.Name}}({{ .Args | ArgList }}) {{ .ReturnArgs | ArgListTypes }} { 46 | {{- if .ReturnArgs }} 47 | return m.{{.Name}}Func({{ .Args | ArgListNames }}) 48 | {{- else }} 49 | m.{{.Name}}Func({{ .Args | ArgListNames }}) 50 | {{- end }} 51 | } 52 | {{- end }} 53 | {{- end }} 54 | {{- end -}}` 55 | 56 | var expected = `package pkgname 57 | 58 | type Interface1Mock struct { 59 | } 60 | 61 | type Interface3Mock struct { 62 | TheMethodFunc func(arg1 string, arg2 string) error 63 | } 64 | func (m *Interface3Mock) TheMethod(arg1 string, arg2 string) error { 65 | return m.TheMethodFunc(arg1, arg2) 66 | } 67 | 68 | type PersonMock struct { 69 | GreetFunc func(name string) (string, error) 70 | ShakeHandFunc func(level int) error 71 | WhisperFunc func(messages ...string) 72 | } 73 | func (m *PersonMock) Greet(name string) (string, error) { 74 | return m.GreetFunc(name) 75 | } 76 | func (m *PersonMock) ShakeHand(level int) error { 77 | return m.ShakeHandFunc(level) 78 | } 79 | func (m *PersonMock) Whisper(messages ...string) { 80 | m.WhisperFunc(messages...) 81 | }` 82 | -------------------------------------------------------------------------------- /editor/static/tags/codebox.tag: -------------------------------------------------------------------------------- 1 | 2 | 36 |
37 |
38 |
39 |
40 | 41 | 44 |
45 | 80 |
-------------------------------------------------------------------------------- /tool/tool.go: -------------------------------------------------------------------------------- 1 | // Package tool does the work of rendering output from templates. 2 | package tool 3 | 4 | import ( 5 | "fmt" 6 | "html/template" 7 | "io" 8 | "io/ioutil" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/matryer/codeform/parser" 13 | "github.com/matryer/codeform/query" 14 | "github.com/matryer/codeform/render" 15 | "github.com/matryer/codeform/source" 16 | "github.com/pkg/errors" 17 | ) 18 | 19 | // Job is a single job. 20 | type Job struct { 21 | Code *source.Source 22 | Template *source.Source 23 | TargetPackage string 24 | Names []string 25 | Packages []string 26 | Interfaces []string 27 | Structs []string 28 | Funcs []string 29 | Log func(...interface{}) 30 | } 31 | 32 | // Execute runs the job and writes the output to the specified io.Writer. 33 | func (j *Job) Execute(w io.Writer) error { 34 | p := parser.New(j.Code) 35 | p.TargetPackage = j.TargetPackage 36 | code, err := p.Parse() 37 | if err != nil { 38 | j.logf("failed to parse source: %s", err) 39 | return errors.Wrap(err, "parser") 40 | } 41 | q := query.New() 42 | q.Name(j.Names...) 43 | if len(j.Names) > 0 { 44 | j.logf("including: %s", strings.Join(j.Names, ", ")) 45 | } 46 | q.Package(j.Packages...) 47 | if len(j.Packages) > 0 { 48 | j.logf("including packages: %s", strings.Join(j.Packages, ", ")) 49 | } 50 | q.Interface(j.Interfaces...) 51 | if len(j.Interfaces) > 0 { 52 | j.logf("including interfaces: %s", strings.Join(j.Interfaces, ", ")) 53 | } 54 | q.Struct(j.Structs...) 55 | if len(j.Structs) > 0 { 56 | j.logf("including structs: %s", strings.Join(j.Structs, ", ")) 57 | } 58 | q.Func(j.Funcs...) 59 | if len(j.Funcs) > 0 { 60 | j.logf("including funcs: %s", strings.Join(j.Funcs, ", ")) 61 | } 62 | code, err = q.Run(*code) 63 | if err != nil { 64 | j.logf("failed to apply query: %s", err) 65 | return errors.Wrap(err, "query") 66 | } 67 | tmplB, err := ioutil.ReadAll(j.Template) 68 | if err != nil { 69 | j.logf("failed to read template: %s", err) 70 | return errors.Wrap(err, "reading template") 71 | } 72 | templateName := filepath.Base(j.Template.Path) 73 | j.logf("executing template %s", templateName) 74 | tmpl, err := template.New(templateName).Funcs(render.TemplateFuncs).Parse(string(tmplB)) 75 | if err != nil { 76 | j.logf("failed to parse template: %s", err) 77 | return errors.Wrap(err, "parsing template") 78 | } 79 | return tmpl.Execute(w, code) 80 | } 81 | 82 | func (j *Job) log(args ...interface{}) { 83 | if j.Log != nil { 84 | j.Log(args...) 85 | } 86 | } 87 | func (j *Job) logf(format string, args ...interface{}) { 88 | j.log(fmt.Sprintf(format, args...)) 89 | } 90 | -------------------------------------------------------------------------------- /parser/different_package_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/source" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestDifferentPackage(t *testing.T) { 11 | is := is.New(t) 12 | p := New(source.MustLocal("./testdata/samepackage")) 13 | 14 | p.TargetPackage = "" 15 | code, err := p.Parse() 16 | is.NoErr(err) // Parse() 17 | is.True(code != nil) 18 | is.Equal(len(code.Packages), 1) 19 | is.Equal(len(code.Packages[0].Funcs), 1) 20 | is.Equal(len(code.Packages[0].Funcs[0].Args), 1) 21 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Name, "ExternalStruct") 22 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Fullname, "*otherpackage.ExternalStruct") 23 | is.Equal(code.TargetPackageName, "samepackage") 24 | is.Equal(len(code.Packages[0].Imports), 1) 25 | is.Equal(code.Packages[0].Imports[0].Name, "github.com/matryer/codeform/parser/testdata/otherpackage") 26 | 27 | p.TargetPackage = "github.com/matryer/codeform/parser/testdata/samepackage" 28 | code, err = p.Parse() 29 | is.NoErr(err) // Parse() 30 | is.True(code != nil) 31 | is.Equal(len(code.Packages), 1) 32 | is.Equal(len(code.Packages[0].Funcs), 1) 33 | is.Equal(len(code.Packages[0].Funcs[0].Args), 1) 34 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Name, "ExternalStruct") 35 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Fullname, "*otherpackage.ExternalStruct") 36 | is.Equal(code.TargetPackageName, "samepackage") 37 | is.Equal(len(code.Packages[0].Imports), 1) 38 | is.Equal(code.Packages[0].Imports[0].Name, "github.com/matryer/codeform/parser/testdata/otherpackage") 39 | 40 | p.TargetPackage = "github.com/matryer/codeform/parser/testdata/otherpackage" 41 | code, err = p.Parse() 42 | is.NoErr(err) // Parse() 43 | is.True(code != nil) 44 | is.Equal(len(code.Packages), 1) 45 | is.Equal(len(code.Packages[0].Funcs), 1) 46 | is.Equal(len(code.Packages[0].Funcs[0].Args), 1) 47 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Name, "*ExternalStruct") 48 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Fullname, "*ExternalStruct") 49 | is.Equal(code.TargetPackageName, "otherpackage") 50 | is.Equal(len(code.Packages[0].Imports), 0) 51 | 52 | p.TargetPackage = "github.com/matryer/codeform/parser/testdata/otherpackage" 53 | code, err = p.Parse() 54 | is.NoErr(err) // Parse() 55 | is.True(code != nil) 56 | is.Equal(len(code.Packages), 1) 57 | is.Equal(len(code.Packages[0].Funcs), 1) 58 | is.Equal(len(code.Packages[0].Funcs[0].Args), 1) 59 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Name, "*ExternalStruct") 60 | is.Equal(code.Packages[0].Funcs[0].Args[0].Type.Fullname, "*ExternalStruct") 61 | is.Equal(code.TargetPackageName, "otherpackage") 62 | is.Equal(len(code.Packages[0].Imports), 0) 63 | 64 | } 65 | -------------------------------------------------------------------------------- /render/funcs_test.go: -------------------------------------------------------------------------------- 1 | package render_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/model" 7 | "github.com/matryer/codeform/render" 8 | "github.com/matryer/is" 9 | ) 10 | 11 | func TestCamel(t *testing.T) { 12 | is := is.New(t) 13 | is.Equal(render.Camel("SomeService"), "someService") 14 | is.Equal(render.Camel("someService"), "someService") 15 | is.Equal(render.Camel("A"), "a") 16 | is.Equal(render.Camel("a"), "a") 17 | is.Equal(render.Camel(""), "") 18 | } 19 | 20 | func TestArgList(t *testing.T) { 21 | is := is.New(t) 22 | args := model.Args{ 23 | {Name: "name", Type: model.Type{Name: "string"}}, 24 | {Name: "age", Type: model.Type{Name: "int"}}, 25 | {Name: "ok", Type: model.Type{Name: "bool"}}, 26 | } 27 | is.Equal(render.ArgList(args), `name string, age int, ok bool`) 28 | args = model.Args{ 29 | {Name: "", Anonymous: true, Type: model.Type{Name: "string"}}, 30 | {Name: "age", Type: model.Type{Name: "int"}}, 31 | {Name: "ok", Type: model.Type{Name: "bool"}}, 32 | } 33 | is.Equal(render.ArgList(args), `arg1 string, age int, ok bool`) 34 | is.Equal(render.ArgList(nil), ``) 35 | } 36 | 37 | func TestArgListTypes(t *testing.T) { 38 | is := is.New(t) 39 | args := model.Args{ 40 | {Name: "err", Type: model.Type{Name: "error"}}, 41 | } 42 | is.Equal(render.ArgListTypes(args), `error`) 43 | args = model.Args{ 44 | {Name: "name", Type: model.Type{Name: "string"}}, 45 | {Name: "age", Type: model.Type{Name: "int"}}, 46 | {Name: "ok", Type: model.Type{Name: "bool"}}, 47 | } 48 | is.Equal(render.ArgListTypes(args), `(string, int, bool)`) 49 | is.Equal(render.ArgListTypes(nil), ``) 50 | } 51 | 52 | func TestArgListNames(t *testing.T) { 53 | is := is.New(t) 54 | args := model.Args{ 55 | {Name: "name", Type: model.Type{Name: "string"}}, 56 | {Name: "age", Type: model.Type{Name: "int"}}, 57 | {Name: "ok", Type: model.Type{Name: "bool"}}, 58 | } 59 | is.Equal(render.ArgListNames(args), `name, age, ok`) 60 | args = model.Args{ 61 | {Name: "", Anonymous: true, Type: model.Type{Name: "string"}}, 62 | {Name: "age", Type: model.Type{Name: "int"}}, 63 | {Name: "ok", Type: model.Type{Name: "bool"}}, 64 | } 65 | is.Equal(render.ArgListNames(args), `arg1, age, ok`) 66 | is.Equal(render.ArgListNames(nil), ``) 67 | } 68 | 69 | func TestSignature(t *testing.T) { 70 | is := is.New(t) 71 | fn := model.Func{ 72 | Args: model.Args{ 73 | {Name: "name", Type: model.Type{Name: "string"}}, 74 | {Name: "age", Type: model.Type{Name: "int"}}, 75 | {Name: "ok", Type: model.Type{Name: "bool"}}, 76 | }, 77 | ReturnArgs: model.Args{ 78 | {Name: "", Type: model.Type{Name: "string"}}, 79 | {Name: "", Type: model.Type{Name: "error"}}, 80 | }, 81 | } 82 | is.Equal(render.Signature(fn), `(name string, age int, ok bool) (string, error)`) 83 | } 84 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Codeform - by Mat Ryer 7 | 8 | 9 | 12 | 17 | 18 | 19 |
20 |
21 |

Codeform

22 |

{{ site.description | default: site.github.project_tagline }}

23 |

Snippets

24 |
25 | 26 |

More stuff

27 | 28 |

29 | Online Editor 30 |
31 | Templates repo 32 |
33 | Data Model documentation 34 |
35 | Template helpers 36 |

37 | 38 |

View the Project on GitHub

39 | 40 | {% if site.show_downloads %} 41 | 46 | {% endif %} 47 |
48 |
49 | 50 | {{ content }} 51 | 52 |
53 | 58 |
59 | 60 | 61 | 62 | {% if site.google_analytics %} 63 | 67 | 73 | {% endif %} 74 | 75 | -------------------------------------------------------------------------------- /parser/funcs_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/codeform/source" 7 | "github.com/matryer/is" 8 | ) 9 | 10 | func TestPackages(t *testing.T) { 11 | is := is.New(t) 12 | code, err := New(source.MustLocal("./testdata/packages")).Parse() 13 | is.NoErr(err) // Parse() 14 | is.True(code != nil) 15 | is.Equal(len(code.Packages), 2) 16 | for _, pkg := range code.Packages { 17 | switch pkg.Name { 18 | case "pkgname", "pkgname_test": 19 | default: 20 | is.Fail() // bad package name 21 | } 22 | } 23 | } 24 | 25 | func TestFuncs(t *testing.T) { 26 | is := is.New(t) 27 | code, err := New(source.MustLocal("./testdata/types")).Parse() 28 | is.NoErr(err) // Parse() 29 | is.True(code != nil) 30 | is.Equal(len(code.Packages), 1) // should be one package 31 | is.Equal(len(code.Packages[0].Funcs), 7) 32 | 33 | is.Equal(code.Packages[0].Funcs[0].Name, "Func1") 34 | is.Equal(len(code.Packages[0].Funcs[0].Args), 0) 35 | 36 | is.Equal(code.Packages[0].Funcs[1].Name, "Func2") 37 | is.Equal(len(code.Packages[0].Funcs[1].Args), 1) 38 | is.Equal(code.Packages[0].Funcs[1].Args[0].Anonymous, false) 39 | is.Equal(code.Packages[0].Funcs[1].Args[0].Name, "a") 40 | is.Equal(code.Packages[0].Funcs[1].Args[0].Type.Name, "int") 41 | 42 | is.Equal(code.Packages[0].Funcs[2].Name, "Func3") 43 | is.Equal(len(code.Packages[0].Funcs[2].Args), 1) 44 | is.Equal(len(code.Packages[0].Funcs[2].ReturnArgs), 1) 45 | is.Equal(code.Packages[0].Funcs[2].ReturnArgs[0].Anonymous, true) 46 | is.Equal(code.Packages[0].Funcs[2].ReturnArgs[0].Name, "") 47 | is.Equal(code.Packages[0].Funcs[2].ReturnArgs[0].Type.Name, "int") 48 | 49 | is.Equal(code.Packages[0].Funcs[3].Name, "Func4") 50 | is.Equal(len(code.Packages[0].Funcs[3].Args), 2) 51 | 52 | is.Equal(code.Packages[0].Funcs[4].Name, "Func5") 53 | is.Equal(len(code.Packages[0].Funcs[4].Args), 2) 54 | is.Equal(len(code.Packages[0].Funcs[4].ReturnArgs), 2) 55 | is.Equal(code.Packages[0].Funcs[4].ReturnArgs[0].Anonymous, false) 56 | is.Equal(code.Packages[0].Funcs[4].ReturnArgs[0].Name, "c") 57 | is.Equal(code.Packages[0].Funcs[4].ReturnArgs[0].Type.Name, "int") 58 | is.Equal(code.Packages[0].Funcs[4].ReturnArgs[1].Anonymous, false) 59 | is.Equal(code.Packages[0].Funcs[4].ReturnArgs[1].Name, "d") 60 | is.Equal(code.Packages[0].Funcs[4].ReturnArgs[1].Type.Name, "int") 61 | 62 | is.Equal(code.Packages[0].Funcs[5].Name, "Func6") 63 | is.Equal(code.Packages[0].Funcs[5].Variadic, false) 64 | is.Equal(len(code.Packages[0].Funcs[5].Args), 1) 65 | is.Equal(code.Packages[0].Funcs[5].Args[0].Anonymous, true) 66 | is.Equal(code.Packages[0].Funcs[5].Args[0].Name, "") 67 | is.Equal(code.Packages[0].Funcs[5].Args[0].Type.Name, "int") 68 | 69 | is.Equal(code.Packages[0].Funcs[6].Name, "Func7") 70 | is.Equal(code.Packages[0].Funcs[6].Variadic, true) 71 | is.Equal(len(code.Packages[0].Funcs[6].Args), 1) 72 | is.Equal(code.Packages[0].Funcs[6].Args[0].Anonymous, false) 73 | is.Equal(code.Packages[0].Funcs[6].Args[0].Name, "names") 74 | is.Equal(code.Packages[0].Funcs[6].Args[0].Variadic, true) 75 | is.Equal(code.Packages[0].Funcs[6].Args[0].Type.Name, "[]string") 76 | 77 | } 78 | -------------------------------------------------------------------------------- /parser/importer.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "errors" 5 | "go/ast" 6 | "go/parser" 7 | "go/token" 8 | "go/types" 9 | "io/ioutil" 10 | "os" 11 | "path" 12 | "path/filepath" 13 | "strings" 14 | "sync" 15 | ) 16 | 17 | // CREDIT 18 | // Most of this was stolen from https://github.com/ernesto-jimenez/gogen/blob/master/importer/importer.go - Copyright (c) 2015 Ernesto Jiménez 19 | // which has an MIT license 20 | 21 | var gopathlistOnce sync.Once 22 | var gopathlistCache []string 23 | 24 | func gopathlist() []string { 25 | gopathlistOnce.Do(func() { 26 | gopathlistCache = filepath.SplitList(os.Getenv("GOPATH")) 27 | }) 28 | return gopathlistCache 29 | } 30 | 31 | // smartImporter is an importer.Importer that looks in the usual places 32 | // for packages. 33 | type smartImporter struct { 34 | base types.Importer 35 | packages map[string]*types.Package 36 | } 37 | 38 | func newSmartImporter(base types.Importer) *smartImporter { 39 | return &smartImporter{ 40 | base: base, 41 | packages: make(map[string]*types.Package), 42 | } 43 | } 44 | 45 | func (i *smartImporter) Import(path string) (*types.Package, error) { 46 | var err error 47 | if path == "" || path[0] == '.' { 48 | path, err = filepath.Abs(filepath.Clean(path)) 49 | if err != nil { 50 | return nil, err 51 | } 52 | path = stripGopath(path) 53 | } 54 | if pkg, ok := i.packages[path]; ok { 55 | return pkg, nil 56 | } 57 | pkg, err := i.doimport(path) 58 | if err != nil { 59 | return nil, err 60 | } 61 | i.packages[path] = pkg 62 | return pkg, nil 63 | } 64 | 65 | func (i *smartImporter) doimport(p string) (*types.Package, error) { 66 | dir, err := lookupImport(p) 67 | if err != nil { 68 | return i.base.Import(p) 69 | } 70 | dirFiles, err := ioutil.ReadDir(dir) 71 | if err != nil { 72 | return i.base.Import(p) 73 | } 74 | fset := token.NewFileSet() 75 | var files []*ast.File 76 | for _, fileInfo := range dirFiles { 77 | if fileInfo.IsDir() { 78 | continue 79 | } 80 | n := fileInfo.Name() 81 | if path.Ext(fileInfo.Name()) != ".go" { 82 | continue 83 | } 84 | // if i.skipTestFiles && strings.Contains(fileInfo.Name(), "_test.go") { 85 | // continue 86 | // } 87 | file := path.Join(dir, n) 88 | src, err := ioutil.ReadFile(file) 89 | if err != nil { 90 | return nil, err 91 | } 92 | f, err := parser.ParseFile(fset, file, src, 0) 93 | if err != nil { 94 | return nil, err 95 | } 96 | files = append(files, f) 97 | } 98 | conf := types.Config{ 99 | Importer: i, 100 | } 101 | pkg, err := conf.Check(p, fset, files, nil) 102 | if err != nil { 103 | return i.base.Import(p) 104 | } 105 | return pkg, nil 106 | } 107 | 108 | func lookupImport(p string) (string, error) { 109 | for _, gopath := range gopathlist() { 110 | absPath, err := filepath.Abs(path.Join(gopath, "src", p)) 111 | if err != nil { 112 | return "", err 113 | } 114 | if dir, err := os.Stat(absPath); err == nil && dir.IsDir() { 115 | return absPath, nil 116 | } 117 | } 118 | return "", errors.New("not in GOPATH: " + p) 119 | } 120 | 121 | func stripGopath(p string) string { 122 | for _, gopath := range gopathlist() { 123 | p = strings.Replace(p, path.Join(gopath, "src")+"/", "", 1) 124 | } 125 | return p 126 | } 127 | -------------------------------------------------------------------------------- /model/model.go: -------------------------------------------------------------------------------- 1 | // Package model contains the types that describe Go code. 2 | package model 3 | 4 | // Code represents the Go code as a data model. 5 | type Code struct { 6 | // Packages represents a list of packages. 7 | Packages []Package `json:"packages"` 8 | // TargetPackageName is the name of the target package. 9 | TargetPackageName string 10 | } 11 | 12 | // Package represents a single Go package. 13 | type Package struct { 14 | // Name is the name of the package. 15 | Name string `json:"name"` 16 | // Imports is a list of packages that need importing. 17 | Imports []Import `json:"imports"` 18 | // Funcs represents the global functions. 19 | Funcs []Func `json:"funcs"` 20 | // Interfaces represents the global interfaces in the 21 | // package. 22 | Interfaces []Interface `json:"interfaces"` 23 | // Vars are the global variables defined in this 24 | // package. 25 | Vars []Var `json:"vars"` 26 | // Consts are the global constants defined in this 27 | // package. 28 | Consts []Var `json:"consts"` 29 | // Structs represents the global structs defined in this 30 | // package. 31 | Structs []Struct `json:"structs"` 32 | } 33 | 34 | // Import represents an imported package. 35 | type Import struct { 36 | Name string `json:"name"` 37 | } 38 | 39 | // Struct represents a struct. 40 | type Struct struct { 41 | // Name is the name of the struct. 42 | Name string `json:"name"` 43 | // Fullname is the full code name of the struct. 44 | // It may be: 45 | // package.Struct 46 | Fullname string `json:"fullname"` 47 | // Methods are the functions that have this struct as 48 | // its receiver. 49 | Methods []Func `json:"methods"` 50 | // Fields are the fields in this struct. 51 | Fields []Var `json:"fields"` 52 | } 53 | 54 | // Interface represents an interface. 55 | type Interface struct { 56 | // Name is the name of the interface. 57 | Name string `json:"name"` 58 | // Fullname is the full code name of the interface. 59 | // It may be: 60 | // package.Interface 61 | Fullname string `json:"fullname"` 62 | // Methods is the methods that make up this 63 | // interface. 64 | Methods []Func `json:"methods"` 65 | } 66 | 67 | // Func represents a function. 68 | type Func struct { 69 | // Name is the name of the function. 70 | Name string `json:"name"` 71 | // Args represents the input arguments. 72 | Args Args `json:"args"` 73 | // ReturnArgs represents the output arguments. 74 | ReturnArgs Args `json:"returnArgs"` 75 | // Variadic indicates whether the final input argument is 76 | // variadic or not. 77 | Variadic bool `json:"variadic"` 78 | } 79 | 80 | // Args represents a set of argument variables. 81 | type Args []Var 82 | 83 | // Var represents a variable. 84 | type Var struct { 85 | // Name is the name of this variable. 86 | Name string `json:"name"` 87 | // Anonymous indicates whether the variable has 88 | // a name or not. 89 | Anonymous bool `json:"anonymous"` 90 | // Type is the type of this variable. 91 | Type Type `json:"type"` 92 | // Tag is the field tag for this variable or an empty 93 | // string if it doesn't have one. 94 | Tag string `json:"tag"` 95 | // Variadic indicates whether this argument is 96 | // variadic or not. 97 | Variadic bool `json:"variadic"` 98 | } 99 | 100 | // Type represents a type. 101 | type Type struct { 102 | // Name is the name of the type. 103 | Name string `json:"name"` 104 | // Fullname is the full code name of the type. 105 | // It may be: 106 | // package.Interface 107 | Fullname string `json:"fullname"` 108 | } 109 | -------------------------------------------------------------------------------- /render/render.go: -------------------------------------------------------------------------------- 1 | // Package render provides utilities for rendering code using 2 | // templates. 3 | // 4 | // Writing templates 5 | // 6 | // Codeform makes use of Go's text/template package, it is recommended 7 | // that you become familiar with Go templates starting with the documentation 8 | // at https://golang.org/pkg/text/template/. 9 | // 10 | // Using template functions 11 | // 12 | // Template functions are helpers that solve common rendering problems, 13 | // such as writing a function signature, or listing argument types, etc. 14 | // 15 | // To use a template function, type its name following a pipe character after 16 | // the item you wish to render. For example, the following template snippet 17 | // will render all methods using the Signature helper: 18 | // 19 | // {{- range .Methods }} 20 | // {{ .Name }}Func func({{ . | Signature }} 21 | // {{- end }} 22 | // 23 | // Template functions 24 | // 25 | // To use the utility functions in the Go template packages, call Funcs 26 | // passing in render.TemplateFuncs. 27 | // 28 | // template.New("templatename").Funcs(render.TemplateFuncs) 29 | package render 30 | 31 | import ( 32 | "fmt" 33 | "strings" 34 | 35 | "github.com/matryer/codeform/model" 36 | ) 37 | 38 | // TemplateFuncs represents codeform utilities for templates. 39 | var TemplateFuncs = map[string]interface{}{ 40 | "ArgList": ArgList, 41 | "ArgListNames": ArgListNames, 42 | "ArgListTypes": ArgListTypes, 43 | "Signature": Signature, 44 | "Camel": Camel, 45 | } 46 | 47 | // Camel turns a string into camel case. 48 | func Camel(s string) string { 49 | if len(s) < 2 { 50 | return strings.ToLower(s) 51 | } 52 | return strings.ToLower(s[0:1]) + s[1:] 53 | } 54 | 55 | // ArgList turns model.Args into a Go argument list. 56 | // name string, age int, ok bool 57 | // Anonymous variables are given a name. 58 | func ArgList(args model.Args) string { 59 | list := make([]string, len(args)) 60 | for i, arg := range args { 61 | name := arg.Name 62 | if len(name) == 0 { 63 | name = fmt.Sprintf("arg%d", i+1) 64 | } 65 | typ := arg.Type.Name 66 | if arg.Variadic { 67 | typ = "..." + typ[2:] 68 | } 69 | list[i] = name + " " + typ 70 | } 71 | return strings.Join(list, ", ") 72 | } 73 | 74 | // ArgListNames turns model.Args into a comma separated list of names. 75 | // name, age, ok 76 | // Anonymous variables are given a name. 77 | func ArgListNames(args model.Args) string { 78 | list := make([]string, len(args)) 79 | for i, arg := range args { 80 | name := arg.Name 81 | if len(name) == 0 { 82 | name = fmt.Sprintf("arg%d", i+1) 83 | } 84 | if arg.Variadic { 85 | name += "..." 86 | } 87 | list[i] = name 88 | } 89 | return strings.Join(list, ", ") 90 | } 91 | 92 | // ArgListTypes turns model.Args into a comma separated list of types. 93 | // (string, int, bool) 94 | // Parentheses will be added for multiple arguments. 95 | func ArgListTypes(args model.Args) string { 96 | list := make([]string, len(args)) 97 | for i, arg := range args { 98 | typ := arg.Type.Name 99 | if arg.Variadic { 100 | typ = "..." + typ[2:] 101 | } 102 | list[i] = typ 103 | } 104 | if len(list) < 2 { 105 | return strings.Join(list, ", ") 106 | } 107 | return "(" + strings.Join(list, ", ") + ")" 108 | } 109 | 110 | // Signature gets the function arguments and return arguments 111 | // as a string. 112 | // (name string, age int) (string, error) 113 | func Signature(fn model.Func) string { 114 | sig := "(" + ArgList(fn.Args) + ")" 115 | if len(fn.ReturnArgs) > 0 { 116 | sig += " " + ArgListTypes(fn.ReturnArgs) 117 | } 118 | return sig 119 | } 120 | -------------------------------------------------------------------------------- /editor/way_test.go: -------------------------------------------------------------------------------- 1 | package editor 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | var tests = []struct { 12 | RouteMethod string 13 | RoutePattern string 14 | 15 | Method string 16 | Path string 17 | Match bool 18 | Params map[string]string 19 | }{ 20 | // simple path matching 21 | { 22 | "GET", "/one", 23 | "GET", "/one", true, nil, 24 | }, 25 | { 26 | "GET", "/two", 27 | "GET", "/two", true, nil, 28 | }, 29 | { 30 | "GET", "/three", 31 | "GET", "/three", true, nil, 32 | }, 33 | // methods 34 | { 35 | "get", "/methodcase", 36 | "GET", "/methodcase", true, nil, 37 | }, 38 | { 39 | "Get", "/methodcase", 40 | "get", "/methodcase", true, nil, 41 | }, 42 | { 43 | "GET", "/methodcase", 44 | "get", "/methodcase", true, nil, 45 | }, 46 | { 47 | "GET", "/method1", 48 | "POST", "/method1", false, nil, 49 | }, 50 | { 51 | "DELETE", "/method2", 52 | "GET", "/method2", false, nil, 53 | }, 54 | { 55 | "GET", "/method3", 56 | "PUT", "/method3", false, nil, 57 | }, 58 | // all methods 59 | { 60 | "*", "/all-methods", 61 | "GET", "/all-methods", true, nil, 62 | }, 63 | { 64 | "*", "/all-methods", 65 | "POST", "/all-methods", true, nil, 66 | }, 67 | { 68 | "*", "/all-methods", 69 | "PUT", "/all-methods", true, nil, 70 | }, 71 | // nested 72 | { 73 | "GET", "/parent/child/one", 74 | "GET", "/parent/child/one", true, nil, 75 | }, 76 | { 77 | "GET", "/parent/child/two", 78 | "GET", "/parent/child/two", true, nil, 79 | }, 80 | { 81 | "GET", "/parent/child/three", 82 | "GET", "/parent/child/three", true, nil, 83 | }, 84 | // slashes 85 | { 86 | "GET", "slashes/one", 87 | "GET", "/slashes/one", true, nil, 88 | }, 89 | { 90 | "GET", "/slashes/two", 91 | "GET", "slashes/two", true, nil, 92 | }, 93 | { 94 | "GET", "slashes/three/", 95 | "GET", "/slashes/three", true, nil, 96 | }, 97 | { 98 | "GET", "/slashes/four", 99 | "GET", "slashes/four/", true, nil, 100 | }, 101 | // prefix 102 | { 103 | "GET", "/prefix/", 104 | "GET", "/prefix/anything/else", true, nil, 105 | }, 106 | { 107 | "GET", "/not-prefix", 108 | "GET", "/not-prefix/anything/else", false, nil, 109 | }, 110 | // path params 111 | { 112 | "GET", "/path-param/:id", 113 | "GET", "/path-param/123", true, map[string]string{"id": "123"}, 114 | }, 115 | { 116 | "GET", "/path-params/:era/:group/:member", 117 | "GET", "/path-params/60s/beatles/lennon", true, map[string]string{ 118 | "era": "60s", 119 | "group": "beatles", 120 | "member": "lennon", 121 | }, 122 | }, 123 | { 124 | "GET", "/path-params-prefix/:era/:group/:member/", 125 | "GET", "/path-params-prefix/60s/beatles/lennon/yoko", true, map[string]string{ 126 | "era": "60s", 127 | "group": "beatles", 128 | "member": "lennon", 129 | }, 130 | }, 131 | // misc no matches 132 | { 133 | "GET", "/not/enough", 134 | "GET", "/not/enough/items", false, nil, 135 | }, 136 | { 137 | "GET", "/not/enough/items", 138 | "GET", "/not/enough", false, nil, 139 | }, 140 | } 141 | 142 | func TestWay(t *testing.T) { 143 | for _, test := range tests { 144 | r := NewRouter() 145 | r.contexter = ContexterBackground() 146 | match := false 147 | var passedCtx context.Context 148 | r.Handle(test.RouteMethod, test.RoutePattern, HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 149 | match = true 150 | passedCtx = ctx 151 | return nil 152 | })) 153 | req, err := http.NewRequest(test.Method, test.Path, nil) 154 | if err != nil { 155 | t.Errorf("NewRequest: %s", err) 156 | } 157 | w := httptest.NewRecorder() 158 | r.ServeHTTP(w, req) 159 | if match != test.Match { 160 | t.Errorf("expected match %v but was %v: %s %s", test.Match, match, test.Method, test.Path) 161 | } 162 | if len(test.Params) > 0 { 163 | for expK, expV := range test.Params { 164 | // check using helper 165 | actualValStr := Param(passedCtx, expK) 166 | if actualValStr != expV { 167 | t.Errorf("Param: context value %s expected \"%s\" but was \"%s\"", expK, expV, actualValStr) 168 | } 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | --- 4 | 5 | # Welcome 6 | 7 | Welcome to Codeform, easy Go code generation using templates. 8 | 9 | * Familiar [Go templates](https://golang.org/pkg/text/template/) 10 | * [Simple data model](https://godoc.org/github.com/matryer/codeform/model) 11 | * [Online editor](http://editor.codeform.in) with live preview 12 | 13 | We are working towards a stable 1.0 release, but the project is entirely usable today. 14 | Please get involved. 15 | 16 | # How it works 17 | 18 | Write [templates](https://golang.org/pkg/text/template/) using the 19 | [simple data model](https://godoc.org/github.com/matryer/codeform/model) 20 | and use the `codeform` tool to generate code. 21 | 22 | # Tutorials 23 | 24 | To play with Codeform, head over to the [Online Editor](http://editor.codeform.in) 25 | where you can modify the template, and see a live preview. 26 | 27 | ## Codeform command line tool 28 | 29 | ### Install Codeform 30 | 31 | To install Codeform, ensure you have [Go installed](https://golang.org/dl/) and in a terminal: 32 | 33 | ```bash 34 | go get github.com/matryer/codeform/... 35 | ``` 36 | 37 | This will install Codeform from source, including adding the `codeform` command to your `$GOBIN` 38 | directory. 39 | 40 | ### Ensure Codeform is installed 41 | 42 | In a terminal, do: 43 | 44 | ```bash 45 | codeform -version 46 | ``` 47 | 48 | You should see the latest Codeform version printed in a terminal. If you get an error, 49 | ensure your `$GOBIN` is added to your `$PATH`. 50 | 51 | ### Find example code 52 | 53 | Navigate to the `$GOPATH/src/github.com/matryer/codeform/tutorial/greeter` folder and look 54 | at the `greeter.go` file: 55 | 56 | ```bash 57 | cd $GOPATH/src/github.com/matryer/codeform/tutorial/greeter 58 | cat greeter.go 59 | ``` 60 | 61 | The code contains two interfaces: 62 | 63 | ```go 64 | package greeter 65 | 66 | type Greeter interface { 67 | Greet(name string) (string, error) 68 | Reset() 69 | } 70 | 71 | type Signoff interface { 72 | Signoff(name string) string 73 | } 74 | ``` 75 | 76 | ### Generate code using hosted template 77 | 78 | The [Codeform Templates repository](https://github.com/matryer/codeform-templates) contains some shared 79 | templates that you are free to use. To access them, provide a template source beginning with the prefix 80 | `template:` followed by the path to the template. 81 | 82 | In a terminal, let's use the interface inspection template: 83 | 84 | ```bash 85 | codeform -src greeter.go -templatesrc template:inspect/interfaces 86 | ``` 87 | 88 | You should get the following output: 89 | 90 | ``` 91 | greeter.Greeter 92 | greeter.Signoff 93 | ``` 94 | 95 | ### Providing our own template 96 | 97 | We are going to provide a simple template that outputs the package name. 98 | 99 | The template (expanded) will look like this: 100 | 101 | {% raw %} 102 | ```liquid 103 | {{ range .Packages }} 104 | {{ .Name }} 105 | {{ end }} 106 | ``` 107 | {% endraw %} 108 | 109 | We can provide this template as a flag to the `codeform` command: 110 | 111 | {% raw %} 112 | ```bash 113 | codeform -src greeter.go -template "{{ range .Packages }}{{ .Name }}{{ end }}" 114 | ``` 115 | {% endraw %} 116 | 117 | You should see the following output: 118 | 119 | ``` 120 | greeter 121 | ``` 122 | 123 | ### Using a template file 124 | 125 | Create a new file called `methods.tpl` and populate it with the following code: 126 | 127 | {% raw %} 128 | ```liquid 129 | {{- range .Packages }} 130 | {{- range .Interfaces }}{{ $interface := . }} 131 | {{- range .Methods }} 132 | {{ $interface.Name }}.{{ .Name }}{{ . | Signature }} 133 | {{- end }} 134 | {{- end }} 135 | {{- end }} 136 | ``` 137 | {% endraw %} 138 | 139 | Now provide the file via the `templatesrc` flag: 140 | 141 | ```bash 142 | codeform -src greeter.go -templatesrc /path/to/methods.tpl 143 | ``` 144 | 145 | You should see a list of interface methods: 146 | 147 | ``` 148 | Greeter.Greet(name string) (string, error) 149 | Greeter.Reset() 150 | Signoff.Signoff(name string) string 151 | ``` 152 | 153 | ### What next? 154 | 155 | Do `codeform -help` for a complete list of available flags. 156 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "strings" 12 | "time" 13 | 14 | "github.com/matryer/codeform/internal/version" 15 | "github.com/matryer/codeform/source" 16 | "github.com/matryer/codeform/tool" 17 | "github.com/pkg/errors" 18 | ) 19 | 20 | func main() { 21 | var fatalErr error 22 | defer func() { 23 | if fatalErr != nil { 24 | io.WriteString(os.Stderr, "codeform: ") 25 | io.WriteString(os.Stderr, fatalErr.Error()+"\n") 26 | os.Exit(1) 27 | } 28 | }() 29 | var ( 30 | src = flag.String("src", ".", "code source") 31 | srcin = flag.Bool("srcin", false, "take source from standard in") 32 | templatesrc = flag.String("templatesrc", "", "template source") 33 | template = flag.String("template", "", "template verbatim") 34 | targetPackage = flag.String("pkg", "", "target package") 35 | names = flag.String("name", "", "comma separated list of things to include") 36 | packages = flag.String("package", "", "comma separated list of packages to include") 37 | interfaces = flag.String("interface", "", "comma separated list of interfaces to include") 38 | structs = flag.String("struct", "", "comma separated list of structs to include") 39 | funcs = flag.String("func", "", "comma separated list of funcs to include") 40 | out = flag.String("out", "", "output file (defaults to standard out)") 41 | timeout = flag.Duration("timeout", 2*time.Second, "timeout for HTTP requests") 42 | verbose = flag.Bool("v", false, "verbose logging") 43 | nolinefeed = flag.Bool("n", false, "suppress final linefeed") 44 | printVersion = flag.Bool("version", false, "print version and exit") 45 | ) 46 | flag.Usage = func() { 47 | fmt.Println("codeform", "v"+version.Current) 48 | flag.PrintDefaults() 49 | } 50 | flag.Parse() 51 | if *printVersion { 52 | fmt.Println(version.Current) 53 | return 54 | } 55 | log := func(args ...interface{}) {} 56 | logf := func(format string, args ...interface{}) { 57 | log(fmt.Sprintf(format, args...)) 58 | } 59 | if *verbose { 60 | log = func(args ...interface{}) { 61 | fmt.Println(args...) 62 | } 63 | } 64 | var err error 65 | sourceLookup := source.Lookup{Client: http.Client{Timeout: *timeout}} 66 | if len(*src) == 0 && !*srcin { 67 | fatalErr = errors.New("provide src") 68 | return 69 | } 70 | logf("lookup: %s", *src) 71 | var codeSource *source.Source 72 | if *srcin { 73 | if len(*src) > 0 { 74 | logf("srcin: ignoring src") 75 | } 76 | codeSource = source.Reader("stdin", os.Stdin) 77 | } else { 78 | codeSource, err = sourceLookup.Get(*src) 79 | if err != nil { 80 | fatalErr = errors.Wrap(err, "lookup code source") 81 | return 82 | } 83 | } 84 | defer codeSource.Close() 85 | var templateSource *source.Source 86 | if len(*template)+len(*templatesrc) == 0 { 87 | fatalErr = errors.New("provide templatesrc or template") 88 | return 89 | } 90 | if len(*template) > 0 { 91 | if len(*templatesrc) > 0 { 92 | fatalErr = errors.New("provide templatesrc or template (not both)") 93 | return 94 | } 95 | log("using verbatim template") 96 | templateSource = source.Reader("verbatim-template", strings.NewReader(*template)) 97 | } else { 98 | logf("lookup: %s", *templatesrc) 99 | templateSource, err = sourceLookup.Get(*templatesrc) 100 | if err != nil { 101 | fatalErr = errors.Wrap(err, "lookup template source") 102 | return 103 | } 104 | } 105 | defer templateSource.Close() 106 | var buf bytes.Buffer 107 | var w io.Writer 108 | w = &buf 109 | if len(*out) == 0 { 110 | w = os.Stdout 111 | } 112 | job := tool.Job{ 113 | Log: log, 114 | Code: codeSource, 115 | Template: templateSource, 116 | Names: splitArgList(*names), 117 | Packages: splitArgList(*packages), 118 | Interfaces: splitArgList(*interfaces), 119 | Structs: splitArgList(*structs), 120 | Funcs: splitArgList(*funcs), 121 | TargetPackage: *targetPackage, 122 | } 123 | err = job.Execute(w) 124 | if err != nil { 125 | fatalErr = err 126 | return 127 | } 128 | if !*nolinefeed { 129 | w.Write([]byte("\n")) 130 | } 131 | if len(*out) > 0 { 132 | // save file 133 | err = ioutil.WriteFile(*out, buf.Bytes(), 0644) 134 | if err != nil { 135 | fatalErr = err 136 | return 137 | } 138 | } 139 | } 140 | 141 | func splitArgList(s string) []string { 142 | if len(s) == 0 { 143 | return []string{} 144 | } 145 | return strings.Split(s, ",") 146 | } 147 | -------------------------------------------------------------------------------- /source/source_test.go: -------------------------------------------------------------------------------- 1 | package source_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | "time" 11 | 12 | "github.com/matryer/codeform/source" 13 | "github.com/matryer/is" 14 | ) 15 | 16 | // lookup is a source lookup that specifies a 17 | // timeout on http requests. 18 | var lookup = source.Lookup{ 19 | Client: http.Client{ 20 | Timeout: 2 * time.Second, 21 | }, 22 | } 23 | 24 | func TestReader(t *testing.T) { 25 | is := is.New(t) 26 | s := source.Reader("verbatim.go", strings.NewReader(`This is a verbatim source file.`)) 27 | defer s.Close() 28 | is.Equal(s.IsDir, false) 29 | is.Equal(s.Path, "verbatim.go") 30 | b, err := ioutil.ReadAll(s) 31 | is.NoErr(err) 32 | is.True(strings.Contains(string(b), `This is a verbatim source file.`)) 33 | } 34 | 35 | func TestLocal(t *testing.T) { 36 | is := is.New(t) 37 | s, err := lookup.Get("./testdata/source.go") 38 | is.NoErr(err) 39 | defer s.Close() 40 | is.Equal(s.IsDir, false) 41 | is.Equal(s.Path, "./testdata/source.go") 42 | b, err := ioutil.ReadAll(s) 43 | is.NoErr(err) 44 | is.True(strings.Contains(string(b), `This is a local source file.`)) 45 | } 46 | 47 | func TestTemplate(t *testing.T) { 48 | is := is.New(t) 49 | s, err := lookup.Get("template:inspect/packages") 50 | is.NoErr(err) 51 | defer s.Close() 52 | is.Equal(s.IsDir, false) 53 | is.True(strings.HasSuffix(s.Path, "github.com/matryer/codeform-templates/inspect/packages.tpl")) 54 | b, err := ioutil.ReadAll(s) 55 | is.NoErr(err) 56 | is.True(strings.Contains(string(b), `{{range .Packages}}`)) 57 | } 58 | 59 | func TestDefault(t *testing.T) { 60 | is := is.New(t) 61 | s, err := lookup.Get("default") 62 | is.NoErr(err) 63 | defer s.Close() 64 | is.Equal(s.IsDir, false) 65 | is.True(strings.HasSuffix(s.Path, "github.com/matryer/codeform/source/default/source.go")) 66 | b, err := ioutil.ReadAll(s) 67 | is.NoErr(err) 68 | is.True(strings.Contains(string(b), `package defaultsource`)) 69 | } 70 | 71 | func TestURL(t *testing.T) { 72 | is := is.New(t) 73 | s, err := lookup.Get("https://raw.githubusercontent.com/matryer/drop-test/master/greet.go") 74 | is.NoErr(err) 75 | defer s.Close() 76 | is.Equal(s.IsDir, false) 77 | is.Equal(s.Path, "https://raw.githubusercontent.com/matryer/drop-test/master/greet.go") 78 | b, err := ioutil.ReadAll(s) 79 | is.NoErr(err) 80 | is.True(strings.Contains(string(b), `func Greet(name string) string`)) 81 | } 82 | 83 | func TestGoGet(t *testing.T) { 84 | is := is.New(t) 85 | s, err := lookup.Get("github.com/matryer/drop-test/explicit") 86 | is.NoErr(err) 87 | defer s.Close() 88 | is.Equal(s.IsDir, true) 89 | is.True(strings.HasSuffix(s.Path, `/src/github.com/matryer/drop-test/explicit`)) 90 | } 91 | 92 | // TestGopath tests that the local GOPATH is checked. 93 | func TestGoPath(t *testing.T) { 94 | is := is.New(t) 95 | originalGopath := os.Getenv("GOPATH") 96 | defer func() { 97 | os.Setenv("GOPATH", originalGopath) 98 | }() 99 | tmpgopath := filepath.Join(os.TempDir(), "codeform-test-gopath") 100 | tmpfiledir := filepath.Join(tmpgopath, "src/github.com/matryer/codeform-test") 101 | tmpfile := filepath.Join(tmpfiledir, "file.go") 102 | err := os.MkdirAll(tmpfiledir, 0777) 103 | is.NoErr(err) 104 | defer os.RemoveAll(tmpgopath) 105 | err = ioutil.WriteFile(tmpfile, []byte(`test file from GOPATH`), 0777) 106 | is.NoErr(err) 107 | os.Setenv("GOPATH", tmpgopath) 108 | s, err := lookup.Get("github.com/matryer/codeform-test/file.go") 109 | is.NoErr(err) 110 | defer s.Close() 111 | is.Equal(s.IsDir, false) 112 | is.Equal(s.Path, tmpfile) 113 | contentb, err := ioutil.ReadAll(s) 114 | is.NoErr(err) 115 | is.Equal(string(contentb), `test file from GOPATH`) 116 | } 117 | 118 | // TestGopath tests that the local GOROOT is checked. 119 | func TestGoRoot(t *testing.T) { 120 | is := is.New(t) 121 | originalGoRoot := os.Getenv("GOROOT") 122 | defer func() { 123 | os.Setenv("GOROOT", originalGoRoot) 124 | }() 125 | tmpgoroot := filepath.Join(os.TempDir(), "codeform-test-goroot") 126 | tmpfiledir := filepath.Join(tmpgoroot, "src/github.com/matryer/codeform-test") 127 | tmpfile := filepath.Join(tmpfiledir, "file.go") 128 | err := os.MkdirAll(tmpfiledir, 0777) 129 | is.NoErr(err) 130 | defer os.RemoveAll(tmpgoroot) 131 | err = ioutil.WriteFile(tmpfile, []byte(`test file from GOROOT`), 0777) 132 | is.NoErr(err) 133 | os.Setenv("GOROOT", tmpgoroot) 134 | s, err := lookup.Get("github.com/matryer/codeform-test/file.go") 135 | is.NoErr(err) 136 | defer s.Close() 137 | is.Equal(s.IsDir, false) 138 | is.Equal(s.Path, tmpfile) 139 | contentb, err := ioutil.ReadAll(s) 140 | is.NoErr(err) 141 | is.Equal(string(contentb), `test file from GOROOT`) 142 | } 143 | -------------------------------------------------------------------------------- /query/query.go: -------------------------------------------------------------------------------- 1 | // Package query provides high-level code querying capabilities. 2 | package query 3 | 4 | import "github.com/matryer/codeform/model" 5 | 6 | // Query represents a code query. 7 | type Query struct { 8 | // packages contains a list of packages to include. 9 | packages []string 10 | // names contains a slice of named things to include 11 | // regardless of kind (struct, interface, etc.) 12 | names []string 13 | // interfaces is a list of interfaces to include. 14 | interfaces []string 15 | // structs is a list of structs to include. 16 | structs []string 17 | // funcs is a list of global functions to include. 18 | funcs []string 19 | } 20 | 21 | // New creates a new Query. 22 | func New() *Query { 23 | return &Query{} 24 | } 25 | 26 | // Run executes the query on the code, returning a new model.Code. 27 | func (q *Query) Run(code model.Code) (*model.Code, error) { 28 | if len(q.packages)+len(q.names)+len(q.interfaces)+len(q.structs)+len(q.funcs) == 0 { 29 | // no querying - just return the code 30 | return &code, nil 31 | } 32 | var pkgs []model.Package 33 | for _, pkg := range code.Packages { 34 | include, err := q.runPackage(&pkg) 35 | if err != nil { 36 | return nil, err 37 | } 38 | if include { 39 | pkgs = append(pkgs, pkg) 40 | } 41 | } 42 | code.Packages = pkgs 43 | return &code, nil 44 | } 45 | 46 | // Name specifies names of things to query for. 47 | // Names are always checked first, if the names match, the item 48 | // will be included. 49 | func (q *Query) Name(names ...string) *Query { 50 | q.names = append(q.names, names...) 51 | return q 52 | } 53 | 54 | // Interface specifies names of interfaces to include. 55 | func (q *Query) Interface(names ...string) *Query { 56 | q.interfaces = append(q.interfaces, names...) 57 | return q 58 | } 59 | 60 | // Struct specifies names of structs to include. 61 | func (q *Query) Struct(names ...string) *Query { 62 | q.structs = append(q.structs, names...) 63 | return q 64 | } 65 | 66 | // Package specifies names of packages to include. 67 | func (q *Query) Package(names ...string) *Query { 68 | q.packages = append(q.packages, names...) 69 | return q 70 | } 71 | 72 | // Func specifies names of global functions to include. 73 | func (q *Query) Func(names ...string) *Query { 74 | q.funcs = append(q.funcs, names...) 75 | return q 76 | } 77 | 78 | // runPackage runs the query on the specified package. 79 | func (q *Query) runPackage(pkg *model.Package) (bool, error) { 80 | 81 | // packages 82 | if len(q.packages) > 0 { 83 | var packageMentioned bool 84 | for _, packageName := range q.packages { 85 | if pkg.Name == packageName { 86 | packageMentioned = true 87 | break 88 | } 89 | } 90 | if !packageMentioned { 91 | return false, nil 92 | } 93 | } 94 | 95 | var include bool 96 | 97 | // interfaces 98 | var ifaces []model.Interface 99 | for _, iface := range pkg.Interfaces { 100 | if q.shouldInterface(&iface) { 101 | ifaces = append(ifaces, iface) 102 | include = true 103 | } 104 | } 105 | pkg.Interfaces = ifaces 106 | 107 | // structs 108 | var structs []model.Struct 109 | for _, structure := range pkg.Structs { 110 | if q.shouldStruct(&structure) { 111 | structs = append(structs, structure) 112 | include = true 113 | } 114 | } 115 | pkg.Structs = structs 116 | 117 | // funcs 118 | var funcs []model.Func 119 | for _, fn := range pkg.Funcs { 120 | if q.shouldFunc(&fn) { 121 | funcs = append(funcs, fn) 122 | include = true 123 | } 124 | } 125 | pkg.Funcs = funcs 126 | 127 | return include, nil 128 | } 129 | 130 | // shouldInterface gets whether the interface should be included 131 | // or not. 132 | func (q *Query) shouldInterface(v *model.Interface) bool { 133 | if q.shouldInclude(v.Name) { 134 | return true 135 | } 136 | for _, iface := range q.interfaces { 137 | if v.Name == iface { 138 | return true 139 | } 140 | } 141 | return false 142 | } 143 | 144 | // shouldStruct gets whether the struct should be included 145 | // or not. 146 | func (q *Query) shouldStruct(v *model.Struct) bool { 147 | if q.shouldInclude(v.Name) { 148 | return true 149 | } 150 | for _, name := range q.structs { 151 | if v.Name == name { 152 | return true 153 | } 154 | } 155 | return false 156 | } 157 | 158 | // shouldFunc gets whether the struct should be included 159 | // or not. 160 | func (q *Query) shouldFunc(v *model.Func) bool { 161 | if q.shouldInclude(v.Name) { 162 | return true 163 | } 164 | for _, name := range q.funcs { 165 | if v.Name == name { 166 | return true 167 | } 168 | } 169 | return false 170 | } 171 | 172 | // shouldInclude gets whether the name is explicitly listed in the 173 | // names slice or not. 174 | func (q *Query) shouldInclude(name string) bool { 175 | for _, actualName := range q.names { 176 | if actualName == name { 177 | return true 178 | } 179 | } 180 | return false 181 | } 182 | -------------------------------------------------------------------------------- /editor/static/tags/editor.tag: -------------------------------------------------------------------------------- 1 | 2 | 51 |
52 | 53 |
54 |
55 |
56 | { message } 57 |
58 | 63 |
64 |
65 |

66 | Source code will be used to render the preview - must be valid Go code 67 |

68 | 69 |
70 |
71 |
72 |
73 | 74 |
75 |
76 |
77 |
78 | 84 | 85 |
86 |
87 | 88 |
89 | 167 |
-------------------------------------------------------------------------------- /editor/way.go: -------------------------------------------------------------------------------- 1 | package editor 2 | 3 | // lovingly stolen and modified from github.com/matryer/way 4 | 5 | import ( 6 | "net/http" 7 | "strings" 8 | 9 | "golang.org/x/net/context" 10 | ) 11 | 12 | // wayContextKey is the context key type for storing 13 | // parameters in context.Context. 14 | type wayContextKey string 15 | 16 | // Router routes HTTP requests. 17 | type Router struct { 18 | routes []*route 19 | // NotFound is the http.Handler to call when no routes 20 | // match. By default uses http.NotFoundHandler(). 21 | NotFound http.Handler 22 | 23 | // OnSystemError is called when a handler returns an error. 24 | OnSystemError func(context.Context, http.ResponseWriter, *http.Request, error) 25 | 26 | // contexter is the Contexter that generates context objects. 27 | contexter Contexter 28 | } 29 | 30 | // NewRouter makes a new Router. 31 | func NewRouter() *Router { 32 | return &Router{ 33 | NotFound: http.NotFoundHandler(), 34 | OnSystemError: func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) { 35 | http.Error(w, err.Error(), http.StatusInternalServerError) 36 | }, 37 | } 38 | } 39 | 40 | func (r *Router) pathSegments(p string) []string { 41 | return strings.Split(strings.Trim(p, "/"), "/") 42 | } 43 | 44 | // Handler is http.Handler with context. 45 | type Handler interface { 46 | ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) error 47 | } 48 | 49 | // HandlerFunc is http.HandlerFunc with context. 50 | type HandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request) error 51 | 52 | func (h HandlerFunc) ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 53 | return h(ctx, w, r) 54 | } 55 | 56 | // Handle adds a handler with the specified method and pattern. 57 | // Method can be any HTTP method string or "*" to match all methods. 58 | // Pattern can contain path segments such as: /item/:id which is 59 | // accessible via Param(context, "id"). 60 | // If pattern ends with trailing /, it acts as a prefix. 61 | // Errors returned from handlers are considered system errors and are 62 | // handled via Router.OnSystemError. 63 | func (r *Router) Handle(method, pattern string, handler Handler) { 64 | route := &route{ 65 | method: strings.ToLower(method), 66 | segs: r.pathSegments(pattern), 67 | handler: handler, 68 | prefix: strings.HasSuffix(pattern, "/"), 69 | } 70 | r.routes = append(r.routes, route) 71 | } 72 | 73 | // HandleFunc is the http.HandlerFunc alternative to http.Handle. 74 | func (r *Router) HandleFunc(method, pattern string, fn HandlerFunc) { 75 | r.Handle(method, pattern, fn) 76 | } 77 | 78 | // ServeHTTP routes the incoming http.Request based on method and path 79 | // extracting path parameters as it goes. 80 | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { 81 | if r == nil { 82 | panic("nil router") 83 | } 84 | method := strings.ToLower(req.Method) 85 | segs := r.pathSegments(req.URL.Path) 86 | for _, route := range r.routes { 87 | if route.method != method && route.method != "*" { 88 | continue 89 | } 90 | if ctx, ok := route.match(r.contexter.Context(req), r, segs); ok { 91 | systemErr := route.handler.ServeHTTP(ctx, w, req) 92 | if systemErr != nil { 93 | r.OnSystemError(ctx, w, req, systemErr) 94 | } 95 | return 96 | } 97 | } 98 | r.NotFound.ServeHTTP(w, req) 99 | } 100 | 101 | // Param gets the path parameter from the specified Context. 102 | // Returns an empty string if the parameter was not found. 103 | func Param(ctx context.Context, param string) string { 104 | v := ctx.Value(wayContextKey(param)) 105 | if v == nil { 106 | return "" 107 | } 108 | vStr, ok := v.(string) 109 | if !ok { 110 | return "" 111 | } 112 | return vStr 113 | } 114 | 115 | type route struct { 116 | method string 117 | segs []string 118 | handler Handler 119 | prefix bool 120 | } 121 | 122 | func (r *route) match(ctx context.Context, router *Router, segs []string) (context.Context, bool) { 123 | if len(segs) > len(r.segs) && !r.prefix { 124 | return nil, false 125 | } 126 | for i, seg := range r.segs { 127 | if i > len(segs)-1 { 128 | return nil, false 129 | } 130 | isParam := false 131 | if strings.HasPrefix(seg, ":") { 132 | isParam = true 133 | seg = strings.TrimPrefix(seg, ":") 134 | } 135 | if !isParam { // verbatim check 136 | if seg != segs[i] { 137 | return nil, false 138 | } 139 | } 140 | if isParam { 141 | ctx = context.WithValue(ctx, wayContextKey(seg), segs[i]) 142 | } 143 | } 144 | return ctx, true 145 | } 146 | 147 | // Contexter gets a context for a specific request. 148 | type Contexter interface { 149 | Context(*http.Request) context.Context 150 | } 151 | 152 | // ContexterFunc is a function wrapper for Contexter. 153 | type ContexterFunc func(*http.Request) context.Context 154 | 155 | // Context gets a context for the specified request. 156 | func (fn ContexterFunc) Context(r *http.Request) context.Context { 157 | return fn(r) 158 | } 159 | 160 | // ContexterBackground gets a Contexter that provides context.Background(). 161 | func ContexterBackground() Contexter { 162 | return ContexterFunc(func(*http.Request) context.Context { 163 | return context.Background() 164 | }) 165 | } 166 | -------------------------------------------------------------------------------- /source/source.go: -------------------------------------------------------------------------------- 1 | // Package source provides a way to refer to source files. 2 | // Valid sources include: 3 | // /local/path/to/file.go 4 | // /local/path/to/package 5 | // github.com/matryer/package 6 | // github.com/matryer/package/specific-file.go 7 | // https://domain.com/path/to/file 8 | package source 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | "io" 14 | "io/ioutil" 15 | "net/http" 16 | "net/url" 17 | "os" 18 | "path/filepath" 19 | "strings" 20 | "sync" 21 | ) 22 | 23 | // Source represents the source of a code file or 24 | // package. 25 | // Sources must always be closed. 26 | type Source struct { 27 | readOnce sync.Once 28 | readCloser io.ReadCloser 29 | closer func() error 30 | Path string 31 | IsDir bool 32 | } 33 | 34 | // ErrNotFound indicates the source file could not be found. 35 | type ErrNotFound string 36 | 37 | func (e ErrNotFound) Error() string { 38 | return "not found: " + string(e) 39 | } 40 | 41 | // Lookup provides source lookup capabilities. 42 | type Lookup struct { 43 | Client http.Client 44 | } 45 | 46 | // DefaultLookup is a default Lookup instance. 47 | var DefaultLookup = &Lookup{} 48 | 49 | // Reader gets a Source for the specified io.Reader. 50 | func Reader(path string, r io.Reader) *Source { 51 | s := &Source{ 52 | Path: path, 53 | IsDir: false, 54 | readCloser: ioutil.NopCloser(r), 55 | } 56 | return s 57 | } 58 | 59 | // Get gets the file at the given source. 60 | // Valid sources include local paths, URLs, or go gettable 61 | // paths. 62 | // Sources must be closed. 63 | // Valid sources include: 64 | // /local/path/to/file.go 65 | // /local/path/to/package 66 | // github.com/matryer/package 67 | // github.com/matryer/package/specific-file.go 68 | // https://domain.com/path/to/file 69 | // 70 | // Special strings 71 | // 72 | // The string "default" will be taken to mean the default source file which can be 73 | // found in default/source.go. 74 | // A special template prefix will indicate the template 75 | // is hosted in the official https://github.com/matryer/codeform-templates repository. 76 | // template:testing/mocking/mock 77 | func (l *Lookup) Get(src string) (*Source, error) { 78 | 79 | // handle special cases 80 | if src == "default" { 81 | src = "github.com/matryer/codeform/source/default/source.go" 82 | } 83 | if strings.HasPrefix(src, "template:") { 84 | src = fmt.Sprintf("github.com/matryer/codeform-templates/%s.tpl", strings.TrimPrefix(src, "template:")) 85 | } 86 | 87 | if s, err := tryLocal(l, src); err == nil { 88 | return s, nil 89 | } 90 | if s, err := tryURL(l, src); err == nil { 91 | return s, nil 92 | } 93 | if s, err := tryGoPath(l, src); err == nil { 94 | return s, nil 95 | } 96 | if s, err := tryGoRoot(l, src); err == nil { 97 | return s, nil 98 | } 99 | if s, err := tryGoGet(l, src); err == nil { 100 | return s, nil 101 | } 102 | return nil, ErrNotFound(src) 103 | } 104 | 105 | // MustLocal gets a local source. 106 | // Panics if the local source is invalid. 107 | func MustLocal(src string) *Source { 108 | s, err := tryLocal(nil, src) 109 | if err != nil { 110 | panic("codeform/source: " + err.Error()) 111 | } 112 | return s 113 | } 114 | 115 | func tryLocal(l *Lookup, src string) (*Source, error) { 116 | info, err := os.Stat(src) 117 | if err != nil { 118 | return nil, err 119 | } 120 | s := &Source{ 121 | Path: src, 122 | IsDir: info.IsDir(), 123 | } 124 | return s, nil 125 | } 126 | 127 | func tryURL(l *Lookup, src string) (*Source, error) { 128 | u, err := url.Parse(src) 129 | if err != nil { 130 | return nil, err 131 | } 132 | res, err := l.Client.Get(u.String()) 133 | if err != nil { 134 | return nil, err 135 | } 136 | s := &Source{ 137 | Path: src, 138 | IsDir: false, 139 | readCloser: res.Body, 140 | } 141 | return s, nil 142 | } 143 | 144 | func tryGoPath(l *Lookup, src string) (*Source, error) { 145 | src = filepath.Join(os.Getenv("GOPATH"), "src", src) 146 | return tryLocal(l, src) 147 | } 148 | 149 | func tryGoRoot(l *Lookup, src string) (*Source, error) { 150 | src = filepath.Join(os.Getenv("GOROOT"), "src", src) 151 | return tryLocal(l, src) 152 | } 153 | 154 | func tryGoGet(l *Lookup, src string) (*Source, error) { 155 | repo, done, err := goget(src) 156 | if err != nil { 157 | return nil, err 158 | } 159 | info, err := os.Stat(repo) 160 | if err != nil { 161 | done() 162 | return nil, err 163 | } 164 | s := &Source{ 165 | Path: repo, 166 | IsDir: info.IsDir(), 167 | closer: func() error { 168 | done() 169 | return nil 170 | }, 171 | } 172 | return s, nil 173 | } 174 | 175 | // Read implements io.Reader and is a safe way of reading 176 | // files. 177 | func (s *Source) Read(b []byte) (int, error) { 178 | if s.IsDir { 179 | return 0, errors.New("source: cannot read directories") 180 | } 181 | var err error 182 | s.readOnce.Do(func() { 183 | if s.readCloser != nil { 184 | return 185 | } 186 | s.readCloser, err = os.Open(s.Path) 187 | }) 188 | if err != nil { 189 | return 0, err 190 | } 191 | return s.readCloser.Read(b) 192 | } 193 | 194 | // Close closes the Source and cleans up any used resources. 195 | func (s *Source) Close() error { 196 | var err1, err2 error 197 | if rc := s.readCloser; rc != nil { 198 | err1 = rc.Close() 199 | } 200 | if c := s.closer; c != nil { 201 | err2 = c() 202 | } 203 | if err1 != nil { 204 | return err1 205 | } 206 | if err2 != nil { 207 | return err2 208 | } 209 | return nil 210 | } 211 | -------------------------------------------------------------------------------- /editor/static/lib/e-clipboard.v1.5.16.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v1.5.16 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Clipboard=e()}}(function(){var e,t,n;return function e(t,n,i){function o(a,c){if(!n[a]){if(!t[a]){var l="function"==typeof require&&require;if(!c&&l)return l(a,!0);if(r)return r(a,!0);var s=new Error("Cannot find module '"+a+"'");throw s.code="MODULE_NOT_FOUND",s}var u=n[a]={exports:{}};t[a][0].call(u.exports,function(e){var n=t[a][1][e];return o(n?n:e)},u,u.exports,e,t,n,i)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function e(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function e(){var t=this,n="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=document.body.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[n?"right":"left"]="-9999px";var i=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.addEventListener("focus",window.scrollTo(0,i)),this.fakeElem.style.top=i+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,document.body.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function e(){this.fakeHandler&&(document.body.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(document.body.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function e(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function e(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function e(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function e(){this.target&&this.target.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function e(){this.removeFake()}},{key:"action",set:function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function e(){return this._action}},{key:"target",set:function e(t){if(void 0!==t){if(!t||"object"!==("undefined"==typeof t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function e(){return this._target}}]),e}();e.exports=c})},{select:5}],8:[function(t,n,i){!function(o,r){if("function"==typeof e&&e.amd)e(["module","./clipboard-action","tiny-emitter","good-listener"],r);else if("undefined"!=typeof i)r(n,t("./clipboard-action"),t("tiny-emitter"),t("good-listener"));else{var a={exports:{}};r(a,o.clipboardAction,o.tinyEmitter,o.goodListener),o.clipboard=a.exports}}(this,function(e,t,n,i){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t){var n="data-clipboard-"+e;if(t.hasAttribute(n))return t.getAttribute(n)}var s=o(t),u=o(n),f=o(i),d=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText}},{key:"listenClick",value:function e(t){var n=this;this.listener=(0,f.default)(t,"click",function(e){return n.onClick(e)})}},{key:"onClick",value:function e(t){var n=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new s.default({action:this.action(n),target:this.target(n),text:this.text(n),trigger:n,emitter:this})}},{key:"defaultAction",value:function e(t){return l("action",t)}},{key:"defaultTarget",value:function e(t){var n=l("target",t);if(n)return document.querySelector(n)}},{key:"defaultText",value:function e(t){return l("text",t)}},{key:"destroy",value:function e(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}]),t}(u.default);e.exports=h})},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)}); -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | // Package parser turns Go code into model.Code. 2 | package parser 3 | 4 | import ( 5 | "fmt" 6 | "go/ast" 7 | "go/importer" 8 | "go/parser" 9 | "go/token" 10 | "go/types" 11 | "path" 12 | "path/filepath" 13 | "strings" 14 | 15 | "github.com/matryer/codeform/model" 16 | "github.com/matryer/codeform/source" 17 | "github.com/pkg/errors" 18 | ) 19 | 20 | // Parser parses source code and generates a model.Code. 21 | type Parser struct { 22 | src *source.Source 23 | // TargetPackage is the name or path of the package 24 | // that becomes the point of view for type names. 25 | // If empty or the same package, types will not be qualified, 26 | // otherwise they will include the package name. 27 | TargetPackage string 28 | qualifier types.Qualifier 29 | fset *token.FileSet 30 | code *model.Code 31 | } 32 | 33 | // New makes a new Generator for the given package (folder) or 34 | // Go code file. 35 | // If src is specified as a []byte or io.Reader, it will be used as 36 | // the source. 37 | func New(src *source.Source) *Parser { 38 | return &Parser{ 39 | src: src, 40 | } 41 | } 42 | 43 | // Parse parses the source file and returns the model.Code. 44 | func (p *Parser) Parse() (*model.Code, error) { 45 | p.qualifier = func(other *types.Package) string { 46 | if other.Name() == p.TargetPackage { 47 | return "" 48 | } 49 | return other.Name() 50 | } 51 | if strings.Contains(p.TargetPackage, "/") { 52 | p.qualifier = func(other *types.Package) string { 53 | // TODO: test this 54 | if other.Path() == p.TargetPackage { 55 | return "" 56 | } 57 | return other.Name() 58 | } 59 | } 60 | importer := newSmartImporter(importer.Default()) 61 | code := &model.Code{} 62 | fset := token.NewFileSet() 63 | var pkgs map[string]*ast.Package 64 | if p.src.IsDir { 65 | var err error 66 | pkgs, err = parser.ParseDir(fset, p.src.Path, nil, parser.SpuriousErrors) 67 | if err != nil { 68 | return nil, err 69 | } 70 | for _, pkg := range pkgs { 71 | if len(p.TargetPackage) == 0 { 72 | p.TargetPackage = pkg.Name 73 | } 74 | if err := p.parsePackage(code, pkg, fset, importer); err != nil { 75 | return nil, err 76 | } 77 | } 78 | } else { 79 | file, err := parser.ParseFile(fset, p.src.Path, p.src, parser.SpuriousErrors) 80 | if err != nil { 81 | return nil, err 82 | } 83 | files := []*ast.File{file} 84 | conf := types.Config{Importer: importer} 85 | tpkg, err := conf.Check(p.src.Path, fset, files, nil) 86 | if err != nil { 87 | return nil, err 88 | } 89 | packageModel := model.Package{ 90 | Name: tpkg.Name(), 91 | } 92 | if len(p.TargetPackage) == 0 { 93 | p.TargetPackage = tpkg.Name() 94 | } 95 | if err := p.parseGlobals(&packageModel, tpkg); err != nil { 96 | return nil, err 97 | } 98 | code.Packages = append(code.Packages, packageModel) 99 | } 100 | code.TargetPackageName = filepath.Base(p.TargetPackage) 101 | return code, nil 102 | } 103 | 104 | func (p *Parser) parsePackage(code *model.Code, pkg *ast.Package, fset *token.FileSet, importer types.Importer) error { 105 | i := 0 106 | files := make([]*ast.File, len(pkg.Files)) 107 | for _, file := range pkg.Files { 108 | files[i] = file 109 | i++ 110 | } 111 | conf := types.Config{Importer: importer} 112 | tpkg, err := conf.Check(p.src.Path, fset, files, nil) 113 | if err != nil { 114 | return err 115 | } 116 | timports := tpkg.Imports() 117 | imports := make([]model.Import, len(timports)) 118 | for i, tpkg := range timports { 119 | imports[i] = model.Import{Name: tpkg.Path()} 120 | } 121 | packageModel := model.Package{ 122 | Name: tpkg.Name(), 123 | Imports: imports, 124 | } 125 | if len(p.TargetPackage) > 0 { 126 | // remove the import if it matches the target package 127 | // since it is no-longer needed 128 | var filteredImports []model.Import 129 | for _, imp := range packageModel.Imports { 130 | if imp.Name != p.TargetPackage { 131 | filteredImports = append(filteredImports, imp) 132 | } 133 | } 134 | packageModel.Imports = filteredImports 135 | } 136 | if err := p.parseGlobals(&packageModel, tpkg); err != nil { 137 | return err 138 | } 139 | code.Packages = append(code.Packages, packageModel) 140 | return nil 141 | } 142 | 143 | func (p *Parser) parseGlobals(packageModel *model.Package, tpkg *types.Package) error { 144 | scope := tpkg.Scope() 145 | for _, name := range scope.Names() { 146 | 147 | thing := scope.Lookup(name) 148 | typ := thing.Type().Underlying() 149 | 150 | parsedType, err := p.parseType(packageModel, tpkg, thing) 151 | if err != nil { 152 | return errors.Wrap(err, "parseType") 153 | } 154 | 155 | switch thing.(type) { 156 | case *types.Const: 157 | packageModel.Consts = append(packageModel.Consts, *parsedType) 158 | continue 159 | case *types.Var: 160 | packageModel.Vars = append(packageModel.Vars, *parsedType) 161 | continue 162 | } 163 | switch val := typ.(type) { 164 | case *types.Signature: 165 | fn, err := p.parseSignature(val, tpkg) 166 | if err != nil { 167 | return errors.Wrap(err, "parseSignature") 168 | } 169 | fn.Name = thing.Name() 170 | packageModel.Funcs = append(packageModel.Funcs, *fn) 171 | case *types.Interface: 172 | iface, err := p.parseInterface(val.Complete(), tpkg) 173 | if err != nil { 174 | return errors.Wrap(err, "parseInterface") 175 | } 176 | iface.Name = thing.Name() 177 | iface.Fullname = types.TypeString(thing.Type(), p.qualifier) 178 | packageModel.Interfaces = append(packageModel.Interfaces, *iface) 179 | case *types.Struct: 180 | structure, err := p.parseStruct(thing.Type(), val, tpkg) 181 | if err != nil { 182 | return errors.Wrap(err, "parseStruct") 183 | } 184 | structure.Name = thing.Name() 185 | structure.Fullname = types.TypeString(thing.Type(), p.qualifier) 186 | packageModel.Structs = append(packageModel.Structs, *structure) 187 | default: 188 | return fmt.Errorf("%T not supported", thing) 189 | } 190 | } 191 | return nil 192 | } 193 | 194 | func (p *Parser) parseStruct(typ types.Type, s *types.Struct, tpkg *types.Package) (*model.Struct, error) { 195 | structModel := &model.Struct{} 196 | var err error 197 | if structModel.Methods, err = p.parseMethods(typ, tpkg); err != nil { 198 | return nil, errors.Wrap(err, "parseMethods") 199 | } 200 | numFields := s.NumFields() 201 | for i := 0; i < numFields; i++ { 202 | field := s.Field(i) 203 | fieldVar, err := p.parseVar(field, tpkg) 204 | if err != nil { 205 | return nil, errors.Wrap(err, "parseVar") 206 | } 207 | fieldVar.Tag = s.Tag(i) 208 | structModel.Fields = append(structModel.Fields, *fieldVar) 209 | } 210 | return structModel, nil 211 | } 212 | 213 | func (p *Parser) parseMethods(typ types.Type, tpkg *types.Package) ([]model.Func, error) { 214 | methods := make(map[string]model.Func) 215 | for _, t := range []types.Type{typ, types.NewPointer(typ)} { 216 | mset := types.NewMethodSet(t) 217 | for i := 0; i < mset.Len(); i++ { 218 | methodModel, err := p.parseMethod(mset.At(i), tpkg) 219 | if err != nil { 220 | return nil, errors.Wrap(err, "parseMethod") 221 | } 222 | if _, present := methods[methodModel.Name]; present { 223 | continue // skip duplicates 224 | } 225 | methods[methodModel.Name] = *methodModel 226 | } 227 | } 228 | // turn them into an array 229 | methodSlice := make([]model.Func, len(methods)) 230 | var i int 231 | for _, method := range methods { 232 | methodSlice[i] = method 233 | i++ 234 | } 235 | return methodSlice, nil 236 | } 237 | 238 | func (p *Parser) parseType(pkg *model.Package, tpkg *types.Package, obj types.Object) (*model.Var, error) { 239 | typ, err := p.parseVarType(obj, tpkg) 240 | if err != nil { 241 | return nil, errors.Wrap(err, "parseVarType") 242 | } 243 | return &model.Var{ 244 | Name: obj.Name(), 245 | Type: typ, 246 | }, nil 247 | } 248 | 249 | func (p *Parser) parseInterface(iface *types.Interface, tpkg *types.Package) (*model.Interface, error) { 250 | interfaceModel := &model.Interface{} 251 | mlen := iface.NumMethods() 252 | for i := 0; i < mlen; i++ { 253 | method := iface.Method(i).Type().(*types.Signature) 254 | fn, err := p.parseSignature(method, tpkg) 255 | if err != nil { 256 | return nil, err 257 | } 258 | fn.Name = iface.Method(i).Name() 259 | interfaceModel.Methods = append(interfaceModel.Methods, *fn) 260 | } 261 | return interfaceModel, nil 262 | } 263 | 264 | func (p *Parser) parseMethod(sel *types.Selection, tpkg *types.Package) (*model.Func, error) { 265 | funcObj, ok := sel.Obj().(*types.Func) 266 | if !ok { 267 | return nil, fmt.Errorf("expected *types.Func but got %T", sel.Obj()) 268 | } 269 | sig, ok := funcObj.Type().(*types.Signature) 270 | if !ok { 271 | return nil, fmt.Errorf("expected *types.Signature but got %T", funcObj.Type()) 272 | } 273 | sigModel, err := p.parseSignature(sig, tpkg) 274 | if err != nil { 275 | return nil, errors.Wrap(err, "parseSignature") 276 | } 277 | sigModel.Name = funcObj.Name() 278 | return sigModel, nil 279 | } 280 | 281 | func (p *Parser) parseSignature(fn *types.Signature, tpkg *types.Package) (*model.Func, error) { 282 | args, err := p.parseVars(fn.Params(), tpkg) 283 | if err != nil { 284 | return nil, errors.Wrap(err, "Params") 285 | } 286 | retArgs, err := p.parseVars(fn.Results(), tpkg) 287 | if err != nil { 288 | return nil, errors.Wrap(err, "Results") 289 | } 290 | variadic := fn.Variadic() 291 | if variadic { 292 | // update the last arg to tell it it's variadic also 293 | args[len(args)-1].Variadic = true 294 | } 295 | return &model.Func{ 296 | Variadic: variadic, 297 | Args: args, 298 | ReturnArgs: retArgs, 299 | }, nil 300 | } 301 | 302 | func (p *Parser) parseVars(vars *types.Tuple, tpkg *types.Package) ([]model.Var, error) { 303 | var varModels []model.Var 304 | paramsLen := vars.Len() 305 | for i := 0; i < paramsLen; i++ { 306 | argModel, err := p.parseVar(vars.At(i), tpkg) 307 | if err != nil { 308 | return nil, errors.Wrap(err, "param") 309 | } 310 | varModels = append(varModels, *argModel) 311 | } 312 | return varModels, nil 313 | } 314 | 315 | func (p *Parser) parseVar(param *types.Var, tpkg *types.Package) (*model.Var, error) { 316 | typ, err := p.parseVarType(param, tpkg) 317 | if err != nil { 318 | return nil, errors.Wrap(err, "parseVarType") 319 | } 320 | n := param.Name() 321 | arg := &model.Var{ 322 | Anonymous: len(n) == 0, 323 | Name: n, 324 | Type: typ, 325 | } 326 | return arg, nil 327 | } 328 | 329 | func (p *Parser) parseVarType(obj types.Object, tpkg *types.Package) (model.Type, error) { 330 | typeStr := types.TypeString(obj.Type(), p.qualifier) 331 | if strings.Contains(typeStr, "/") { 332 | // turn *path/to/package.Type into *package.Type 333 | pointer := strings.HasPrefix(typeStr, "*") 334 | typeStr = path.Base(typeStr) 335 | if pointer { 336 | typeStr = "*" + typeStr 337 | } 338 | } 339 | nameStr := typeStr 340 | if dotindex := strings.LastIndex(nameStr, "."); dotindex >= 0 { 341 | nameStr = nameStr[dotindex+1:] 342 | } 343 | return model.Type{ 344 | Name: nameStr, 345 | Fullname: typeStr, 346 | }, nil 347 | } 348 | -------------------------------------------------------------------------------- /editor/static/lib/d-riot+compiler.v3.0.5.min.js: -------------------------------------------------------------------------------- 1 | /* Riot v3.0.5, @license MIT */ 2 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.riot=t()}(this,function(){"use strict";function e(e){return st.test(e)}function t(e){return lt.test(e)}function r(e){return typeof e===et||!1}function n(e){return e&&typeof e===Xe}function i(e){return typeof e===Ye}function o(e){return typeof e===Je}function a(e){return i(e)||null===e||""===e}function s(e){return Array.isArray(e)||e instanceof Array}function u(e,t){var r=Object.getOwnPropertyDescriptor(e,t);return i(e[t])||r&&r.writable}function c(e){return at.test(e)}function l(e,t){return(t||document).querySelectorAll(e)}function p(e,t){return(t||document).querySelector(e)}function f(){return document.createDocumentFragment()}function h(){return document.createTextNode("")}function d(e,t){return t?document.createElementNS("http://www.w3.org/2000/svg","svg"):document.createElement(e)}function g(e){if(e.outerHTML)return e.outerHTML;var t=d("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}function m(e,t){if(i(e.innerHTML)){var r=(new DOMParser).parseFromString(t,"application/xml"),n=e.ownerDocument.importNode(r.documentElement,!0);e.appendChild(n)}else e.innerHTML=t}function v(e,t){e.removeAttribute(t)}function x(e,t){return e.getAttribute(t)}function y(e,t,r){var n=rt.exec(t);n&&n[1]?e.setAttributeNS(tt,n[1],r):e.setAttribute(t,r)}function b(e,t,r){e.insertBefore(t,r.parentNode&&r)}function w(e,t){if(e)for(var r;r=ut.exec(e);)t(r[1].toLowerCase(),r[2]||r[3]||r[4])}function C(e,t,r){if(e){var n,i=t(e,r);if(i===!1)return;for(e=e.firstChild;e;)n=e.nextSibling,C(e,t,i),e=n}}function O(e,t){for(var r,n=e?e.length:0,i=0;ia;)i=t[--o],t.splice(o,1),i.unmount(),le(n.tags,r,i,!0)}function $(e){var t=this;O(Object.keys(this.tags),function(r){var n=t.tags[r];s(n)?O(n,function(t){ne.apply(t,[r,e])}):ne.apply(n,[r,e])})}function F(e,t,r){r?de.apply(this,[e,t]):b(e,this.root,t.root)}function P(e,t,r){r?he.apply(this,[e,t]):b(e,this.root,t.root)}function H(e,t){t?he.call(this,e):e.appendChild(this.root)}function B(e,t,r){v(e,"each");var n,i=typeof x(e,"no-reorder")!==Je||v(e,"no-reorder"),o=se(e),a=Ze[o]||{tmpl:g(e)},u=it.test(o),c=e.parentNode,l=h(),p=te(e),d=x(e,"if"),m=[],y=[],b=!0,w=!Ze[o],C="VIRTUAL"===e.tagName;return r=wt.loopKeys(r),r.isLoop=!0,d&&v(e,"if"),c.insertBefore(l,e),c.removeChild(e),r.update=function(){var c=wt(r.val,t),h=f(),g=l.parentNode;s(c)?n=!1:(n=c||!1,c=n?Object.keys(c).map(function(e){return I(r,c[e],e)}):[]),d&&(c=c.filter(function(e,n){return r.key?!!wt(d,I(r,e,n,t)):!!wt(d,t)||!!wt(d,e)})),O(c,function(s,l){var f=i&&typeof s===Xe&&!n,d=y.indexOf(s),v=~d&&f?d:l,x=m[v];if(s=!n&&r.key?I(r,s,l):s,!f&&!x||f&&!~d){var O=l===m.length;x=new ee(a,{parent:t,isLoop:b,isAnonymous:w,root:u?g:e.cloneNode(),item:s},e.innerHTML),x.mount(),O?H.apply(x,[h||g,C]):P.apply(x,[g,m[l],C]),O||y.splice(l,0,s),m.splice(l,0,x),p&&ce(t.tags,o,x,!0),v=l}else x.update(s);v!==l&&f&&(_(c,y[l])&&F.apply(x,[g,m[l],C]),r.pos&&(x[r.pos]=l),m.splice(l,0,m.splice(v,1)[0]),y.splice(l,0,y.splice(v,1)[0]),!p&&x.tags&&$.call(x,l)),x._item=s,j(x,"_parent",t)}),k(c,m,o,t),y=c.slice(),g.insertBefore(h,l)},r.unmount=function(){O(m,function(e){e.unmount()})},r}function z(e,t,r){var n=this,i={parent:{children:t}};return C(e,function(t,i){var o,a,s,u=t.nodeType,c=i.parent;if(!r&&t===e)return{parent:c};if(3===u&&"STYLE"!==t.parentNode.tagName&&wt.hasExpr(t.nodeValue)&&c.children.push({dom:t,expr:t.nodeValue}),1!==u)return i;if(o=x(t,"each"))return c.children.push(B(t,n,o)),!1;if(o=x(t,"if"))return c.children.push(Object.create(Et).init(t,n,o)),!1;if((a=x(t,We))&&wt.hasExpr(a))return c.children.push({isRtag:!0,expr:a,dom:t}),!1;if(s=te(t),s&&(t!==e||r)){var l={root:t,parent:n,hasImpl:!0};return c.children.push(ie(s,l,t.innerHTML,n)),!1}return D.apply(n,[t,t.attributes,function(e,t){t&&c.children.push(t)}]),{parent:c}},i),{tree:i,root:e}}function D(e,r,n){var i=this;O(r,function(r){var o,a=r.name,s=t(a);~["ref","data-ref"].indexOf(a)?o=Object.create(Nt).init(e,a,r.value,i):wt.hasExpr(r.value)&&(o={dom:e,expr:r.value,attr:r.name,bool:s}),n(r,o)})}function U(e,t,r){var n="o"===r[0],i=n?"select>":"table>";if(e.innerHTML="<"+i+t.trim()+""}),!t.whitespace){var n=[];/]/.test(e)&&(e=e.replace(Xt,function(e){return n.push(e),""})),e=e.trim().replace(/\s+/g," "),n.length&&(e=e.replace(/\u0002/g,function(){return n.shift()}))}return t.compact&&(e=e.replace(Qt,"><$1")),be(e,r).replace(tr,"")}function Ce(e,t,r){return Array.isArray(t)?(r=t,t={}):(r||(r=[]),t||(t={})),r._bp=bt.array(t.brackets),we(ve(e),t,r)}function Oe(e){function t(e,t,r){for(t.lastIndex=0;r=t.exec(e);)"/"!==r[0][0]||r[1]||r[2]||(e=u.leftContext+" "+u.rightContext,t.lastIndex=r[3]+1);return e}function r(e,t){var r,n=1;for(t.lastIndex=0;n&&(r=t.exec(e));)"{"===r[0]?++n:"}"===r[0]&&--n;return n?e.length:t.lastIndex}var n,i,o,a,s=[],u=RegExp;for(~e.indexOf("/")&&(e=t(e,lr));n=e.match(ur);)s.push(u.leftContext),e=u.rightContext,o=r(e,cr),a=n[1],i=!/^(?:if|while|for|switch|catch|function)$/.test(a),a=i?n[0].replace(a,"this."+a+" = function"):n[0],s.push(a,e.slice(0,o)),e=e.slice(o),i&&!/^\s*.\s*bind\b/.test(e)&&s.push(".bind(this)");return s.length?s.join("")+e:e}function _e(e,t,r,n,i){if(!/\S/.test(e))return"";r||(r=t.type);var o=t.parser||r&&Dt._req("js."+r,!0)||Oe;return o(e,n,i).replace(/\r\n?/g,"\n").replace(tr,"")}function Ee(e,t,r,n){return"string"==typeof t&&(n=r,r=t,t={}),r&&"object"==typeof r&&(n=r,r=""),n||(n={}),_e(e,t||{},r,n.parserOptions,n.url)}function Ne(e,t){var r=":scope";return t.replace(pr,function(t,n,i){return i?(i=i.replace(/[^,]+/g,function(t){var n=t.trim();return 0===n.indexOf(e)?t:n&&"from"!==n&&"to"!==n&&"%"!==n.slice(-1)?n=n.indexOf(r)<0?e+" "+n+',[data-is="'+e+'"] '+n:n.replace(r,e)+","+n.replace(r,'[data-is="'+e+'"]'):t}),n?n+" "+i:i):t})}function je(e,t,r,n){if(n=n||{},r&&"css"!==r){var i=Dt._req("css."+r,!0);e=i(t,e,n.parserOpts||{},n.url)}return e=e.replace(bt.R_MLCOMMS,"").replace(/\s+/g," ").trim(),t&&(e=Ne(t,e)),e}function Se(e,t,r){return t&&"object"==typeof t?(r=t,t=""):r||(r={}),je(e,r.tagName,t,r)}function Te(e,t){return e?(e=sr+e.replace(/\\/g,"\\\\").replace(/'/g,"\\'")+sr,t&&~e.indexOf("\n")?e.replace(/\n/g,"\\n"):e):"''"}function Le(e,t,r,n,i,o,a){var s=a.debug?",\n ":", ",u="});";return i&&"\n"!==i.slice(-1)&&(u="\n"+u),o+"riot.tag2('"+e+sr+s+Te(t,1)+s+Te(r)+s+Te(n)+", function(opts) {\n"+i+u}function Ae(e){if(/<[-\w]/.test(e))for(var t,r=e.lastIndexOf("<"),n=e.length;~r;){if(t=e.slice(r,n).match(dr))return r+=t.index+t[0].length,t=e.slice(0,r),"<-/>\n"===t.slice(-5)&&(t=t.slice(0,-5)),[t,e.slice(r)];n=r,r=e.lastIndexOf("<",r-1)}return["",e]}function Re(e){if(e){var t=e.match(fr);if(t=t&&(t[2]||t[3]))return t.replace("text/","")}return""}function Me(e,t){if(e){var r=e.match(RegExp("\\s"+t+hr,"i"));if(r=r&&r[1])return/^['"]/.test(r)?r.slice(1,-1):r}return""}function Ie(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function ke(e){var t=Ie(Me(e,"options"));return t?JSON.parse(t):null}function $e(e,t,r,n){var i=Re(r),o=Me(r,"src"),a=Ut({},t.parserOptions.js);return!o&&_e(e,t,i,Ut(a,ke(r)),n)}function Fe(e,t,r,n,i){var o=Ut({},t.parserOptions.style),a={parserOpts:Ut(o,ke(r)),url:n};return je(e,i,Re(r)||t.style,a)}function Pe(e,t,r,n){var i=Dt._req("html."+r,!0);return i(e,n,t)}function He(e,t,r){var n,i=[],o={template:{},js:{},style:{}};t||(t={}),t.parserOptions=Ut(o,t.parserOptions||{}),n=t.exclude?function(e){return t.exclude.indexOf(e)<0}:function(){return 1},r||(r="");var a=bt.array(t.brackets);return t.template&&(e=Pe(e,r,t.template,t.parserOptions.template)),e=ve(e).replace(gr,function(e,o,s,u,c,l){var p="",f="",h="",d="",g=[];if(g._bp=a,s=s.toLowerCase(),u=u&&n("attribs")?be(xe(ye(u,t,g),g),g):"",(c||(c=l))&&/\S/.test(c))if(l)n("html")&&(h=we(l,t,g));else{c=c.replace(RegExp("^"+o,"gm"),""),c=c.replace(vr,function(e,i,o){return n("css")&&(f+=(f?" ":"")+Fe(o,t,i,r,s)),""}),c=c.replace(mr,function(e,i,o){if(n("js")){var a=$e(o,t,i,r);a&&(p+=(p?"\n":"")+a)}return""});var m=Ae(c.replace(tr,""));n("html")&&(h=we(m[0],t,g)),n("js")&&(c=_e(m[1],t,null,null,r),c&&(p+=(p?"\n":"")+c),p=p.replace(er,function(e){return d+=e.trim()+"\n",""}))}return p=/\S/.test(p)?p.replace(/\n{3,}/g,"\n\n"):"",t.entities?(i.push({tagName:s,html:h,css:f,attribs:u,js:p,imports:d}),""):Le(s,h,f,u,p,d,t)}),t.entities?i:e}function Be(e,t,r){var n=new XMLHttpRequest;n.onreadystatechange=function(){4===n.readyState&&(200===n.status||!n.status&&n.responseText.length)&&t(n.responseText,r,e)},n.open("GET",e,!0),n.send("")}function ze(e,t){if(typeof e===Je){var r=d("script"),n=document.documentElement;t&&(e+="\n//# sourceURL="+t+".js"),r.text=e,n.appendChild(r),n.removeChild(r)}}function De(e,t){function r(){vt.trigger("ready"),xt=!0,e&&e()}function n(e,t,n){var i=yr.compile(e,t,n);ze(i,n),--o||r()}var i=l('script[type="riot/tag"]'),o=i.length;if(o)for(var a=0;a1?/{[\S\s]*?}/:v[4],t),t[5]=r(e.length>3?/\\({|})/g:v[5],t),t[6]=r(v[6],t),t[7]=RegExp("\\\\("+t[3]+")|([[({])|("+t[3]+")|"+f,c),t[8]=e,t}function i(e){return e instanceof RegExp?s(e):y[e]}function o(e){(e||(e=m))!==y[8]&&(y=n(e),s=e===m?t:r,y[9]=s(v[9])),x=e}function a(e){var t;e=e||{},t=e.brackets,Object.defineProperty(e,"brackets",{set:o,get:function(){return x},enumerable:!0}),u=e,o(t)}var s,u,c="g",l=/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,p=/"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'/g,f=p.source+"|"+/(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source+"|"+/\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?(\/)[gim]*/.source,h=RegExp("[\\x00-\\x1F<>a-zA-Z0-9'\",;\\\\]"),d=/(?=[[\]()*+?.^$|])/g,g={"(":RegExp("([()])|"+f,c),"[":RegExp("([[\\]])|"+f,c),"{":RegExp("([{}])|"+f,c)},m="{ }",v=["{","}","{","}",/{[^}]*}/,/\\([{}])/g,/\\({)|{/g,RegExp("\\\\(})|([[({])|(})|"+f,c),m,/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,/(^|[^\\]){=[\S\s]*?}/],x=e,y=[];return i.split=function(e,t,r){function n(e){t||a?c.push(e&&e.replace(r[5],"$1")):c.push(e)}function i(e,t,r){var n,i=g[t];for(i.lastIndex=r,r=1;(n=i.exec(e))&&(!n[1]||(n[1]===t?++r:--r)););return r?e.length:i.lastIndex}r||(r=y);var o,a,s,u,c=[],l=r[6];for(a=s=l.lastIndex=0;o=l.exec(e);){if(u=o.index,a){if(o[2]){l.lastIndex=i(e,o[2],l.lastIndex);continue}if(!o[3])continue}o[1]||(n(e.slice(s,u)),s=l.lastIndex,l=r[6+(a^=1)],l.lastIndex=s)}return e&&s tag",t.riotData.tagName.toLowerCase()),console.error(t))}function r(e){var t=n(e);return"try{return "!==t.slice(0,11)&&(t="return "+t),new Function("E",t+";")}function n(e){var t,r=[],n=bt.split(e.replace(l,'"'),1);if(n.length>2||n[0]){var o,a,s=[];for(o=a=0;o2&&!t?s+(r.push(e)-1)+"~":e}).replace(/\s+/g," ").trim().replace(/\ ?([[\({},?\.:])\ ?/g,"$1")){for(var i,a=[],l=0;e&&(i=e.match(u))&&!i.index;){var p,h,d=/,|([[{(])|$/g;for(e=RegExp.rightContext,p=i[2]?r[i[2]].slice(1,-1).trim().replace(/\s+/g," "):i[1];h=(i=d.exec(e))[1];)n(h,d);h=e.slice(0,i.index),e=RegExp.rightContext,a[l++]=o(h,1,p)}e=l?l>1?"["+a.join(",")+'].join(" ").trim()':a[0]:o(e,t)}return e}function o(e,t,r){var n;return e=e.replace(d,function(e,t,r,i,o){return r&&(i=n?0:i+e.length,"this"!==r&&"global"!==r&&"window"!==r?(e=t+'("'+r+h+r,i&&(n="."===(o=o[i])||"("===o||"["===o)):i&&(n=!g.test(o.slice(i)))),e}),n&&(e="try{return "+e+"}catch(e){E(e,this)}"),r?e=(n?"function(){"+e+"}.call(this)":"("+e+")")+'?"'+r+'":""':t&&(e="function(v){"+(n?e.replace("return ","v="):"v=("+e+")")+';return v||v===0?v:""}.call(this)'),e}var a={};e.hasExpr=bt.hasExpr,e.loopKeys=bt.loopKeys,e.clearCache=function(){a={}},e.errorHandler=null;var s=String.fromCharCode(8279),u=/^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/,c=RegExp(bt.S_QBLOCKS,"g"),l=/\u2057/g,p=/\u2057(\d+)~/g,f={"(":/[()]/g,"[":/[[\]]/g,"{":/[{}]/g},h='"in this?this:'+("object"!=typeof window?"global":"window")+").",d=/[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,g=/^(?=(\.[$\w]+))\1(?:[^.[(]|$)/;return e.version=bt.version="v3.0.1",e}(),Ct=Object.freeze({each:O,contains:_,toCamel:E,startsWith:N,defineProperty:j,extend:S}),Ot=function(e){e=e||{};var t={},r=Array.prototype.slice;return Object.defineProperties(e,{on:{value:function(r,n){return"function"==typeof n&&(t[r]=t[r]||[]).push(n),e},enumerable:!1,writable:!1,configurable:!1},off:{value:function(r,n){if("*"!=r||n)if(n)for(var i,o=t[r],a=0;i=o&&o[a];++a)i==n&&o.splice(a--,1);else delete t[r];else t={};return e},enumerable:!1,writable:!1,configurable:!1},one:{value:function(t,r){function n(){e.off(t,n),r.apply(e,arguments)}return e.on(t,n)},enumerable:!1,writable:!1,configurable:!1},trigger:{value:function(n){var i,o,a,s=arguments,u=arguments.length-1,c=new Array(u);for(a=0;a|>([\S\s]*?)<\/yield\s*>|>)/gi,Tt=/]*)['"]\s*>([\S\s]*?)<\/yield\s*>/gi,Lt=/|>([\S\s]*?)<\/yield\s*>)/gi,At={tr:"tbody",th:"tr",td:"tr",col:"colgroup"},Rt=pt&&pt<10?it:ot,Mt="div",It={},kt=It[Ke]={},$t=0,Ft=0,Pt=Object.freeze({getTag:te,inheritFrom:re,moveChildTag:ne,initChildTag:ie,getImmediateCustomParentTag:oe,unmountAll:ae,getTagName:se,cleanUpData:ue,arrayishAdd:ce,arrayishRemove:le,isInStub:pe,mountTo:fe,makeVirtual:he,moveVirtual:de,selectTags:ge}),Ht=Object.create(bt.settings),Bt={tmpl:wt,brackets:bt,styleManager:yt,vdom:Ge,styleNode:yt.styleNode,dom:ht,check:ft,misc:Ct,tags:Pt},zt=Object.freeze({settings:Ht,util:Bt,observable:Ot,Tag:G,tag:Z,tag2:K,mount:Q,mixin:W,update:J,unregister:X}),Dt=function(e){function t(t){var r=e[t];if(r)return r;throw new Error('Parser "'+t+'" not loaded.')}function r(e){var t=e.split(".");if(2!==t.length)throw new Error("Bad format for parsers._req");var r=o[t[0]][t[1]];if(r)return r;throw new Error('Parser "'+e+'" not found.')}function n(e,t){if(t)for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}function i(e,r,i,o){return i=n({pretty:!0,filename:o,doctype:"html"},i),t(e).render(r,i)}var o={};return o.html={jade:function(e,t,r){return console.log('DEPRECATION WARNING: jade was renamed "pug" - The jade parser will be removed in riot@3.0.0!'),i("jade",e,t,r)},pug:function(e,t,r){return i("pug",e,t,r)}},o.css={less:function(e,r,i,o){var a;return i=n({sync:!0,syncImport:!0,filename:o},i),t("less").render(r,i,function(e,t){if(e)throw e;a=t.css}),a}},o.js={es6:function(e,r,i){return t("babel").transform(e,n({filename:i},r)).code},buble:function(e,r,i){return r=n({source:i,modules:!1},r),t("buble").transform(e,r).code},coffee:function(e,r){return t("CoffeeScript").compile(e,n({bare:!0},r))},livescript:function(e,r){return t("livescript").compile(e,n({bare:!0,header:!1},r))},typescript:function(e,r){return t("typescript")(e,r)},none:function(e){return e}},o.js.javascript=o.js.none,o.js.coffeescript=o.js.coffee,o._req=r,o.utils={extend:n},o}(window||global),Ut=Dt.utils.extend,Vt=/"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source,qt=bt.R_STRINGS.source,Gt=/ *([-\w:\xA0-\xFF]+) ?(?:= ?('[^']*'|"[^"]*"|\S+))?/g,Zt=RegExp(//.source+"|"+Vt,"g"),Kt=/<(-?[A-Za-z][-\w\xA0-\xFF]*)(?:\s+([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)|\s*)(\/?)>/g,Qt=/>[ \t]+<(-?[A-Za-z]|\/[-A-Za-z])/g,Wt=["style","src","d","value"],Jt=/^(?:input|img|br|wbr|hr|area|base|col|embed|keygen|link|meta|param|source|track)$/,Xt=/]*|"[^"]*")*)?>([\S\s]+?)<\/pre\s*>/gi,Yt=/^"(?:number|date(?:time)?|time|month|email|color)\b/i,er=/^\s*import(?:(?:\s|[^\s'"])*)['|"].*\n?/gm,tr=/[ \t]+$/gm,rr=me(/@#\d/,"x01"),nr=me(/@#(\d+)/g,"x01"),ir="#",or="⁗",ar='"',sr="'",ur=/^[ \t]*([$_A-Za-z][$\w]*)\s*\([^()]*\)\s*{/m,cr=RegExp("[{}]|"+bt.S_QBLOCKS,"g"),lr=RegExp(bt.R_MLCOMMS.source+"|//[^\r\n]*|"+bt.S_QBLOCKS,"g"),pr=RegExp("([{}]|^)[; ]*((?:[^@ ;{}][^{}]*)?[^@ ;{}:] ?)(?={)|"+Vt,"g"),fr=/\stype\s*=\s*(?:(['"])(.+?)\1|(\S+))/i,hr="\\s*=\\s*("+qt+"|{[^}]+}|\\S+)",dr=/\/>\n|^<(?:\/?-?[A-Za-z][-\w\xA0-\xFF]*\s*|-?[A-Za-z][-\w\xA0-\xFF]*\s+[-\w:\xA0-\xFF][\S\s]*?)>\n/,gr=RegExp(/^([ \t]*)<(-?[A-Za-z][-\w\xA0-\xFF]*)(?:\s+([^'"\/>]+(?:(?:@|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\S\s]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/.source.replace("@",qt),"gim"),mr=/]*)?>\n?([\S\s]*?)<\/script\s*>/gi,vr=/]*)?>\n?([\S\s]*?)<\/style\s*>/gi,xr="v3.1.1",yr={ 3 | compile:He,compileHTML:Ce,compileCSS:Se,compileJS:Ee,parsers:Dt,version:xr},br=yr.parsers,wr=function(e,t,i){if(typeof e===Je){if(n(t)&&(i=t,t=!1),/^\s* 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------