Repository: robertkrimen/godocdown Branch: master Commit: 0bfa04905481 Files: 38 Total size: 98.4 KB Directory structure: gitextract_lium6a1f/ ├── .gitignore ├── Makefile ├── README.markdown ├── example/ │ ├── .godocdown.markdown │ └── example.go ├── example.markdown ├── example.template ├── godocdown/ │ ├── .test/ │ │ ├── godocdown.markdown/ │ │ │ ├── .godocdown.markdown │ │ │ ├── .godocdown.md │ │ │ ├── .godocdown.template │ │ │ └── .godocdown.tmpl │ │ ├── godocdown.md/ │ │ │ ├── .godocdown.md │ │ │ ├── .godocdown.template │ │ │ └── .godocdown.tmpl │ │ ├── godocdown.template/ │ │ │ ├── .godocdown.template │ │ │ └── .godocdown.tmpl │ │ ├── godocdown.tmpl/ │ │ │ └── .godocdown.tmpl │ │ └── issue3/ │ │ └── issue3.go │ ├── 7f_shim │ ├── Makefile │ ├── README.markdown │ ├── dbg/ │ │ └── dbg.go │ ├── dbg.go │ ├── go_doc_totext.go │ ├── kilt/ │ │ ├── at.go │ │ ├── exec_command.go │ │ ├── grave_trim.go │ │ ├── kilt.go │ │ ├── print_defaults.go │ │ ├── sha1.go │ │ ├── symlink.go │ │ └── write_atomic_file.go │ ├── kilt.go │ ├── main.go │ ├── main_test.go │ ├── render.go │ └── terst/ │ └── terst.go └── godocdown.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /godocdown/godocdown ================================================ FILE: Makefile ================================================ .PHONY: build test install release build test install: $(MAKE) -C godocdown $@ release: build godocdown/godocdown -template example.template $(HOME)/go/src/pkg/strings > example.markdown (cd godocdown && ./godocdown -signature > README.markdown) || false cp godocdown/README.markdown . ================================================ FILE: README.markdown ================================================ # godocdown -- Command godocdown generates Go documentation in a GitHub-friendly Markdown format. $ go get github.com/robertkrimen/godocdown/godocdown $ godocdown /path/to/package > README.markdown # Generate documentation for the package/command in the current directory $ godocdown > README.markdown # Generate standard Markdown $ godocdown -plain . This program is targeted at providing nice-looking documentation for GitHub. With this in mind, it generates GitHub Flavored Markdown (http://github.github.com/github-flavored-markdown/) by default. This can be changed with the use of the "plain" flag to generate standard Markdown. ### Install go get github.com/robertkrimen/godocdown/godocdown ### Example http://github.com/robertkrimen/godocdown/blob/master/example.markdown ### Usage -output="" Write output to a file instead of stdout Write to stdout with - -template="" The template file to use -no-template=false Disable template processing -plain=false Emit standard Markdown, rather than Github Flavored Markdown -heading="TitleCase1Word" Heading detection method: 1Word, TitleCase, Title, TitleCase1Word, "" For each line of the package declaration, godocdown attempts to detect if a heading is present via a pattern match. If a heading is detected, it prefixes the line with a Markdown heading indicator (typically "###"). 1Word: Only a single word on the entire line [A-Za-z0-9_-]+ TitleCase: A line where each word has the first letter capitalized ([A-Z][A-Za-z0-9_-]\s*)+ Title: A line without punctuation (e.g. a period at the end) ([A-Za-z0-9_-]\s*)+ TitleCase1Word: The line matches either the TitleCase or 1Word pattern ### Templating In addition to Markdown rendering, godocdown provides templating via text/template (http://golang.org/pkg/text/template/) for further customization. By putting a file named ".godocdown.template" (or one from the list below) in the same directory as your package/command, godocdown will know to use the file as a template. # text/template .godocdown.markdown .godocdown.md .godocdown.template .godocdown.tmpl A template file can also be specified with the "-template" parameter Along with the standard template functionality, the starting data argument has the following interface: {{ .Emit }} // Emit the standard documentation (what godocdown would emit without a template) {{ .EmitHeader }} // Emit the package name and an import line (if one is present/needed) {{ .EmitSynopsis }} // Emit the package declaration {{ .EmitUsage }} // Emit package usage, which includes a constants section, a variables section, // a functions section, and a types section. In addition, each type may have its own constant, // variable, and/or function/method listing. {{ if .IsCommand }} ... {{ end }} // A boolean indicating whether the given package is a command or a plain package {{ .Name }} // The name of the package/command (string) {{ .ImportPath }} // The import path for the package (string) // (This field will be the empty string if godocdown is unable to guess it) -- **godocdown** http://github.com/robertkrimen/godocdown ================================================ FILE: example/.godocdown.markdown ================================================ {{ .Emit }} {{ .EmitSignature }} -- This was via template ================================================ FILE: example/example.go ================================================ /* Package example is an example package with documentation // Here is some code func example() { abc := 1 + 1 }() Installation # This is how to install it: $ curl http://example.com $ tar xf example.tar.gz -C . $ ./example & */ package example // A constant section const Other = 3 // A variable section var ( This = 1 this = 0 // A description of That That = 2.1 ) // Another constant section const ( Another = 0 Again = "this" ) // Example is a function that does nothing func Example() { } // ExampleType is a type of nothing // // // Here is how to use it: // return &ExampleType{ // First: 1, // Second: "second", // nil, // } type ExampleType struct { First int Second string Third float64 Parent *ExampleType first int hidden string } func (ExampleType) Set() bool { return false } func NewExample() *ExampleType { return &ExampleType{} } ================================================ FILE: example.markdown ================================================ # Example godocdown (strings) This markdown was generated with the help of custom template file ([example.template](http://github.com/robertkrimen/godocdown/blob/master/example.template)). To add custom markdown to your documentation, you can do something like: godocdown -template=godocdown.tmpl ... The template format is the standard Go text/template: http://golang.org/pkg/text/template Along with the standard template functionality, the starting data argument has the following interface: {{ .Emit }} // Emit the standard documentation (what godocdown would emit without a template) {{ .EmitHeader }} // Emit the package name and an import line (if one is present/needed) {{ .EmitSynopsis }} // Emit the package declaration {{ .EmitUsage }} // Emit package usage, which includes a constants section, a variables section, // a functions section, and a types section. In addition, each type may have its own constant, // variable, and/or function/method listing. {{ if .IsCommand }} ... {{ end }} // A boolean indicating whether the given package is a command or a plain package {{ .Name }} // The name of the package/command (string) {{ .ImportPath }} // The import path for the package (string) // (This field will be the empty string if godocdown is unable to guess it) godocdown for the http://golang.org/pkg/strings package: -- # strings -- import "strings" Package strings implements simple functions to manipulate strings. ## Usage #### func Contains ```go func Contains(s, substr string) bool ``` Contains returns true if substr is within s. #### func ContainsAny ```go func ContainsAny(s, chars string) bool ``` ContainsAny returns true if any Unicode code points in chars are within s. #### func ContainsRune ```go func ContainsRune(s string, r rune) bool ``` ContainsRune returns true if the Unicode code point r is within s. #### func Count ```go func Count(s, sep string) int ``` Count counts the number of non-overlapping instances of sep in s. #### func EqualFold ```go func EqualFold(s, t string) bool ``` EqualFold reports whether s and t, interpreted as UTF-8 strings, are equal under Unicode case-folding. #### func Fields ```go func Fields(s string) []string ``` Fields splits the string s around each instance of one or more consecutive white space characters, as defined by unicode.IsSpace, returning an array of substrings of s or an empty list if s contains only white space. #### func FieldsFunc ```go func FieldsFunc(s string, f func(rune) bool) []string ``` FieldsFunc splits the string s at each run of Unicode code points c satisfying f(c) and returns an array of slices of s. If all code points in s satisfy f(c) or the string is empty, an empty slice is returned. #### func HasPrefix ```go func HasPrefix(s, prefix string) bool ``` HasPrefix tests whether the string s begins with prefix. #### func HasSuffix ```go func HasSuffix(s, suffix string) bool ``` HasSuffix tests whether the string s ends with suffix. #### func Index ```go func Index(s, sep string) int ``` Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. #### func IndexAny ```go func IndexAny(s, chars string) int ``` IndexAny returns the index of the first instance of any Unicode code point from chars in s, or -1 if no Unicode code point from chars is present in s. #### func IndexFunc ```go func IndexFunc(s string, f func(rune) bool) int ``` IndexFunc returns the index into s of the first Unicode code point satisfying f(c), or -1 if none do. #### func IndexRune ```go func IndexRune(s string, r rune) int ``` IndexRune returns the index of the first instance of the Unicode code point r, or -1 if rune is not present in s. #### func Join ```go func Join(a []string, sep string) string ``` Join concatenates the elements of a to create a single string. The separator string sep is placed between elements in the resulting string. #### func LastIndex ```go func LastIndex(s, sep string) int ``` LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s. #### func LastIndexAny ```go func LastIndexAny(s, chars string) int ``` LastIndexAny returns the index of the last instance of any Unicode code point from chars in s, or -1 if no Unicode code point from chars is present in s. #### func LastIndexFunc ```go func LastIndexFunc(s string, f func(rune) bool) int ``` LastIndexFunc returns the index into s of the last Unicode code point satisfying f(c), or -1 if none do. #### func Map ```go func Map(mapping func(rune) rune, s string) string ``` Map returns a copy of the string s with all its characters modified according to the mapping function. If mapping returns a negative value, the character is dropped from the string with no replacement. #### func Repeat ```go func Repeat(s string, count int) string ``` Repeat returns a new string consisting of count copies of the string s. #### func Replace ```go func Replace(s, old, new string, n int) string ``` Replace returns a copy of the string s with the first n non-overlapping instances of old replaced by new. If n < 0, there is no limit on the number of replacements. #### func Split ```go func Split(s, sep string) []string ``` Split slices s into all substrings separated by sep and returns a slice of the substrings between those separators. If sep is empty, Split splits after each UTF-8 sequence. It is equivalent to SplitN with a count of -1. #### func SplitAfter ```go func SplitAfter(s, sep string) []string ``` SplitAfter slices s into all substrings after each instance of sep and returns a slice of those substrings. If sep is empty, SplitAfter splits after each UTF-8 sequence. It is equivalent to SplitAfterN with a count of -1. #### func SplitAfterN ```go func SplitAfterN(s, sep string, n int) []string ``` SplitAfterN slices s into substrings after each instance of sep and returns a slice of those substrings. If sep is empty, SplitAfterN splits after each UTF-8 sequence. The count determines the number of substrings to return: n > 0: at most n substrings; the last substring will be the unsplit remainder. n == 0: the result is nil (zero substrings) n < 0: all substrings #### func SplitN ```go func SplitN(s, sep string, n int) []string ``` SplitN slices s into substrings separated by sep and returns a slice of the substrings between those separators. If sep is empty, SplitN splits after each UTF-8 sequence. The count determines the number of substrings to return: n > 0: at most n substrings; the last substring will be the unsplit remainder. n == 0: the result is nil (zero substrings) n < 0: all substrings #### func Title ```go func Title(s string) string ``` Title returns a copy of the string s with all Unicode letters that begin words mapped to their title case. BUG: The rule Title uses for word boundaries does not handle Unicode punctuation properly. #### func ToLower ```go func ToLower(s string) string ``` ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. #### func ToLowerSpecial ```go func ToLowerSpecial(_case unicode.SpecialCase, s string) string ``` ToLowerSpecial returns a copy of the string s with all Unicode letters mapped to their lower case, giving priority to the special casing rules. #### func ToTitle ```go func ToTitle(s string) string ``` ToTitle returns a copy of the string s with all Unicode letters mapped to their title case. #### func ToTitleSpecial ```go func ToTitleSpecial(_case unicode.SpecialCase, s string) string ``` ToTitleSpecial returns a copy of the string s with all Unicode letters mapped to their title case, giving priority to the special casing rules. #### func ToUpper ```go func ToUpper(s string) string ``` ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case. #### func ToUpperSpecial ```go func ToUpperSpecial(_case unicode.SpecialCase, s string) string ``` ToUpperSpecial returns a copy of the string s with all Unicode letters mapped to their upper case, giving priority to the special casing rules. #### func Trim ```go func Trim(s string, cutset string) string ``` Trim returns a slice of the string s with all leading and trailing Unicode code points contained in cutset removed. #### func TrimFunc ```go func TrimFunc(s string, f func(rune) bool) string ``` TrimFunc returns a slice of the string s with all leading and trailing Unicode code points c satisfying f(c) removed. #### func TrimLeft ```go func TrimLeft(s string, cutset string) string ``` TrimLeft returns a slice of the string s with all leading Unicode code points contained in cutset removed. #### func TrimLeftFunc ```go func TrimLeftFunc(s string, f func(rune) bool) string ``` TrimLeftFunc returns a slice of the string s with all leading Unicode code points c satisfying f(c) removed. #### func TrimPrefix ```go func TrimPrefix(s, prefix string) string ``` TrimPrefix returns s without the provided leading prefix string. If s doesn't start with prefix, s is returned unchanged. #### func TrimRight ```go func TrimRight(s string, cutset string) string ``` TrimRight returns a slice of the string s, with all trailing Unicode code points contained in cutset removed. #### func TrimRightFunc ```go func TrimRightFunc(s string, f func(rune) bool) string ``` TrimRightFunc returns a slice of the string s with all trailing Unicode code points c satisfying f(c) removed. #### func TrimSpace ```go func TrimSpace(s string) string ``` TrimSpace returns a slice of the string s, with all leading and trailing white space removed, as defined by Unicode. #### func TrimSuffix ```go func TrimSuffix(s, suffix string) string ``` TrimSuffix returns s without the provided trailing suffix string. If s doesn't end with suffix, s is returned unchanged. #### type Reader ```go type Reader struct { } ``` A Reader implements the io.Reader, io.ReaderAt, io.Seeker, io.WriterTo, io.ByteScanner, and io.RuneScanner interfaces by reading from a string. #### func NewReader ```go func NewReader(s string) *Reader ``` NewReader returns a new Reader reading from s. It is similar to bytes.NewBufferString but more efficient and read-only. #### func (*Reader) Len ```go func (r *Reader) Len() int ``` Len returns the number of bytes of the unread portion of the string. #### func (*Reader) Read ```go func (r *Reader) Read(b []byte) (n int, err error) ``` #### func (*Reader) ReadAt ```go func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) ``` #### func (*Reader) ReadByte ```go func (r *Reader) ReadByte() (b byte, err error) ``` #### func (*Reader) ReadRune ```go func (r *Reader) ReadRune() (ch rune, size int, err error) ``` #### func (*Reader) Seek ```go func (r *Reader) Seek(offset int64, whence int) (int64, error) ``` Seek implements the io.Seeker interface. #### func (*Reader) UnreadByte ```go func (r *Reader) UnreadByte() error ``` #### func (*Reader) UnreadRune ```go func (r *Reader) UnreadRune() error ``` #### func (*Reader) WriteTo ```go func (r *Reader) WriteTo(w io.Writer) (n int64, err error) ``` WriteTo implements the io.WriterTo interface. #### type Replacer ```go type Replacer struct { } ``` A Replacer replaces a list of strings with replacements. #### func NewReplacer ```go func NewReplacer(oldnew ...string) *Replacer ``` NewReplacer returns a new Replacer from a list of old, new string pairs. Replacements are performed in order, without overlapping matches. #### func (*Replacer) Replace ```go func (r *Replacer) Replace(s string) string ``` Replace returns a copy of s with all replacements performed. #### func (*Replacer) WriteString ```go func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) ``` WriteString writes s to w with all replacements performed. ================================================ FILE: example.template ================================================ # Example godocdown (strings) This markdown was generated with the help of custom template file ([example.template](http://github.com/robertkrimen/godocdown/blob/master/example.template)). To add custom markdown to your documentation, you can do something like: godocdown -template=godocdown.tmpl ... The template format is the standard Go text/template: http://golang.org/pkg/text/template Along with the standard template functionality, the starting data argument has the following interface: {{ "{{ .Emit }}" }} // Emit the standard documentation (what godocdown would emit without a template) {{ "{{ .EmitHeader }}" }} // Emit the package name and an import line (if one is present/needed) {{ "{{ .EmitSynopsis }}" }} // Emit the package declaration {{ "{{ .EmitUsage }}" }} // Emit package usage, which includes a constants section, a variables section, // a functions section, and a types section. In addition, each type may have its own constant, // variable, and/or function/method listing. {{ "{{ if .IsCommand }} ... {{ end }}" }} // A boolean indicating whether the given package is a command or a plain package {{ "{{ .Name }}" }} // The name of the package/command (string) {{ "{{ .ImportPath }}" }} // The import path for the package (string) // (This field will be the empty string if godocdown is unable to guess it) godocdown for the http://golang.org/pkg/strings package: -- {{ .Emit }} ================================================ FILE: godocdown/.test/godocdown.markdown/.godocdown.markdown ================================================ ================================================ FILE: godocdown/.test/godocdown.markdown/.godocdown.md ================================================ ================================================ FILE: godocdown/.test/godocdown.markdown/.godocdown.template ================================================ ================================================ FILE: godocdown/.test/godocdown.markdown/.godocdown.tmpl ================================================ ================================================ FILE: godocdown/.test/godocdown.md/.godocdown.md ================================================ ================================================ FILE: godocdown/.test/godocdown.md/.godocdown.template ================================================ ================================================ FILE: godocdown/.test/godocdown.md/.godocdown.tmpl ================================================ ================================================ FILE: godocdown/.test/godocdown.template/.godocdown.template ================================================ ================================================ FILE: godocdown/.test/godocdown.template/.godocdown.tmpl ================================================ ================================================ FILE: godocdown/.test/godocdown.tmpl/.godocdown.tmpl ================================================ ================================================ FILE: godocdown/.test/issue3/issue3.go ================================================ // Documentation for package issue3 // // Nothing happens. // // Some code happens. package issue3 // Documentation for func Test() // // Something happens. // // Some code happens. func Test() { } ================================================ FILE: godocdown/7f_shim ================================================ #!/usr/bin/env perl use strict; use warnings; my @input = ; my $longest = 0; for (@input) { chomp; s/\s*\x{7f}\s*$//; my $length = length $_; if ($length > $longest) { $longest = $length; } } $longest += 4; for (@input) { chomp; my $length = length $_; print $_; print " " x ($longest - $length); print "\x{7f}\n"; } ================================================ FILE: godocdown/Makefile ================================================ .PHONY: build test test-example install release build: test go build test: go test -i go test test-example: build ./godocdown -signature example > test/README.markdown #cd test && git commit -m 'WIP' * && git push install: go install release: $(MAKE) -C .. $@ ================================================ FILE: godocdown/README.markdown ================================================ # godocdown -- Command godocdown generates Go documentation in a GitHub-friendly Markdown format. $ go get github.com/robertkrimen/godocdown/godocdown $ godocdown /path/to/package > README.markdown # Generate documentation for the package/command in the current directory $ godocdown > README.markdown # Generate standard Markdown $ godocdown -plain . This program is targeted at providing nice-looking documentation for GitHub. With this in mind, it generates GitHub Flavored Markdown (http://github.github.com/github-flavored-markdown/) by default. This can be changed with the use of the "plain" flag to generate standard Markdown. ### Install go get github.com/robertkrimen/godocdown/godocdown ### Example http://github.com/robertkrimen/godocdown/blob/master/example.markdown ### Usage -output="" Write output to a file instead of stdout Write to stdout with - -template="" The template file to use -no-template=false Disable template processing -plain=false Emit standard Markdown, rather than Github Flavored Markdown -heading="TitleCase1Word" Heading detection method: 1Word, TitleCase, Title, TitleCase1Word, "" For each line of the package declaration, godocdown attempts to detect if a heading is present via a pattern match. If a heading is detected, it prefixes the line with a Markdown heading indicator (typically "###"). 1Word: Only a single word on the entire line [A-Za-z0-9_-]+ TitleCase: A line where each word has the first letter capitalized ([A-Z][A-Za-z0-9_-]\s*)+ Title: A line without punctuation (e.g. a period at the end) ([A-Za-z0-9_-]\s*)+ TitleCase1Word: The line matches either the TitleCase or 1Word pattern ### Templating In addition to Markdown rendering, godocdown provides templating via text/template (http://golang.org/pkg/text/template/) for further customization. By putting a file named ".godocdown.template" (or one from the list below) in the same directory as your package/command, godocdown will know to use the file as a template. # text/template .godocdown.markdown .godocdown.md .godocdown.template .godocdown.tmpl A template file can also be specified with the "-template" parameter Along with the standard template functionality, the starting data argument has the following interface: {{ .Emit }} // Emit the standard documentation (what godocdown would emit without a template) {{ .EmitHeader }} // Emit the package name and an import line (if one is present/needed) {{ .EmitSynopsis }} // Emit the package declaration {{ .EmitUsage }} // Emit package usage, which includes a constants section, a variables section, // a functions section, and a types section. In addition, each type may have its own constant, // variable, and/or function/method listing. {{ if .IsCommand }} ... {{ end }} // A boolean indicating whether the given package is a command or a plain package {{ .Name }} // The name of the package/command (string) {{ .ImportPath }} // The import path for the package (string) // (This field will be the empty string if godocdown is unable to guess it) -- **godocdown** http://github.com/robertkrimen/godocdown ================================================ FILE: godocdown/dbg/dbg.go ================================================ // This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg /* Package dbg is a println/printf/log-debugging utility library. import ( Dbg "github.com/robertkrimen/dbg" ) dbg, dbgf := Dbg.New() dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi) # "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793" dbgf("With a %s formatting %.2f", "little", math.Pi) # "2013/01/28 16:51:55 With a little formatting (3.14)" dbgf("%/fatal//A fatal debug statement: should not be here") # "A fatal debug statement: should not be here" # ...and then, os.Exit(1) dbgf("%/panic//Can also panic %s", "this") # "Can also panic this" # ...as a panic, equivalent to: panic("Can also panic this") dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()") # "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()" dbgf("%d %d", 1, 2, 3, 4, 5) # "2013/01/28 17:16:32 Another example: 1 2 3 4 5" dbgf("%@: Include the function name for a little context (via %s)", "%@") # "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)" By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output. However, you can also provide your own output destination by invoking dbg.New with a customization function: import ( "bytes" Dbg "github.com/robertkrimen/dbg" "os" ) # dbg to os.Stderr dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { dbgr.SetOutput(os.Stderr) }) # A slightly contrived example: var buffer bytes.Buffer dbg, dbgf := New(func(dbgr *Dbgr) { dbgr.SetOutput(&buffer) }) */ package dbg import ( "bytes" "fmt" "io" "log" "os" "regexp" "runtime" "strings" "unicode" ) type _frmt struct { ctl string format string operandCount int panic bool fatal bool check bool } var ( ctlTest = regexp.MustCompile(`^\s*%/`) ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`) ) func operandCount(format string) int { count := 0 end := len(format) for at := 0; at < end; { for at < end && format[at] != '%' { at++ } at++ if at < end { if format[at] != '%' && format[at] != '@' { count++ } at++ } } return count } func parseFormat(format string) (frmt _frmt) { if ctlTest.MatchString(format) { format = strings.TrimLeftFunc(format, unicode.IsSpace) index := strings.Index(format, "//") if index != -1 { frmt.ctl = format[0:index] format = format[index+2:] // Skip the second slash via +2 (instead of +1) } else { frmt.ctl = format format = "" } for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) { for _, value := range tmp[1:] { switch value { case "panic": frmt.panic = true case "fatal": frmt.fatal = true case "check": frmt.check = true } } } } frmt.format = format frmt.operandCount = operandCount(format) return } type Dbgr struct { emit _emit } type DbgFunction func(values ...interface{}) func NewDbgr() *Dbgr { self := &Dbgr{} return self } /* New will create and return a pair of debugging functions. You can customize where they output to by passing in an (optional) customization function: import ( Dbg "github.com/robertkrimen/dbg" "os" ) # dbg to os.Stderr dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { dbgr.SetOutput(os.Stderr) }) */ func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) { dbgr := NewDbgr() if len(options) > 0 { if fn, ok := options[0].(func(*Dbgr)); ok { fn(dbgr) } } return dbgr.DbgDbgf() } func (self Dbgr) Dbg(values ...interface{}) { self.getEmit().emit(_frmt{}, "", values...) } func (self Dbgr) Dbgf(values ...interface{}) { self.dbgf(values...) } func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) { dbg = func(vl ...interface{}) { self.Dbg(vl...) } dbgf = func(vl ...interface{}) { self.dbgf(vl...) } return dbg, dbgf // Redundant, but... } func (self Dbgr) dbgf(values ...interface{}) { var frmt _frmt if len(values) > 0 { tmp := fmt.Sprint(values[0]) frmt = parseFormat(tmp) values = values[1:] } buffer_f := bytes.Buffer{} format := frmt.format end := len(format) for at := 0; at < end; { last := at for at < end && format[at] != '%' { at++ } if at > last { buffer_f.WriteString(format[last:at]) } if at >= end { break } // format[at] == '%' at++ // format[at] == ? if format[at] == '@' { depth := 2 pc, _, _, _ := runtime.Caller(depth) name := runtime.FuncForPC(pc).Name() buffer_f.WriteString(name) } else { buffer_f.WriteString(format[at-1 : at+1]) } at++ } //values_f := append([]interface{}{}, values[0:frmt.operandCount]...) values_f := values[0:frmt.operandCount] values_dbg := values[frmt.operandCount:] if len(values_dbg) > 0 { // Adjust frmt.format: // (%v instead of %s because: frmt.check) { tmp := format if len(tmp) > 0 { if unicode.IsSpace(rune(tmp[len(tmp)-1])) { buffer_f.WriteString("%v") } else { buffer_f.WriteString(" %v") } } else if frmt.check { // Performing a check, so no output } else { buffer_f.WriteString("%v") } } // Adjust values_f: if !frmt.check { tmp := []string{} for _, value := range values_dbg { tmp = append(tmp, fmt.Sprintf("%v", value)) } // First, make a copy of values_f, so we avoid overwriting values_dbg when appending values_f = append([]interface{}{}, values_f...) values_f = append(values_f, strings.Join(tmp, " ")) } } format = buffer_f.String() if frmt.check { // We do not actually emit to the log, but panic if // a non-nil value is detected (e.g. a non-nil error) for _, value := range values_dbg { if value != nil { if format == "" { panic(value) } else { panic(fmt.Sprintf(format, append(values_f, value)...)) } } } } else { self.getEmit().emit(frmt, format, values_f...) } } // Idiot-proof &Dbgr{}, etc. func (self *Dbgr) getEmit() _emit { if self.emit == nil { self.emit = standardEmit() } return self.emit } // SetOutput will accept the following as a destination for output: // // *log.Logger Print*/Panic*/Fatal* of the logger // io.Writer - // nil Reset to the default output (os.Stderr) // "log" Print*/Panic*/Fatal* via the "log" package // func (self *Dbgr) SetOutput(output interface{}) { if output == nil { self.emit = standardEmit() return } switch output := output.(type) { case *log.Logger: self.emit = _emitLogger{ logger: output, } return case io.Writer: self.emit = _emitWriter{ writer: output, } return case string: if output == "log" { self.emit = _emitLog{} return } } panic(output) } // ======== // // = emit = // // ======== // func standardEmit() _emit { return _emitWriter{ writer: os.Stderr, } } func ln(tmp string) string { length := len(tmp) if length > 0 && tmp[length-1] != '\n' { return tmp + "\n" } return tmp } type _emit interface { emit(_frmt, string, ...interface{}) } type _emitWriter struct { writer io.Writer } func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) { if format == "" { fmt.Fprintln(self.writer, values...) } else { if frmt.panic { panic(fmt.Sprintf(format, values...)) } fmt.Fprintf(self.writer, ln(format), values...) if frmt.fatal { os.Exit(1) } } } type _emitLogger struct { logger *log.Logger } func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) { if format == "" { self.logger.Println(values...) } else { if frmt.panic { self.logger.Panicf(format, values...) } else if frmt.fatal { self.logger.Fatalf(format, values...) } else { self.logger.Printf(format, values...) } } } type _emitLog struct { } func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) { if format == "" { log.Println(values...) } else { if frmt.panic { log.Panicf(format, values...) } else if frmt.fatal { log.Fatalf(format, values...) } else { log.Printf(format, values...) } } } ================================================ FILE: godocdown/dbg.go ================================================ // This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg package main import ( Dbg "github.com/robertkrimen/godocdown/godocdown/dbg" ) var dbg, dbgf = Dbg.New() ================================================ FILE: godocdown/go_doc_totext.go ================================================ package main // Our own, slightly different version of ToText. import ( "io" "regexp" "strings" "unicode" "unicode/utf8" ) var ( ldquo = []byte("“") rdquo = []byte("”") ) const ( // Regexp for Go identifiers identRx = `[a-zA-Z_][a-zA-Z_0-9]*` // TODO(gri) ASCII only for now - fix this // Regexp for URLs protocol = `(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero):` hostPart = `[a-zA-Z0-9_@\-]+` filePart = `[a-zA-Z0-9_?%#~&/\-+=]+` urlRx = protocol + `//` + // http:// hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/ filePart + `([:.,]` + filePart + `)*` ) var matchRx = regexp.MustCompile(`(` + urlRx + `)|(` + identRx + `)`) func indentLen(s string) int { i := 0 for i < len(s) && (s[i] == ' ' || s[i] == '\t') { i++ } return i } func isBlank(s string) bool { return len(s) == 0 || (len(s) == 1 && s[0] == '\n') } func commonPrefix(a, b string) string { i := 0 for i < len(a) && i < len(b) && a[i] == b[i] { i++ } return a[0:i] } func unindent(block []string) { if len(block) == 0 { return } // compute maximum common white prefix prefix := block[0][0:indentLen(block[0])] for _, line := range block { if !isBlank(line) { prefix = commonPrefix(prefix, line[0:indentLen(line)]) } } n := len(prefix) // remove for i, line := range block { if !isBlank(line) { block[i] = line[n:] } } } // heading returns the trimmed line if it passes as a section heading; // otherwise it returns the empty string. func heading(line string) string { line = strings.TrimSpace(line) if len(line) == 0 { return "" } // a heading must start with an uppercase letter r, _ := utf8.DecodeRuneInString(line) if !unicode.IsLetter(r) || !unicode.IsUpper(r) { return "" } // it must end in a letter or digit: r, _ = utf8.DecodeLastRuneInString(line) if !unicode.IsLetter(r) && !unicode.IsDigit(r) { return "" } // exclude lines with illegal characters if strings.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 { return "" } // allow "'" for possessive "'s" only for b := line; ; { i := strings.IndexRune(b, '\'') if i < 0 { break } if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') { return "" // not followed by "s " } b = b[i+2:] } return line } type op int const ( opPara op = iota opHead opPre ) type block struct { op op lines []string } var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`) func anchorID(line string) string { // Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols. return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_") } func blocks(text string) []block { var ( out []block para []string lastWasBlank = false lastWasHeading = false ) close := func() { if para != nil { out = append(out, block{opPara, para}) para = nil } } lines := strings.SplitAfter(text, "\n") unindent(lines) for i := 0; i < len(lines); { line := lines[i] if isBlank(line) { // close paragraph close() i++ lastWasBlank = true continue } if indentLen(line) > 0 { // close paragraph close() // count indented or blank lines j := i + 1 for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) { j++ } // but not trailing blank lines for j > i && isBlank(lines[j-1]) { j-- } pre := lines[i:j] i = j unindent(pre) // put those lines in a pre block out = append(out, block{opPre, pre}) lastWasHeading = false continue } if lastWasBlank && !lastWasHeading && i+2 < len(lines) && isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 { // current line is non-blank, surrounded by blank lines // and the next non-blank line is not indented: this // might be a heading. if head := heading(line); head != "" { close() out = append(out, block{opHead, []string{head}}) i += 2 lastWasHeading = true continue } } // open paragraph lastWasBlank = false lastWasHeading = false para = append(para, lines[i]) i++ } close() return out } // ToText prepares comment text for presentation in textual output. // It wraps paragraphs of text to width or fewer Unicode code points // and then prefixes each line with the indent. In preformatted sections // (such as program text), it prefixes each non-blank line with preIndent. func toText(w io.Writer, text string, indent, preIndent string, width int) { l := lineWrapper{ out: w, width: width, indent: indent, } for _, b := range blocks(text) { switch b.op { case opPara: // l.write will add leading newline if required for _, line := range b.lines { l.write(line) } l.flush() case opHead: w.Write(nl) for _, line := range b.lines { l.write(line + "\n") } l.flush() case opPre: w.Write(nl) for _, line := range b.lines { if !isBlank(line) { w.Write([]byte(preIndent)) w.Write([]byte(line)) } else { w.Write([]byte(line)) } } } } } type lineWrapper struct { out io.Writer printed bool width int indent string n int pendSpace int } var nl = []byte("\n") var space = []byte(" ") func (l *lineWrapper) write(text string) { if l.n == 0 && l.printed { l.out.Write(nl) // blank line before new paragraph } l.printed = true for _, f := range strings.Fields(text) { w := utf8.RuneCountInString(f) // wrap if line is too long if l.n > 0 && l.n+l.pendSpace+w > l.width { l.out.Write(nl) l.n = 0 l.pendSpace = 0 } if l.n == 0 { l.out.Write([]byte(l.indent)) } l.out.Write(space[:l.pendSpace]) l.out.Write([]byte(f)) l.n += l.pendSpace + w l.pendSpace = 1 } } func (l *lineWrapper) flush() { if l.n == 0 { return } l.out.Write(nl) l.pendSpace = 0 l.n = 0 } ================================================ FILE: godocdown/kilt/at.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "os" ) // At func (self Kilt) At(there string, fn func() error) []error { return At(there, fn) } func At(there string, fn func() error) (err []error) { origin, err_ := os.Getwd() if err_ != nil { origin = "" return []error{err_, nil} } err_ = os.Chdir(there) if err_ != nil { origin = "" return []error{err_, nil} } defer func() { if origin != "" { err_ = os.Chdir(origin) if err != nil { err[0] = err_ } else { err = []error{err_, nil} } } }() err_ = fn() if err_ != nil { return []error{nil, err_} } return nil } ================================================ FILE: godocdown/kilt/exec_command.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "os/exec" ) // ExecCommand func commandArguments(values ...string) []string { arguments := make([]string, 0, len(values)) for _, value := range values { if value == "\x00" { continue } arguments = append(arguments, value) } return arguments } func (self Kilt) ExecCommand(program string, arguments ...string) *exec.Cmd { return ExecCommand(program, arguments...) } func ExecCommand(program string, arguments ...string) *exec.Cmd { return exec.Command(program, commandArguments(arguments...)...) } ================================================ FILE: godocdown/kilt/grave_trim.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "unicode" ) func (self Kilt) GraveTrim(target string) string { return GraveTrim(target) } // GraveTrim func GraveTrim(target string) string { // Discard \r? Go already does this for raw string literals. end := len(target) last := 0 index := 0 for index = 0; index < end; index++ { chr := rune(target[index]) if chr == '\n' || !unicode.IsSpace(chr) { last = index break } } if index >= end { return "" } start := last if rune(target[start]) == '\n' { // Skip the leading newline start++ } last = end - 1 for index = last; index > start; index-- { chr := rune(target[index]) if chr == '\n' || !unicode.IsSpace(chr) { last = index break } } stop := last result := target[start : stop+1] return result } ================================================ FILE: godocdown/kilt/kilt.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt type Kilt struct { } func New() *Kilt { return &Kilt{} } ================================================ FILE: godocdown/kilt/print_defaults.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "bufio" "bytes" Flag "flag" "fmt" "os" "strings" ) // PrintDefaults func (self Kilt) PrintDefaults(flag *Flag.FlagSet) { PrintDefaults(flag) } func PrintDefaults(flag *Flag.FlagSet) { var into bytes.Buffer flag.SetOutput(&into) flag.PrintDefaults() outfrom := bufio.NewReader(&into) for { line, err := outfrom.ReadString('\n') if err != nil { break } if strings.HasSuffix(line, ": \x00\n") { continue } fmt.Fprint(os.Stderr, line) } } ================================================ FILE: godocdown/kilt/sha1.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "crypto/sha1" "encoding/hex" "io" "os" "path/filepath" ) // Sha1Path func (self Kilt) Sha1Path(path ...string) string { return Sha1Path(path...) } func Sha1Path(path ...string) string { file, err := os.Open(filepath.Join(path...)) if err != nil { return "" } return Sha1Of(file) } // Sha1Of func (self Kilt) Sha1Of(src io.Reader) string { return Sha1Of(src) } func Sha1Of(src io.Reader) string { hash := sha1.New() _, err := io.Copy(hash, src) if err != nil { return "" } return hex.EncodeToString(hash.Sum(nil)) } // Sha1 func (self Kilt) Sha1(data []byte) string { return Sha1(data) } func Sha1(data []byte) string { hash := sha1.New() hash.Write(data) return hex.EncodeToString(hash.Sum(nil)) } ================================================ FILE: godocdown/kilt/symlink.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "os" ) // Symlink func (self Kilt) Symlink(oldname, newname string, overwrite bool) error { return Symlink(oldname, newname, overwrite) } func Symlink(oldname, newname string, overwrite bool) error { err := os.Symlink(oldname, newname) if err == nil { return nil // Success } if !os.IsExist(err) { return err // Failure } // Failure, file exists symbolic := false { stat, err := os.Lstat(newname) if err != nil { return err } symbolic = stat.Mode()&os.ModeSymlink != 0 } if !symbolic { return err } if !overwrite { return nil } err = os.Remove(newname) if err != nil { return err } return os.Symlink(oldname, newname) } ================================================ FILE: godocdown/kilt/write_atomic_file.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) from github.com/robertkrimen/kilt package kilt import ( "fmt" "io" "io/ioutil" "os" "path/filepath" ) // WriteAtomicFile func (self Kilt) WriteAtomicFile(filename string, data io.Reader, mode os.FileMode) error { return WriteAtomicFile(filename, data, mode) } func WriteAtomicFile(filename string, data io.Reader, mode os.FileMode) error { parent := filepath.Dir(filename) tmp, err := ioutil.TempDir(parent, ".tmp.") if err != nil { return err } if len(parent) >= len(tmp) { // Should never, ever happen return (fmt.Errorf("%s < %s", tmp, parent)) } defer os.RemoveAll(tmp) tmpname := filepath.Join(tmp, filepath.Base(filename)) file, err := os.OpenFile(tmpname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) if err != nil { return err } defer file.Close() _, err = io.Copy(file, data) if err != nil { return err } return os.Rename(tmpname, filename) } ================================================ FILE: godocdown/kilt.go ================================================ // This file was AUTOMATICALLY GENERATED by kilt-import (smuggol) for github.com/robertkrimen/kilt package main import ( Kilt "github.com/robertkrimen/godocdown/godocdown/kilt" ) var kilt = Kilt.New() ================================================ FILE: godocdown/main.go ================================================ /* Command godocdown generates Go documentation in a GitHub-friendly Markdown format. $ go get github.com/robertkrimen/godocdown/godocdown   $ godocdown /path/to/package > README.markdown   # Generate documentation for the package/command in the current directory  $ godocdown > README.markdown   # Generate standard Markdown  $ godocdown -plain .  This program is targeted at providing nice-looking documentation for GitHub. With this in mind, it generates GitHub Flavored Markdown (http://github.github.com/github-flavored-markdown/) by default. This can be changed with the use of the "plain" flag to generate standard Markdown. Install go get github.com/robertkrimen/godocdown/godocdown Example http://github.com/robertkrimen/godocdown/blob/master/example.markdown Usage -output=""  Write output to a file instead of stdout  Write to stdout with -   -template=""  The template file to use   -no-template=false  Disable template processing   -plain=false  Emit standard Markdown, rather than Github Flavored Markdown   -heading="TitleCase1Word"  Heading detection method: 1Word, TitleCase, Title, TitleCase1Word, ""  For each line of the package declaration, godocdown attempts to detect if  a heading is present via a pattern match. If a heading is detected,  it prefixes the line with a Markdown heading indicator (typically "###").   1Word: Only a single word on the entire line  [A-Za-z0-9_-]+   TitleCase: A line where each word has the first letter capitalized  ([A-Z][A-Za-z0-9_-]\s*)+   Title: A line without punctuation (e.g. a period at the end)  ([A-Za-z0-9_-]\s*)+   TitleCase1Word: The line matches either the TitleCase or 1Word pattern  Templating In addition to Markdown rendering, godocdown provides templating via text/template (http://golang.org/pkg/text/template/) for further customization. By putting a file named ".godocdown.template" (or one from the list below) in the same directory as your package/command, godocdown will know to use the file as a template. # text/template .godocdown.markdown .godocdown.md .godocdown.template .godocdown.tmpl A template file can also be specified with the "-template" parameter Along with the standard template functionality, the starting data argument has the following interface: {{ .Emit }}  // Emit the standard documentation (what godocdown would emit without a template)   {{ .EmitHeader }}  // Emit the package name and an import line (if one is present/needed)   {{ .EmitSynopsis }}  // Emit the package declaration   {{ .EmitUsage }}  // Emit package usage, which includes a constants section, a variables section,  // a functions section, and a types section. In addition, each type may have its own constant,  // variable, and/or function/method listing.   {{ if .IsCommand }} ... {{ end }}  // A boolean indicating whether the given package is a command or a plain package   {{ .Name }}  // The name of the package/command (string)   {{ .ImportPath }}  // The import path for the package (string)  // (This field will be the empty string if godocdown is unable to guess it)  */ package main import ( "bytes" Flag "flag" "fmt" "go/build" "go/doc" "go/parser" "go/printer" "go/token" "io/ioutil" "os" "path/filepath" "regexp" "strings" Template "text/template" Time "time" ) const ( punchCardWidth = 80 debug = false ) var ( flag = Flag.NewFlagSet("", Flag.ExitOnError) flag_signature = flag.Bool("signature", false, string(0)) flag_plain = flag.Bool("plain", false, "Emit standard Markdown, rather than Github Flavored Markdown (the default)") flag_heading = flag.String("heading", "TitleCase1Word", "Heading detection method: 1Word, TitleCase, Title, TitleCase1Word, \"\"") flag_template = flag.String("template", "", "The template file to use") flag_noTemplate = flag.Bool("no-template", false, "Disable template processing") flag_output = "" _ = func() byte { flag.StringVar(&flag_output, "output", flag_output, "Write output to a file instead of stdout. Write to stdout with -") flag.StringVar(&flag_output, "o", flag_output, string(0)) return 0 }() ) var ( fset *token.FileSet synopsisHeading1Word_Regexp = regexp.MustCompile("(?m)^([A-Za-z0-9_-]+)$") synopsisHeadingTitleCase_Regexp = regexp.MustCompile("(?m)^((?:[A-Z][A-Za-z0-9_-]*)(?:[ \t]+[A-Z][A-Za-z0-9_-]*)*)$") synopsisHeadingTitle_Regexp = regexp.MustCompile("(?m)^((?:[A-Za-z0-9_-]+)(?:[ \t]+[A-Za-z0-9_-]+)*)$") synopsisHeadingTitleCase1Word_Regexp = regexp.MustCompile("(?m)^((?:[A-Za-z0-9_-]+)|(?:(?:[A-Z][A-Za-z0-9_-]*)(?:[ \t]+[A-Z][A-Za-z0-9_-]*)*))$") strip_Regexp = regexp.MustCompile("(?m)^\\s*// contains filtered or unexported fields\\s*\n") indent_Regexp = regexp.MustCompile("(?m)^([^\\n])") // Match at least one character at the start of the line synopsisHeading_Regexp = synopsisHeading1Word_Regexp match_7f = regexp.MustCompile(`(?m)[\t ]*\x7f[\t ]*$`) ) var DefaultStyle = Style{ IncludeImport: true, SynopsisHeader: "###", SynopsisHeading: synopsisHeadingTitleCase1Word_Regexp, UsageHeader: "## Usage\n", ConstantHeader: "####", VariableHeader: "####", FunctionHeader: "####", TypeHeader: "####", TypeFunctionHeader: "####", IncludeSignature: false, } var RenderStyle = DefaultStyle func usage() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) kilt.PrintDefaults(flag) executable, err := os.Stat(os.Args[0]) if err != nil { return } time := executable.ModTime() since := Time.Since(time) fmt.Fprintf(os.Stderr, "---\n%s (%.2f)\n", time.Format("2006-01-02 15:04 MST"), since.Minutes()) } func init() { flag.Usage = usage } type Style struct { IncludeImport bool SynopsisHeader string SynopsisHeading *regexp.Regexp UsageHeader string ConstantHeader string VariableHeader string FunctionHeader string TypeHeader string TypeFunctionHeader string IncludeSignature bool } type _document struct { Name string pkg *doc.Package buildPkg *build.Package IsCommand bool ImportPath string } func takeOut7f(input string) string { return match_7f.ReplaceAllString(input, "") } func _formatIndent(target, indent, preIndent string) string { var buffer bytes.Buffer toText(&buffer, target, indent, preIndent, punchCardWidth-2*len(indent)) return buffer.String() } func spacer(width int) string { return strings.Repeat(" ", width) } func formatIndent(target string) string { return _formatIndent(target, spacer(0), spacer(4)) } func indentCode(target string) string { if *flag_plain { return indent(target+"\n", spacer(4)) } return fmt.Sprintf("```go\n%s\n```", target) } func headifySynopsis(target string) string { detect := RenderStyle.SynopsisHeading if detect == nil { return target } return detect.ReplaceAllStringFunc(target, func(heading string) string { return fmt.Sprintf("%s %s", RenderStyle.SynopsisHeader, heading) }) } func headlineSynopsis(synopsis, header string, scanner *regexp.Regexp) string { return scanner.ReplaceAllStringFunc(synopsis, func(headline string) string { return fmt.Sprintf("%s %s", header, headline) }) } func sourceOfNode(target interface{}) string { var buffer bytes.Buffer mode := printer.TabIndent | printer.UseSpaces err := (&printer.Config{Mode: mode, Tabwidth: 4}).Fprint(&buffer, fset, target) if err != nil { return "" } return strip_Regexp.ReplaceAllString(buffer.String(), "") } func indent(target string, indent string) string { return indent_Regexp.ReplaceAllString(target, indent+"$1") } func filterText(input string) string { // Why is this here? // Normally, godoc will ALWAYS collapse adjacent lines separated only by whitespace. // However, if you place a (normally invisible) \x7f character in the documentation, // this collapse will not happen. Thankfully, Markdown does not need this sort of hack, // so we remove it. return takeOut7f(input) } func trimSpace(buffer *bytes.Buffer) { tmp := bytes.TrimSpace(buffer.Bytes()) buffer.Reset() buffer.Write(tmp) } func fromSlash(path string) string { return filepath.FromSlash(path) } /* This is how godoc does it: // Determine paths. // // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc, // we need to map that path somewhere in the fs name space so that routines // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target" // for this. That is, if we get passed a directory like the above, we map that // directory so that getPageInfo sees it as /target. const target = "/target" const cmdPrefix = "cmd/" path := flag.Arg(0) var forceCmd bool var abspath, relpath string if filepath.IsAbs(path) { fs.Bind(target, OS(path), "/", bindReplace) abspath = target } else if build.IsLocalImport(path) { cwd, _ := os.Getwd() // ignore errors path = filepath.Join(cwd, path) fs.Bind(target, OS(path), "/", bindReplace) abspath = target } else if strings.HasPrefix(path, cmdPrefix) { path = path[len(cmdPrefix):] forceCmd = true } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { fs.Bind(target, OS(bp.Dir), "/", bindReplace) abspath = target relpath = bp.ImportPath } else { abspath = pathpkg.Join(pkgHandler.fsRoot, path) } if relpath == "" { relpath = abspath } */ func buildImport(target string) (*build.Package, error) { if filepath.IsAbs(target) { return build.Default.ImportDir(target, build.FindOnly) } else if build.IsLocalImport(target) { base, _ := os.Getwd() path := filepath.Join(base, target) return build.Default.ImportDir(path, build.FindOnly) } else if pkg, _ := build.Default.Import(target, "", build.FindOnly); pkg.Dir != "" && pkg.ImportPath != "" { return pkg, nil } path, _ := filepath.Abs(target) // Even if there is an error, still try? return build.Default.ImportDir(path, build.FindOnly) } func guessImportPath(target string) (string, error) { buildPkg, err := buildImport(target) if err != nil { return "", err } if buildPkg.SrcRoot == "" { return "", nil } return buildPkg.ImportPath, nil } func loadDocument(target string) (*_document, error) { buildPkg, err := buildImport(target) if err != nil { return nil, err } if buildPkg.Dir == "" { return nil, fmt.Errorf("Could not find package \"%s\"", target) } path := buildPkg.Dir fset = token.NewFileSet() pkgSet, err := parser.ParseDir(fset, path, func(file os.FileInfo) bool { name := file.Name() if name[0] != '.' && strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") { return true } return false }, parser.ParseComments) if err != nil { return nil, fmt.Errorf("Could not parse \"%s\": %v", path, err) } importPath := "" if read, err := ioutil.ReadFile(filepath.Join(path, ".godocdown.import")); err == nil { importPath = strings.TrimSpace(strings.Split(string(read), "\n")[0]) } else { importPath = buildPkg.ImportPath } { isCommand := false name := "" var pkg *doc.Package // Choose the best package for documentation. Either // documentation, main, or whatever the package is. for _, parsePkg := range pkgSet { tmpPkg := doc.New(parsePkg, ".", 0) switch tmpPkg.Name { case "main": if isCommand || name != "" { // We've already seen "package documentation" // (or something else), so favor that over main. continue } fallthrough case "documentation": // We're a command, this package/file contains the documentation // path is used to get the containing directory in the case of // command documentation path, err := filepath.Abs(path) if err != nil { panic(err) } _, name = filepath.Split(path) isCommand = true pkg = tmpPkg default: // Just a regular package name = tmpPkg.Name pkg = tmpPkg } } if pkg != nil { return &_document{ Name: name, pkg: pkg, buildPkg: buildPkg, IsCommand: isCommand, ImportPath: importPath, }, nil } } return nil, nil } func emitString(fn func(*bytes.Buffer)) string { var buffer bytes.Buffer fn(&buffer) trimSpace(&buffer) return buffer.String() } // Emit func (self *_document) Emit() string { return emitString(func(buffer *bytes.Buffer) { self.EmitTo(buffer) }) } func (self *_document) EmitTo(buffer *bytes.Buffer) { // Header self.EmitHeaderTo(buffer) // Synopsis self.EmitSynopsisTo(buffer) // Usage if !self.IsCommand { self.EmitUsageTo(buffer) } trimSpace(buffer) } // Signature func (self *_document) EmitSignature() string { return emitString(func(buffer *bytes.Buffer) { self.EmitSignatureTo(buffer) }) } func (self *_document) EmitSignatureTo(buffer *bytes.Buffer) { renderSignatureTo(buffer) trimSpace(buffer) } // Header func (self *_document) EmitHeader() string { return emitString(func(buffer *bytes.Buffer) { self.EmitHeaderTo(buffer) }) } func (self *_document) EmitHeaderTo(buffer *bytes.Buffer) { renderHeaderTo(buffer, self) } // Synopsis func (self *_document) EmitSynopsis() string { return emitString(func(buffer *bytes.Buffer) { self.EmitSynopsisTo(buffer) }) } func (self *_document) EmitSynopsisTo(buffer *bytes.Buffer) { renderSynopsisTo(buffer, self) } // Usage func (self *_document) EmitUsage() string { return emitString(func(buffer *bytes.Buffer) { self.EmitUsageTo(buffer) }) } func (self *_document) EmitUsageTo(buffer *bytes.Buffer) { renderUsageTo(buffer, self) } var templateNameList = strings.Fields(` .godocdown.markdown .godocdown.md .godocdown.template .godocdown.tmpl `) func findTemplate(path string) string { for _, templateName := range templateNameList { templatePath := filepath.Join(path, templateName) _, err := os.Stat(templatePath) if err != nil { if os.IsExist(err) { continue } continue // Other error reporting? } return templatePath } return "" // Nothing found } func loadTemplate(document *_document) *Template.Template { if *flag_noTemplate { return nil } templatePath := *flag_template if templatePath == "" { templatePath = findTemplate(document.buildPkg.Dir) } if templatePath == "" { return nil } template := Template.New("").Funcs(Template.FuncMap{}) template, err := template.ParseFiles(templatePath) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing template \"%s\": %v", templatePath, err) os.Exit(1) } return template } func main() { flag.Parse(os.Args[1:]) target := flag.Arg(0) fallbackUsage := false if target == "" { fallbackUsage = true target = "." } RenderStyle.IncludeSignature = *flag_signature switch *flag_heading { case "1Word": RenderStyle.SynopsisHeading = synopsisHeading1Word_Regexp case "TitleCase": RenderStyle.SynopsisHeading = synopsisHeadingTitleCase_Regexp case "Title": RenderStyle.SynopsisHeading = synopsisHeadingTitle_Regexp case "TitleCase1Word": RenderStyle.SynopsisHeading = synopsisHeadingTitleCase1Word_Regexp case "", "-": RenderStyle.SynopsisHeading = nil } document, err := loadDocument(target) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) } if document == nil { // Nothing found. if fallbackUsage { usage() os.Exit(2) } else { fmt.Fprintf(os.Stderr, "Could not find package: %s\n", target) os.Exit(1) } } template := loadTemplate(document) var buffer bytes.Buffer if template == nil { document.EmitTo(&buffer) document.EmitSignatureTo(&buffer) } else { err := template.Templates()[0].Execute(&buffer, document) if err != nil { fmt.Fprintf(os.Stderr, "Error running template: %v", err) os.Exit(1) } document.EmitSignatureTo(&buffer) } if debug { // Skip printing if we're debugging return } documentation := buffer.String() documentation = strings.TrimSpace(documentation) if flag_output == "" || flag_output == "-" { fmt.Println(documentation) } else { file, err := os.Create(flag_output) if err != nil { } defer file.Close() _, err = fmt.Fprintln(file, documentation) } } ================================================ FILE: godocdown/main_test.go ================================================ package main import ( . "./terst" "bytes" "path/filepath" "regexp" "strings" "testing" ) func canTestImport() bool { have, err := guessImportPath("../example") Is(err, nil) return len(have) > 0 } func testImportPath(target, want string) { have, err := guessImportPath(fromSlash(target)) Is(err, nil) if have == "" { // Probably in a non-standard location, skip the test return } Is(have, want) } func TestGuessImportPath(t *testing.T) { Terst(t) testImportPath("./example", "github.com/robertkrimen/godocdown/godocdown/example") testImportPath("../example", "github.com/robertkrimen/godocdown/example") if filepath.Separator == '/' { // This test does not work well on windows testImportPath("/not/in/GOfromSlash", "") } testImportPath("in/GOfromSlash", "github.com/robertkrimen/godocdown/godocdown/in/GOfromSlash") testImportPath(".", "github.com/robertkrimen/godocdown/godocdown") testImportPath("../example/example", "github.com/robertkrimen/godocdown/example/example") } func TestFindTemplate(t *testing.T) { Terst(t) Is(findTemplate(fromSlash(".test/godocdown.template")), fromSlash(".test/godocdown.template/.godocdown.template")) Is(findTemplate(fromSlash(".test/godocdown.tmpl")), fromSlash(".test/godocdown.tmpl/.godocdown.tmpl")) Is(findTemplate(fromSlash(".test/godocdown.md")), fromSlash(".test/godocdown.md/.godocdown.md")) Is(findTemplate(fromSlash(".test/godocdown.markdown")), fromSlash(".test/godocdown.markdown/.godocdown.markdown")) } func TestIndent(t *testing.T) { Terst(t) Is(indent("1\n 2\n\n 3\n 4\n", " "), " 1\n 2\n\n 3\n 4\n") } func TestHeadlineSynopsis(t *testing.T) { Terst(t) synopsis := ` Headline The previous line is a single word. a Title Is Without punctuation In this mode, a title can be something without punctuation Also do not title something with a space at the end Only Title Casing Is Allowed Here What it says on the tin above. 1word A title with a-dash ` is := func(scanner *regexp.Regexp, want string) { have := headlineSynopsis(synopsis, "#", scanner) Is(strings.TrimSpace(have), strings.TrimSpace(want)) } is(synopsisHeading1Word_Regexp, ` # Headline The previous line is a single word. a Title Is Without punctuation In this mode, a title can be something without punctuation Also do not title something with a space at the end Only Title Casing Is Allowed Here What it says on the tin above. # 1word A title with a-dash `) is(synopsisHeadingTitleCase_Regexp, ` # Headline The previous line is a single word. a Title Is Without punctuation In this mode, a title can be something without punctuation Also do not title something with a space at the end # Only Title Casing Is Allowed Here What it says on the tin above. 1word A title with a-dash `) is(synopsisHeadingTitle_Regexp, ` # Headline The previous line is a single word. # a Title Is Without punctuation In this mode, a title can be something without punctuation Also do not title something with a space at the end # Only Title Casing Is Allowed Here What it says on the tin above. # 1word # A title with a-dash `) is(synopsisHeadingTitleCase1Word_Regexp, ` # Headline The previous line is a single word. a Title Is Without punctuation In this mode, a title can be something without punctuation Also do not title something with a space at the end # Only Title Casing Is Allowed Here What it says on the tin above. # 1word A title with a-dash `) } func Test(t *testing.T) { Terst(t) document, err := loadDocument("../example") if err != nil { Is(err.Error(), "") return } if document == nil { Is("200", "404") // Heh return } buffer := bytes.NewBuffer([]byte{}) is := func(want string) { Is(strings.TrimSpace(buffer.String()), strings.TrimSpace(want)) buffer.Reset() } renderHeaderTo(buffer, document) if canTestImport() { is("# example\n--\n import \"github.com/robertkrimen/godocdown/example\"") } else { is("# example\n--") } RenderStyle.IncludeImport = false renderHeaderTo(buffer, document) is(` # example -- `) renderSynopsisTo(buffer, document) is(` Package example is an example package with documentation // Here is some code func example() { abc := 1 + 1 }() ### Installation # This is how to install it: $ curl http://example.com $ tar xf example.tar.gz -C . $ ./example & `) RenderStyle.IncludeSignature = true renderSignatureTo(buffer) is(` -- **godocdown** http://github.com/robertkrimen/godocdown `) renderSignatureTo(buffer) Is(buffer.String(), "\n\n--\n**godocdown** http://github.com/robertkrimen/godocdown\n") } func Test_issue3(t *testing.T) { Terst(t) document, err := loadDocument(filepath.Join(".test", "issue3")) Is(err, nil) IsNot(document, nil) buffer := bytes.NewBuffer([]byte{}) document.EmitTo(buffer) Is(strings.TrimSpace(buffer.String()), strings.TrimSpace(` # issue3 -- Documentation for package issue3 Nothing happens. Some code happens. ## Usage #### func Test `+"```go\nfunc Test()\n```"+` Documentation for func Test() Something happens. Some code happens. `)) } ================================================ FILE: godocdown/render.go ================================================ package main import ( "fmt" "go/doc" "io" ) func renderConstantSectionTo(writer io.Writer, list []*doc.Value) { for _, entry := range list { fmt.Fprintf(writer, "%s\n%s\n", indentCode(sourceOfNode(entry.Decl)), formatIndent(filterText(entry.Doc))) } } func renderVariableSectionTo(writer io.Writer, list []*doc.Value) { for _, entry := range list { fmt.Fprintf(writer, "%s\n%s\n", indentCode(sourceOfNode(entry.Decl)), formatIndent(filterText(entry.Doc))) } } func renderFunctionSectionTo(writer io.Writer, list []*doc.Func, inTypeSection bool) { header := RenderStyle.FunctionHeader if inTypeSection { header = RenderStyle.TypeFunctionHeader } for _, entry := range list { receiver := " " if entry.Recv != "" { receiver = fmt.Sprintf("(%s) ", entry.Recv) } fmt.Fprintf(writer, "%s func %s%s\n\n%s\n%s\n", header, receiver, entry.Name, indentCode(sourceOfNode(entry.Decl)), formatIndent(filterText(entry.Doc))) } } func renderTypeSectionTo(writer io.Writer, list []*doc.Type) { header := RenderStyle.TypeHeader for _, entry := range list { fmt.Fprintf(writer, "%s type %s\n\n%s\n\n%s\n", header, entry.Name, indentCode(sourceOfNode(entry.Decl)), formatIndent(filterText(entry.Doc))) renderConstantSectionTo(writer, entry.Consts) renderVariableSectionTo(writer, entry.Vars) renderFunctionSectionTo(writer, entry.Funcs, true) renderFunctionSectionTo(writer, entry.Methods, true) } } func renderHeaderTo(writer io.Writer, document *_document) { fmt.Fprintf(writer, "# %s\n--\n", document.Name) if !document.IsCommand { // Import if RenderStyle.IncludeImport { if document.ImportPath != "" { fmt.Fprintf(writer, spacer(4)+"import \"%s\"\n\n", document.ImportPath) } } } } func renderSynopsisTo(writer io.Writer, document *_document) { fmt.Fprintf(writer, "%s\n", headifySynopsis(formatIndent(filterText(document.pkg.Doc)))) } func renderUsageTo(writer io.Writer, document *_document) { // Usage fmt.Fprintf(writer, "%s\n", RenderStyle.UsageHeader) // Constant Section renderConstantSectionTo(writer, document.pkg.Consts) // Variable Section renderVariableSectionTo(writer, document.pkg.Vars) // Function Section renderFunctionSectionTo(writer, document.pkg.Funcs, false) // Type Section renderTypeSectionTo(writer, document.pkg.Types) } func renderSignatureTo(writer io.Writer) { if RenderStyle.IncludeSignature { fmt.Fprintf(writer, "\n\n--\n**godocdown** http://github.com/robertkrimen/godocdown\n") } } ================================================ FILE: godocdown/terst/terst.go ================================================ // This file was AUTOMATICALLY GENERATED by terst-import from github.com/robertkrimen/terst /* Package terst is a terse (terst = test + terse), easy-to-use testing library for Go. terst is compatible with (and works via) the standard testing package: http://golang.org/pkg/testing import ( "testing" . "github.com/robertkrimen/terst" ) func Test(t *testing.T) { Terst(t) // Associate terst methods with t (the current testing.T) Is(getApple(), "apple") // Pass Is(getOrange(), "orange") // Fail: emits nice-looking diagnostic Compare(1, ">", 0) // Pass Compare(1, "==", 1.0) // Pass } func getApple() string { return "apple" } func getOrange() string { return "apple" // Intentional mistake } At the top of your testing function, call Terst(), passing the testing.T you receive as the first argument: func TestExample(t *testing.T) { Terst(t) ... } After you initialize with the given *testing.T, you can use the following to test: Is IsNot Equal Unequal IsTrue IsFalse Like Unlike Compare Each of the methods above can take an additional (optional) argument, which is a string describing the test. If the test fails, this description will be included with the test output For example: Is(2 + 2, float32(5), "This result is Doubleplusgood") --- FAIL: Test (0.00 seconds) test.go:17: This result is Doubleplusgood Failed test (Is) got: 4 (int) expected: 5 (float32) Future - Add Catch() for testing panic() - Add Same() for testing via .DeepEqual && == (without panicking?) - Add StrictCompare to use {}= scoping - Add BigCompare for easier math/big.Int testing? - Support the complex type in Compare() - Equality test for NaN? - Better syntax for At* - Need IsType/TypeIs */ package terst import ( "fmt" "math/big" "os" "reflect" "regexp" "runtime" "strings" "testing" "unsafe" ) func (self *Tester) hadResult(result bool, test *test, onFail func()) bool { if self.selfTesting { expect := true if self.failIsPassing { expect = false } if expect != result { self.Log(fmt.Sprintf("Expect %v but got %v (%v) (%v) (%v)\n", expect, result, test.kind, test.have, test.want)) onFail() self._fail() } return result } if !result { onFail() self._fail() } return result } // IsTrue is DEPRECATED by: // // Is(..., true) // func IsTrue(have bool, description ...interface{}) bool { return terstTester().IsTrue(have, description...) } // IsTrue is DEPRECATED by: // // Is(..., true) // func (self *Tester) IsTrue(have bool, description ...interface{}) bool { return self.trueOrFalse(true, have, description...) } // IsFalse is DEPRECATED by: // // Is(..., false) // func IsFalse(have bool, description ...interface{}) bool { return terstTester().IsFalse(have, description...) } // IsFalse is DEPRECATED by: // // Is(..., false) // func (self *Tester) IsFalse(have bool, description ...interface{}) bool { return self.trueOrFalse(false, have, description...) } func (self *Tester) trueOrFalse(want bool, have bool, description ...interface{}) bool { kind := "IsTrue" if want == false { kind = "IsFalse" } test := newTest(kind, have, want, description) didPass := have == want return self.hadResult(didPass, test, func() { self.Log(self.failMessageForIsTrue(test)) }) } // Fail will fail immediately, reporting a test failure with the (optional) description func Fail(description ...interface{}) bool { return terstTester().Fail(description...) } // Fail will fail immediately, reporting a test failure with the (optional) description func (self *Tester) Fail(description ...interface{}) bool { return self.fail(description...) } func (self *Tester) fail(description ...interface{}) bool { kind := "Fail" test := newTest(kind, false, false, description) didPass := false return self.hadResult(didPass, test, func() { self.Log(self.failMessageForFail(test)) }) } // FailNow will fail immediately, triggering testing.FailNow() and optionally reporting a test failure with description func FailNow(description ...interface{}) bool { return terstTester().FailNow(description...) } // FailNow will fail immediately, triggering testing.FailNow() and optionally reporting a test failure with description func (self *Tester) FailNow(description ...interface{}) bool { return self.failNow(description...) } func (self *Tester) failNow(description ...interface{}) bool { if len(description) > 0 { kind := "FailNow" test := newTest(kind, false, false, description) didPass := false self.hadResult(didPass, test, func() { self.Log(self.failMessageForFail(test)) }) } self.TestingT.FailNow() return false } // Equal tests have against want via ==: // // Equal(have, want) // Pass if have == want // // No special coercion or type inspection is done. // // If the type is incomparable (e.g. type mismatch) this will panic. func Equal(have, want interface{}, description ...interface{}) bool { return terstTester().Equal(have, want, description...) } func (self *Tester) Equal(have, want interface{}, description ...interface{}) bool { return self.equal(have, want, description...) } func (self *Tester) equal(have, want interface{}, description ...interface{}) bool { test := newTest("==", have, want, description) didPass := have == want return self.hadResult(didPass, test, func() { self.Log(self.failMessageForEqual(test)) }) } // Unequal tests have against want via !=: // // Unequal(have, want) // Pass if have != want // // No special coercion or type inspection is done. // // If the type is incomparable (e.g. type mismatch) this will panic. func Unequal(have, want interface{}, description ...interface{}) bool { return terstTester().Unequal(have, want, description...) } func (self *Tester) Unequal(have, want interface{}, description ...interface{}) bool { return self.unequal(have, want, description...) } func (self *Tester) unequal(have, want interface{}, description ...interface{}) bool { test := newTest("!=", have, want, description) didPass := have != want return self.hadResult(didPass, test, func() { self.Log(self.failMessageForIs(test)) }) } // Is tests against in different ways, depending on the // type of . // // If is a string, then it will first convert // to a string before doing the comparison: // // Is(fmt.Sprintf("%v", have), want) // Pass if have == want // // Otherwise, Is is a shortcut for: // // Compare(have, "==", want) // // If is a slice, struct, or similar, Is will perform a reflect.DeepEqual() comparison. func Is(have, want interface{}, description ...interface{}) bool { return terstTester().Is(have, want, description...) } // TODO "slice, struct, or similar" What is similar? func (self *Tester) Is(have, want interface{}, description ...interface{}) bool { return self.isOrIsNot(true, have, want, description...) } // IsNot tests against in different ways, depending on the // type of . // // If is a string, then it will first convert // to a string before doing the comparison: // // IsNot(fmt.Sprintf("%v", have), want) // Pass if have != want // // Otherwise, Is is a shortcut for: // // Compare(have, "!=", want) // // If is a slice, struct, or similar, Is will perform a reflect.DeepEqual() comparison. func IsNot(have, want interface{}, description ...interface{}) bool { return terstTester().IsNot(have, want, description...) } // TODO "slice, struct, or similar" What is similar? func (self *Tester) IsNot(have, want interface{}, description ...interface{}) bool { return self.isOrIsNot(false, have, want, description...) } func (self *Tester) isOrIsNot(wantIs bool, have, want interface{}, description ...interface{}) bool { test := newTest("Is", have, want, description) if !wantIs { test.kind = "IsNot" } didPass := false switch want.(type) { case string: didPass = stringValue(have) == want default: didPass, _ = compare(have, "{}* ==", want) } if !wantIs { didPass = !didPass } return self.hadResult(didPass, test, func() { self.Log(self.failMessageForIs(test)) }) } // Like tests against in different ways, depending on the // type of . // // If is a string, then it will first convert // to a string before doing a regular expression comparison: // // Like(fmt.Sprintf("%v", have), want) // Pass if regexp.Match(want, have) // // Otherwise, Like is a shortcut for: // // Compare(have, "{}~ ==", want) // // If is a slice, struct, or similar, Like will perform a reflect.DeepEqual() comparison. func Like(have, want interface{}, description ...interface{}) bool { return terstTester().Like(have, want, description...) } func (self *Tester) Like(have, want interface{}, description ...interface{}) bool { return self.likeOrUnlike(true, have, want, description...) } // Unlike tests against in different ways, depending on the // type of . // // If is a string, then it will first convert // to a string before doing a regular expression comparison: // // Unlike(fmt.Sprintf("%v", have), want) // Pass if !regexp.Match(want, have) // // Otherwise, Unlike is a shortcut for: // // Compare(have, "{}~ !=", want) // // If is a slice, struct, or similar, Unlike will perform a reflect.DeepEqual() comparison. func Unlike(have, want interface{}, description ...interface{}) bool { return terstTester().Unlike(have, want, description...) } func (self *Tester) Unlike(have, want interface{}, description ...interface{}) bool { return self.likeOrUnlike(false, have, want, description...) } func (self *Tester) likeOrUnlike(wantLike bool, have, want interface{}, description ...interface{}) bool { test := newTest("Like", have, want, description) if !wantLike { test.kind = "Unlike" } didPass := false switch want0 := want.(type) { case string: haveString := stringValue(have) didPass, error := regexp.Match(want0, []byte(haveString)) if !wantLike { didPass = !didPass } if error != nil { panic("regexp.Match(" + want0 + ", ...): " + error.Error()) } want = fmt.Sprintf("(?:%v)", want) // Make it look like a regular expression return self.hadResult(didPass, test, func() { self.Log(self.failMessageForMatch(test, stringValue(have), stringValue(want), wantLike)) }) } didPass, operator := compare(have, "{}~ ==", want) if !wantLike { didPass = !didPass } test.operator = operator return self.hadResult(didPass, test, func() { self.Log(self.failMessageForLike(test, stringValue(have), stringValue(want), wantLike)) }) } // Compare will compare to with the given operator. The operator can be one of the following: // // == // != // < // <= // > // >= // // Compare is not strict when comparing numeric types, // and will make a best effort to promote and to the // same type. // // Compare will promote int and uint to big.Int for testing // against each other. // // Compare will promote int, uint, and float to float64 for // float testing. // // For example: // // Compare(float32(1.0), "<", int8(2)) // A valid test // // result := float32(1.0) < int8(2) // Will not compile because of the type mismatch // func Compare(have interface{}, operator string, want interface{}, description ...interface{}) bool { return terstTester().Compare(have, operator, want, description...) } func (self *Tester) Compare(have interface{}, operator string, want interface{}, description ...interface{}) bool { return self.compare(have, operator, want, description...) } func (self *Tester) compare(left interface{}, operatorString string, right interface{}, description ...interface{}) bool { operatorString = strings.TrimSpace(operatorString) test := newTest("Compare "+operatorString, left, right, description) didPass, operator := compare(left, operatorString, right) test.operator = operator return self.hadResult(didPass, test, func() { self.Log(self.failMessageForCompare(test)) }) } type ( compareScope int ) const ( compareScopeEqual compareScope = iota compareScopeTilde compareScopeAsterisk ) type compareOperator struct { scope compareScope comparison string } var newCompareOperatorRE *regexp.Regexp = regexp.MustCompile(`^\s*(?:((?:{}|#)[*~=])\s+)?(==|!=|<|<=|>|>=)\s*$`) func newCompareOperator(operatorString string) compareOperator { if operatorString == "" { return compareOperator{compareScopeEqual, ""} } result := newCompareOperatorRE.FindStringSubmatch(operatorString) if result == nil { panic(fmt.Errorf("Unable to parse %v into a compareOperator", operatorString)) } scope := compareScopeAsterisk switch result[1] { case "#*", "{}*": scope = compareScopeAsterisk case "#~", "{}~": scope = compareScopeTilde case "#=", "{}=": scope = compareScopeEqual } comparison := result[2] return compareOperator{scope, comparison} } func compare(left interface{}, operatorString string, right interface{}) (bool, compareOperator) { pass := true operator := newCompareOperator(operatorString) comparator := newComparator(left, operator, right) // FIXME Confusing switch operator.comparison { case "==": pass = comparator.IsEqual() case "!=": pass = !comparator.IsEqual() default: if comparator.HasOrder() { switch operator.comparison { case "<": pass = comparator.Compare() == -1 case "<=": pass = comparator.Compare() <= 0 case ">": pass = comparator.Compare() == 1 case ">=": pass = comparator.Compare() >= 0 default: panic(fmt.Errorf("Compare operator (%v) is invalid", operator.comparison)) } } else { pass = false } } return pass, operator } // Compare / Comparator type compareKind int const ( kindInterface compareKind = iota kindInteger kindUnsignedInteger kindFloat kindString kindBoolean ) func comparatorValue(value interface{}) (reflect.Value, compareKind) { reflectValue := reflect.ValueOf(value) kind := kindInterface switch value.(type) { case int, int8, int16, int32, int64: kind = kindInteger case uint, uint8, uint16, uint32, uint64: kind = kindUnsignedInteger case float32, float64: kind = kindFloat case string: kind = kindString case bool: kind = kindBoolean } return reflectValue, kind } func toFloat(value reflect.Value) float64 { switch result := value.Interface().(type) { case int, int8, int16, int32, int64: return float64(value.Int()) case uint, uint8, uint16, uint32, uint64: return float64(value.Uint()) case float32, float64: return float64(value.Float()) default: panic(fmt.Errorf("toFloat( %v )", result)) } panic(0) } func toInteger(value reflect.Value) *big.Int { switch result := value.Interface().(type) { case int, int8, int16, int32, int64: return big.NewInt(value.Int()) case uint, uint8, uint16, uint32, uint64: yield := big.NewInt(0) yield.SetString(fmt.Sprintf("%v", value.Uint()), 10) return yield default: panic(fmt.Errorf("toInteger( %v )", result)) } panic(0) } func toString(value reflect.Value) string { switch result := value.Interface().(type) { case string: return result default: panic(fmt.Errorf("toString( %v )", result)) } panic(0) } func toBoolean(value reflect.Value) bool { switch result := value.Interface().(type) { case bool: return result default: panic(fmt.Errorf("toBoolean( %v )", result)) } panic(0) } type aComparator interface { Compare() int HasOrder() bool IsEqual() bool CompareScope() compareScope } type baseComparator struct { hasOrder bool operator compareOperator } func (self *baseComparator) Compare() int { panic(fmt.Errorf("Invalid .Compare()")) } func (self *baseComparator) HasOrder() bool { return self.hasOrder } func (self *baseComparator) CompareScope() compareScope { return self.operator.scope } func comparatorWithOrder(operator compareOperator) *baseComparator { return &baseComparator{true, operator} } func comparatorWithoutOrder(operator compareOperator) *baseComparator { return &baseComparator{false, operator} } type interfaceComparator struct { *baseComparator left interface{} right interface{} } func (self *interfaceComparator) IsEqual() bool { if self.CompareScope() != compareScopeEqual { return reflect.DeepEqual(self.left, self.right) } return self.left == self.right } type floatComparator struct { *baseComparator left float64 right float64 } func (self *floatComparator) Compare() int { if self.left == self.right { return 0 } else if self.left < self.right { return -1 } return 1 } func (self *floatComparator) IsEqual() bool { return self.left == self.right } type integerComparator struct { *baseComparator left *big.Int right *big.Int } func (self *integerComparator) Compare() int { return self.left.Cmp(self.right) } func (self *integerComparator) IsEqual() bool { return 0 == self.left.Cmp(self.right) } type stringComparator struct { *baseComparator left string right string } func (self *stringComparator) Compare() int { if self.left == self.right { return 0 } else if self.left < self.right { return -1 } return 1 } func (self *stringComparator) IsEqual() bool { return self.left == self.right } type booleanComparator struct { *baseComparator left bool right bool } func (self *booleanComparator) IsEqual() bool { return self.left == self.right } func newComparator(left interface{}, operator compareOperator, right interface{}) aComparator { leftValue, _ := comparatorValue(left) rightValue, rightKind := comparatorValue(right) // The simplest comparator is comparing interface{} =? interface{} targetKind := kindInterface // Are left and right of the same kind? // (reflect.Value.Kind() is different from compareKind) scopeEqual := leftValue.Kind() == rightValue.Kind() scopeTilde := false scopeAsterisk := false if scopeEqual { targetKind = rightKind // Since left and right are the same, the targetKind is Integer/Float/String/Boolean } else { // Examine the prefix of reflect.Value.Kind().String() to see if there is a similarity of // the left value to right value lk := leftValue.Kind().String() hasPrefix := func(prefix string) bool { return strings.HasPrefix(lk, prefix) } switch right.(type) { case float32, float64: // Right is float* if hasPrefix("float") { // Left is also float* targetKind = kindFloat scopeTilde = true } else if hasPrefix("int") || hasPrefix("uint") { // Left is a kind of numeric (int* or uint*) targetKind = kindFloat scopeAsterisk = true } else { // Otherwise left is a non-numeric } case uint, uint8, uint16, uint32, uint64: // Right is uint* if hasPrefix("uint") { // Left is also uint* targetKind = kindInteger scopeTilde = true } else if hasPrefix("int") { // Left is an int* (a numeric) targetKind = kindInteger scopeAsterisk = true } else if hasPrefix("float") { // Left is an float* (a numeric) targetKind = kindFloat scopeAsterisk = true } else { // Otherwise left is a non-numeric } case int, int8, int16, int32, int64: // Right is int* if hasPrefix("int") { // Left is also int* targetKind = kindInteger scopeTilde = true } else if hasPrefix("uint") { // Left is a uint* (a numeric) targetKind = kindInteger scopeAsterisk = true } else if hasPrefix("float") { // Left is an float* (a numeric) targetKind = kindFloat scopeAsterisk = true } else { // Otherwise left is a non-numeric } default: // Right is a non-numeric // Can only really compare string to string or boolean to boolean, so // we will either have a string/boolean/interfaceComparator } } /*fmt.Println("%v %v %v %v %s %s", operator.scope, same, sibling, family, leftValue, rightValue)*/ { mismatch := false switch operator.scope { case compareScopeEqual: mismatch = !scopeEqual case compareScopeTilde: mismatch = !scopeEqual && !scopeTilde case compareScopeAsterisk: mismatch = !scopeEqual && !scopeTilde && !scopeAsterisk } if mismatch { targetKind = kindInterface } } switch targetKind { case kindFloat: return &floatComparator{ comparatorWithOrder(operator), toFloat(leftValue), toFloat(rightValue), } case kindInteger: return &integerComparator{ comparatorWithOrder(operator), toInteger(leftValue), toInteger(rightValue), } case kindString: return &stringComparator{ comparatorWithOrder(operator), toString(leftValue), toString(rightValue), } case kindBoolean: return &booleanComparator{ comparatorWithoutOrder(operator), toBoolean(leftValue), toBoolean(rightValue), } } // As a last resort, we can always compare left (interface{}) to right (interface{}) return &interfaceComparator{ comparatorWithoutOrder(operator), left, right, } } // failMessage* func (self *Tester) failMessageForIsTrue(test *test) string { test.findFileLineFunction(self) return formatMessage(` %s:%d: %s Failed test (%s) got: %s expected: %s `, test.file, test.line, test.Description(), test.kind, stringValue(test.have), stringValue(test.want)) } func (self *Tester) failMessageForFail(test *test) string { test.findFileLineFunction(self) return formatMessage(` %s:%d: %s Failed test (%s) `, test.file, test.line, test.Description(), test.kind) } func typeKindString(value interface{}) string { reflectValue := reflect.ValueOf(value) kind := reflectValue.Kind().String() result := fmt.Sprintf("%T", value) if kind == result { if kind == "string" { return "" } return fmt.Sprintf(" (%T)", value) } return fmt.Sprintf(" (%T=%s)", value, kind) } func (self *Tester) failMessageForCompare(test *test) string { test.findFileLineFunction(self) return formatMessage(` %s:%d: %s Failed test (%s) %v%s %s %v%s `, test.file, test.line, test.Description(), test.kind, test.have, typeKindString(test.have), test.operator.comparison, test.want, typeKindString(test.want)) } func (self *Tester) failMessageForEqual(test *test) string { return self.failMessageForIs(test) } func (self *Tester) failMessageForIs(test *test) string { test.findFileLineFunction(self) return formatMessage(` %s:%d: %v Failed test (%s) got: %v%s expected: %v%s `, test.file, test.line, test.Description(), test.kind, test.have, typeKindString(test.have), test.want, typeKindString(test.want)) } func (self *Tester) failMessageForMatch(test *test, have, want string, wantMatch bool) string { test.findFileLineFunction(self) expect := " like" if !wantMatch { expect = "unlike" } return formatMessage(` %s:%d: %s Failed test (%s) got: %v%s %s: %s `, test.file, test.line, test.Description(), test.kind, have, typeKindString(have), expect, want) } func (self *Tester) failMessageForLike(test *test, have, want string, wantLike bool) string { test.findFileLineFunction(self) if !wantLike { want = "Anything else" } return formatMessage(` %s:%d: %s Failed test (%s) got: %v%s expected: %v%s `, test.file, test.line, test.Description(), test.kind, have, typeKindString(have), want, typeKindString(want)) } // ... type Tester struct { TestingT *testing.T sanityChecking bool selfTesting bool failIsPassing bool testEntry uintptr focusEntry uintptr } var _terstTester *Tester = nil func findTestEntry() uintptr { height := 2 for { functionPC, _, _, ok := runtime.Caller(height) function := runtime.FuncForPC(functionPC) functionName := function.Name() if !ok { return 0 } if index := strings.LastIndex(functionName, ".Test"); index >= 0 { // Assume we have an instance of TestXyzzy in a _test file return function.Entry() } height += 1 } return 0 } // Focus will focus the entry point of the test to the current method. // // This is important for test failures in getting feedback on which line was at fault. // // Consider the following scenario: // // func testingMethod( ... ) { // Is( ..., ... ) // } // // func TestExample(t *testing.T) { // Terst(t) // // testingMethod( ... ) // testingMethod( ... ) // If something in testingMethod fails, this line number will come up // testingMethod( ... ) // } // // By default, when a test fails, terst will report the outermost line that led to the failure. // Usually this is what you want, but if you need to drill down, you can by inserting a special // call at the top of your testing method: // // func testingMethod( ... ) { // Terst().Focus() // Grab the global Tester and tell it to focus on this method // Is( ..., ... ) // Now if this test fails, this line number will come up // } // func (self *Tester) Focus() { pc, _, _, ok := runtime.Caller(1) if ok { function := runtime.FuncForPC(pc) self.focusEntry = function.Entry() } } // Terst(*testing.T) // // Create a new terst Tester and return it. Associate calls to Is, Compare, Like, etc. with the newly created terst. // // Terst() // // Return the current Tester (if any). // // Terst(nil) // // Clear out the current Tester (if any). func Terst(terst ...interface{}) *Tester { if len(terst) == 0 { return terstTester() } else { if terst[0] == nil { _terstTester = nil return nil } _terstTester = newTester(terst[0].(*testing.T)) _terstTester.enableSanityChecking() _terstTester.testEntry = findTestEntry() _terstTester.focusEntry = _terstTester.testEntry } return _terstTester } func terstTester() *Tester { if _terstTester == nil { panic("_terstTester == nil") } return _terstTester.checkSanity() } func newTester(t *testing.T) *Tester { return &Tester{ TestingT: t, } } func formatMessage(message string, argumentList ...interface{}) string { message = fmt.Sprintf(message, argumentList...) message = strings.TrimLeft(message, "\n") message = strings.TrimRight(message, " \n") return message + "\n\n" } // Log is a utility method that will append the given output to the normal output stream. func (self *Tester) Log(output string) { outputValue := reflect.ValueOf(self.TestingT).Elem().FieldByName("output") output_ := outputValue.Bytes() output_ = append(output_, output...) *(*[]byte)(unsafe.Pointer(outputValue.UnsafeAddr())) = output_ } func (self *Tester) _fail() { self.TestingT.Fail() } func (self *Tester) enableSanityChecking() *Tester { self.sanityChecking = true return self } func (self *Tester) disableSanityChecking() *Tester { self.sanityChecking = false return self } func (self *Tester) enableSelfTesting() *Tester { self.selfTesting = true return self } func (self *Tester) disableSelfTesting() *Tester { self.selfTesting = false return self } func (self *Tester) failIsPass() *Tester { self.failIsPassing = true return self } func (self *Tester) passIsPass() *Tester { self.failIsPassing = false return self } func (self *Tester) checkSanity() *Tester { if self.sanityChecking && self.testEntry != 0 { foundEntryPoint := findTestEntry() if self.testEntry != foundEntryPoint { panic(fmt.Errorf("TestEntry(%v) does not match foundEntry(%v): Did you call Terst when entering a new Test* function?", self.testEntry, foundEntryPoint)) } } return self } func (self *Tester) findDepth() int { height := 1 // Skip us for { pc, _, _, ok := runtime.Caller(height) function := runtime.FuncForPC(pc) if !ok { // Got too close to the sun if false { for ; height > 0; height-- { pc, _, _, ok := runtime.Caller(height) fmt.Printf("[%d %v %v]", height, pc, ok) if ok { function := runtime.FuncForPC(pc) fmt.Printf(" => [%s]", function.Name()) } fmt.Printf("\n") } } return 1 } functionEntry := function.Entry() if functionEntry == self.focusEntry || functionEntry == self.testEntry { return height - 1 // Not the surrounding test function, but within it } height += 1 } return 1 } // test type test struct { kind string have interface{} want interface{} description []interface{} operator compareOperator file string line int functionPC uintptr function string } func newTest(kind string, have, want interface{}, description []interface{}) *test { operator := newCompareOperator("") return &test{ kind: kind, have: have, want: want, description: description, operator: operator, } } func (self *test) findFileLineFunction(tester *Tester) { self.file, self.line, self.functionPC, self.function, _ = atFileLineFunction(tester.findDepth()) } func (self *test) Description() string { description := "" if len(self.description) > 0 { description = fmt.Sprintf("%v", self.description[0]) } return description } func findPathForFile(file string) string { terstBase := os.ExpandEnv("$TERST_BASE") if len(terstBase) > 0 && strings.HasPrefix(file, terstBase) { file = file[len(terstBase):] if file[0] == '/' || file[0] == '\\' { file = file[1:] } return file } if index := strings.LastIndex(file, "/"); index >= 0 { file = file[index+1:] } else if index = strings.LastIndex(file, "\\"); index >= 0 { file = file[index+1:] } return file } func atFileLineFunction(callDepth int) (string, int, uintptr, string, bool) { pc, file, line, ok := runtime.Caller(callDepth + 1) function := runtime.FuncForPC(pc).Name() if ok { file = findPathForFile(file) if index := strings.LastIndex(function, ".Test"); index >= 0 { function = function[index+1:] } } else { pc = 0 file = "?" line = 1 } return file, line, pc, function, ok } // Conversion func integerValue(value interface{}) int64 { return reflect.ValueOf(value).Int() } func unsignedIntegerValue(value interface{}) uint64 { return reflect.ValueOf(value).Uint() } func floatValue(value interface{}) float64 { return reflect.ValueOf(value).Float() } func stringValue(value interface{}) string { return fmt.Sprintf("%v", value) } ================================================ FILE: godocdown.go ================================================ // Package godocdown is placeholder for the godocdown command // // This is a placeholder package, the actual command is: // http://github.com/robertkrimen/godocdown/godocdown // // $ go get http://github.com/robertkrimen/godocdown/godocdown // // # Generate documentation, like godoc // $ godocdown . // package godocdown import ( _ "github.com/robertkrimen/godocdown/godocdown" )