diff --git a/.air.toml b/.air.toml deleted file mode 100644 index f1fec89..0000000 --- a/.air.toml +++ /dev/null @@ -1,37 +0,0 @@ -root = "." -testdata_dir = "" -tmp_dir = "tmp" - -[build] - args_bin = [] - bin = "./tmp/air-main" - cmd = "go run . -out=assets/index.html" - delay = 1 - exclude_dir = ["assets", "tmp", "vendor"] - exclude_file = [] - exclude_regex = [] - exclude_unchanged = false - follow_symlink = false - full_bin = "" - include_dir = [] - include_ext = ["go", "tpl", "tmpl", "html", "css"] - kill_delay = "0s" - log = "build-errors.log" - send_interrupt = false - stop_on_error = true - -[color] - app = "" - build = "yellow" - main = "magenta" - runner = "green" - watcher = "cyan" - -[log] - time = false - -[misc] - clean_on_exit = false - -[screen] - clear_on_rebuild = false diff --git a/README.md b/README.md index 203933e..b8240e6 100644 --- a/README.md +++ b/README.md @@ -35,13 +35,11 @@ cd .. go run . ``` -## Semi-Live reloading -Run the [example](#Example) then -Optional Uncomment refresh line in [index.html](go/cover/index.html#L4) +## Page Development -Open [index.html](assets/index.html) in your browser - -```shell -air +``` +cd go/cover/react-coverage +npm i +npm run dev ``` diff --git a/_example/cover.out b/_example/cover.out index 2c6560b..90bf287 100644 --- a/_example/cover.out +++ b/_example/cover.out @@ -2,6 +2,9 @@ mode: set github.com/skelouse/cover-pretty/_example/lib/func.go:3.31,4.7 1 1 github.com/skelouse/cover-pretty/_example/lib/func.go:4.7,6.3 1 1 github.com/skelouse/cover-pretty/_example/lib/func.go:8.2,8.13 1 0 +github.com/skelouse/cover-pretty/_example/lib/inner/func.go:3.36,4.7 1 1 +github.com/skelouse/cover-pretty/_example/lib/inner/func.go:4.7,6.3 1 1 +github.com/skelouse/cover-pretty/_example/lib/inner/func.go:8.2,8.13 1 0 github.com/skelouse/cover-pretty/_example/func.go:9.28,10.7 1 1 github.com/skelouse/cover-pretty/_example/func.go:10.7,12.3 1 1 github.com/skelouse/cover-pretty/_example/func.go:14.2,14.13 1 0 @@ -44,6 +47,3 @@ github.com/skelouse/cover-pretty/_example/util.go:17.15,21.31 4 0 github.com/skelouse/cover-pretty/_example/util.go:21.31,23.34 2 0 github.com/skelouse/cover-pretty/_example/util.go:23.34,25.5 1 0 github.com/skelouse/cover-pretty/_example/util.go:29.2,36.36 6 0 -github.com/skelouse/cover-pretty/_example/lib/inner/func.go:3.36,4.7 1 1 -github.com/skelouse/cover-pretty/_example/lib/inner/func.go:4.7,6.3 1 1 -github.com/skelouse/cover-pretty/_example/lib/inner/func.go:8.2,8.13 1 0 diff --git a/assets/example.png b/assets/example.png index 03aac26..9ef665e 100644 Binary files a/assets/example.png and b/assets/example.png differ diff --git a/go/cover/html.go b/go/cover/html.go index 10f4d64..a73032b 100644 --- a/go/cover/html.go +++ b/go/cover/html.go @@ -15,7 +15,6 @@ import ( "strings" "github.com/skelouse/cover-pretty/go/browser" - "golang.org/x/tools/cover" ) @@ -100,7 +99,7 @@ func HtmlOutput(profile, outfile string) error { tmplFile.Indent = idx * 3 _loc, ok := location[dir] - if ok == false { + if !ok { _loc = &templateFile{ Name: dir, IsDir: true, @@ -115,31 +114,66 @@ func HtmlOutput(profile, outfile string) error { location[fileSuffix] = tmplFile } - var out *os.File + var jsOut, htmlOut *os.File + var jsFilePath, htmlFilePath string + if outfile == "" { var dir string dir, err = os.MkdirTemp("", "cover") if err != nil { return err } - out, err = os.Create(filepath.Join(dir, "coverage.html")) + + jsFilePath = filepath.Join(dir, "coverage.js") + htmlFilePath = filepath.Join(dir, "index.html") + + jsOut, err = os.Create(jsFilePath) + if err != nil { + return err + } + defer jsOut.Close() + + htmlOut, err = os.Create(htmlFilePath) + if err != nil { + return err + } + defer htmlOut.Close() + } else { - out, err = os.Create(outfile) - } - if err != nil { - return err - } - err = htmlTemplate.Execute(out, d) - if err2 := out.Close(); err == nil { - err = err2 + jsOut, err = os.Create(outfile) + if err != nil { + return err + } + defer jsOut.Close() + + // If outfile is provided, generate the HTML file next to the JavaScript file + htmlFilePath = outfile[:len(outfile)-len(filepath.Ext(outfile))] + ".html" + htmlOut, err = os.Create(htmlFilePath) + if err != nil { + return err + } + defer htmlOut.Close() } + + // Execute JavaScript template + // err = jsTemplate.Execute(jsOut, d) + // if err != nil { + // return err + // } + + // Execute HTML template + err = generateHTML(htmlFilePath, d) if err != nil { return err } + // Print paths to generated files + fmt.Printf("HTML and JavaScript generated:\n- %s\n", htmlFilePath) + + // Optionally, open the HTML file in a browser if outfile == "" { - if !browser.Open("file://" + out.Name()) { - fmt.Fprintf(os.Stderr, "HTML output written to %s\n", out.Name()) + if !browser.Open("file://" + strings.Replace(htmlFilePath, "index.html", "/index.html", -1)) { + fmt.Fprintf(os.Stderr, "HTML output written to %s\n", htmlFilePath) } } @@ -197,28 +231,6 @@ func htmlGen(w io.Writer, src []byte, boundaries []cover.Boundary) error { return dst.Flush() } -// rgb returns an rgb value for the specified coverage value -// between 0 (no coverage) and 10 (max coverage). -func rgb(n int) string { - if n == 0 { - return "rgb(192, 0, 0)" // Red - } - // Gradient from gray to green. - r := 128 - 12*(n-1) - g := 128 + 12*(n-1) - b := 128 + 3*(n-1) - return fmt.Sprintf("rgb(%v, %v, %v)", r, g, b) -} - -// colors generates the CSS rules for coverage colors. -func colors() string { - var buf strings.Builder - for i := 0; i < 11; i++ { - fmt.Fprintf(&buf, ".cov%v { color: %v }\n", i, rgb(i)) - } - return buf.String() -} - type templateData struct { Header string Files map[string]*templateFile diff --git a/go/cover/index.html b/go/cover/index.html deleted file mode 100644 index bce6296..0000000 --- a/go/cover/index.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - {{$pkg := .PackageName}}{{if $pkg}}{{$pkg}}: {{end}}Go Coverage Report - - {{style}} - - - -

{{.Header}}

-
- -
- not tracked - {{if .Set}} - not covered - covered - {{else}} - no coverage - low coverage - * - * - * - * - * - * - * - * - high coverage - {{end}} -
-
- -
- {{range $i, $f := .RawFiles}} - - {{end}} -
- - - diff --git a/go/cover/react-coverage/.gitignore b/go/cover/react-coverage/.gitignore new file mode 100644 index 0000000..ee1da39 --- /dev/null +++ b/go/cover/react-coverage/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +/public diff --git a/go/cover/react-coverage/build/asset-manifest.json b/go/cover/react-coverage/build/asset-manifest.json new file mode 100644 index 0000000..7675d3f --- /dev/null +++ b/go/cover/react-coverage/build/asset-manifest.json @@ -0,0 +1,11 @@ +{ + "files": { + "main.css": "/static/css/main.b961cacc.css", + "main.js": "/static/js/main.8b5c8154.js", + "index.html": "/index.html" + }, + "entrypoints": [ + "static/css/main.b961cacc.css", + "static/js/main.8b5c8154.js" + ] +} \ No newline at end of file diff --git a/go/cover/react-coverage/build/favicon.ico b/go/cover/react-coverage/build/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/go/cover/react-coverage/build/favicon.ico differ diff --git a/go/cover/react-coverage/build/index.html b/go/cover/react-coverage/build/index.html new file mode 100644 index 0000000..2b7bedc --- /dev/null +++ b/go/cover/react-coverage/build/index.html @@ -0,0 +1 @@ +React App
\ No newline at end of file diff --git a/go/cover/react-coverage/build/logo192.png b/go/cover/react-coverage/build/logo192.png new file mode 100644 index 0000000..fc44b0a Binary files /dev/null and b/go/cover/react-coverage/build/logo192.png differ diff --git a/go/cover/react-coverage/build/logo512.png b/go/cover/react-coverage/build/logo512.png new file mode 100644 index 0000000..a4e47a6 Binary files /dev/null and b/go/cover/react-coverage/build/logo512.png differ diff --git a/go/cover/react-coverage/build/manifest.json b/go/cover/react-coverage/build/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/go/cover/react-coverage/build/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/go/cover/react-coverage/build/robots.txt b/go/cover/react-coverage/build/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/go/cover/react-coverage/build/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/go/cover/react-coverage/build/static/css/main.b961cacc.css b/go/cover/react-coverage/build/static/css/main.b961cacc.css new file mode 100644 index 0000000..53c22ec --- /dev/null +++ b/go/cover/react-coverage/build/static/css/main.b961cacc.css @@ -0,0 +1 @@ +.file-content{font-family:monospace;margin-left:50px;white-space:pre-wrap}.file-content .cov0{color:#c00}.file-content .cov8{color:#0c0}.tree-content-container{align-items:flex-start;display:flex}.tree-container{border-right:2px solid #5c5c5c;flex-grow:0;flex-shrink:0;margin-right:20px;padding-right:20px}.content-container{flex-grow:1}body{background-color:#141414;color:#5c5c5c;font-family:Arial,sans-serif} \ No newline at end of file diff --git a/go/cover/react-coverage/build/static/js/main.8b5c8154.js b/go/cover/react-coverage/build/static/js/main.8b5c8154.js new file mode 100644 index 0000000..083da95 --- /dev/null +++ b/go/cover/react-coverage/build/static/js/main.8b5c8154.js @@ -0,0 +1,2 @@ +/*! For license information please see main.8b5c8154.js.LICENSE.txt */ +!function(){var e={924:function(e,n){"use strict";n.Z={Header:"github.com/skelouse/cover-pretty/_example/",Files:{"func.go":{Idx:0,Name:"func.go",Body:'package example\n\nimport (\n "math/rand"\n "strings"\n "time"\n)\n\nfunc Function(a bool) bool {\n if a {\n return false\n }\n\n return true\n}\n\n// https://github.com/XANi/loremipsum\n// LoremIpsum is a lorem ipsum generator\ntype LoremIpsum struct {\n first bool\n words []string\n idx int\n rng *rand.Rand\n}\n\n// New returns new instance of LoremIpsum\nfunc New() *LoremIpsum {\n return NewWithSeed(time.Now().Unix())\n}\n\n// New returns new instance of LoremIpsum with PRNG seeded with the parameter\nfunc NewWithSeed(seed int64) *LoremIpsum {\n li := new(LoremIpsum)\n li.rng = rand.New(rand.NewSource(seed))\n li.first = true\n li.idx = 0\n li.shuffle()\n return li\n}\n\n// Word returns a single word of lorem ipsum\nfunc (li *LoremIpsum) Word() string {\n return li.words[li.rng.Intn(len(li.words))]\n}\n\n// WordList returns list of words of lorem ipsum\nfunc (li *LoremIpsum) WordList(count int) []string {\n return li.words[:count]\n}\n\n// Words returns words of lorem ipsum\nfunc (li *LoremIpsum) Words(count int) string {\n return strings.Join(li.WordList(count), " ")\n}\n\n// Sentence returns full sentence of lorem ipsum\nfunc (li *LoremIpsum) Sentence() string {\n l := int(li.gauss(24.46, 5.08))\n words := li.words[:l]\n return li.punctuate(words)\n}\n\n// SentenceList returns list of sentences of lorem ipsum\nfunc (li *LoremIpsum) SentenceList(count int) []string {\n var sentences []string\n sentences = make([]string, count)\n for idx := range sentences {\n sentences[idx] = li.Sentence()\n li.shuffle()\n }\n return sentences\n}\n\n// Sentences returns sentences of lorem ipsum\nfunc (li *LoremIpsum) Sentences(count int) string {\n return strings.Join(li.SentenceList(count), " ")\n}\n\n// Paragraph returns full paragraph of lorem ipsum\nfunc (li *LoremIpsum) Paragraph() string {\n return li.Sentences(int(li.gauss(5.8, 1.93)))\n}\n\n// ParagraphList returns list of paragraphs of lorem ipsum\nfunc (li *LoremIpsum) ParagraphList(count int) []string {\n var paragraphs []string\n paragraphs = make([]string, count)\n for idx := range paragraphs {\n paragraphs[idx] = li.Paragraph()\n }\n return paragraphs\n}\n\n// Paragraphs returns paragraphs of lorem ipsum\nfunc (li *LoremIpsum) Paragraphs(count int) string {\n return strings.Join(li.ParagraphList(count), "\\\\n")\n}\n',Coverage:6.666666666666667,IsDir:!1,Files:{},Indent:0},lib:{Idx:0,Name:"lib",Body:"",Coverage:0,IsDir:!0,Files:{"func.go":{Idx:1,Name:"lib/func.go",Body:'package example\n\nfunc LibFunction(a bool) bool {\n if a {\n return false\n }\n\n return true\n}\n',Coverage:66.66666666666666,IsDir:!1,Files:{},Indent:0},inner:{Idx:0,Name:"inner",Body:"",Coverage:0,IsDir:!0,Files:{"func.go":{Idx:2,Name:"lib/inner/func.go",Body:'package example\n\nfunc LibInnerFunction(a bool) bool {\n if a {\n return false\n }\n\n return true\n}\n',Coverage:66.66666666666666,IsDir:!1,Files:{},Indent:3}},Indent:3}},Indent:0},"shuffle.go":{Idx:3,Name:"shuffle.go",Body:'package example\n\nimport (\n "math/rand"\n)\n\n// int31n returns, as an int32, a non-negative pseudo-random number in [0,n).\n// n must be > 0, but int31n does not check this; the caller must ensure it.\n// int31n exists because Int31n is inefficient, but Go 1 compatibility\n// requires that the stream of values produced by math/rand remain unchanged.\n// int31n can thus only be used internally, by newly introduced APIs.\n//\n// For implementation details, see:\n// http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction\n// http://lemire.me/blog/2016/06/30/fast-random-shuffling\nfunc (li *LoremIpsum) int31n(n int32) int32 {\n v := li.rng.Uint32()\n prod := uint64(v) * uint64(n)\n low := uint32(prod)\n if low < uint32(n) {\n thresh := uint32(-n) % uint32(n)\n for low < thresh {\n v = li.rng.Uint32()\n prod = uint64(v) * uint64(n)\n low = uint32(prod)\n }\n }\n return int32(prod >> 32)\n}\n\n// Shuffle pseudo-randomizes the order of elements.\n// n is the number of elements. Shuffle panics if n < 0.\n// swap swaps the elements with indexes i and j.\nfunc (li *LoremIpsum) shuffleWords(n int, swap func(i, j int)) {\n if n < 0 {\n panic("invalid argument to Shuffle")\n }\n // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle\n // Shuffle really ought not be called with n that doesn\'t fit in 32 bits.\n // Not only will it take a very long time, but with 2\xb3\xb9! possible permutations,\n // there\'s no way that any PRNG can have a big enough internal state to\n // generate even a minuscule percentage of the possible permutations.\n // Nevertheless, the right API signature accepts an int n, so handle it as best we can.\n i := n - 1\n for ; i > 1<<31-1-1; i-- {\n j := int(rand.Int63n(int64(i + 1)))\n swap(i, j)\n }\n for ; i > 0; i-- {\n j := int(li.int31n(int32(i + 1)))\n swap(i, j)\n }\n}\n\n// Shuffle the words\nfunc (li *LoremIpsum) shuffle() {\n var words []string\n\n if !li.first {\n words = make([]string, len(LoremIpsumWords))\n copy(words, LoremIpsumWords[:])\n } else {\n words = make([]string, len(Rest))\n copy(words, Rest)\n }\n li.shuffleWords(len(words), func(i int, j int) {\n words[i], words[j] = words[j], words[i]\n })\n if li.first {\n b := make([]string, len(Beg))\n copy(b, Beg)\n // words, b = b, words\n // words = append(words, b...)\n words = append(b, words...)\n }\n li.words = words\n li.first = false\n}\n',Coverage:0,IsDir:!1,Files:{},Indent:0},"util.go":{Idx:4,Name:"util.go",Body:'package example\n\nimport (\n "math"\n "strings"\n)\n\nfunc (li *LoremIpsum) gauss(mean, stdDev float64) float64 {\n x := li.rng.Float64()\n y := li.rng.Float64()\n z := math.Sqrt(-2*math.Log(x)) * math.Cos(2*math.Pi*y)\n return z*stdDev + mean\n}\n\nfunc (li *LoremIpsum) punctuate(sentence []string) string {\n count := len(sentence)\n if count > 4 {\n mean := math.Log(float64(count)) / math.Log(6.0)\n stdDev := mean / 6\n commas := int(li.gauss(mean, stdDev))\n for i := 1; i < commas; i++ {\n idx := int(float64(i) * float64(count) / (float64(commas) + 1))\n if idx > 0 && idx < (count-1) {\n sentence[idx] = sentence[idx] + ","\n }\n }\n }\n\n first := strings.Split(sentence[0], "")\n first[0] = strings.ToUpper(first[0])\n sentence[0] = strings.Join(first, "")\n\n lastIdx := count - 1\n sentence[lastIdx] = sentence[lastIdx] + "."\n\n return strings.Join(sentence, " ")\n}\n',Coverage:0,IsDir:!1,Files:{},Indent:0}},RawFiles:[{Idx:0,Name:"func.go",Body:'package example\n\nimport (\n "math/rand"\n "strings"\n "time"\n)\n\nfunc Function(a bool) bool {\n if a {\n return false\n }\n\n return true\n}\n\n// https://github.com/XANi/loremipsum\n// LoremIpsum is a lorem ipsum generator\ntype LoremIpsum struct {\n first bool\n words []string\n idx int\n rng *rand.Rand\n}\n\n// New returns new instance of LoremIpsum\nfunc New() *LoremIpsum {\n return NewWithSeed(time.Now().Unix())\n}\n\n// New returns new instance of LoremIpsum with PRNG seeded with the parameter\nfunc NewWithSeed(seed int64) *LoremIpsum {\n li := new(LoremIpsum)\n li.rng = rand.New(rand.NewSource(seed))\n li.first = true\n li.idx = 0\n li.shuffle()\n return li\n}\n\n// Word returns a single word of lorem ipsum\nfunc (li *LoremIpsum) Word() string {\n return li.words[li.rng.Intn(len(li.words))]\n}\n\n// WordList returns list of words of lorem ipsum\nfunc (li *LoremIpsum) WordList(count int) []string {\n return li.words[:count]\n}\n\n// Words returns words of lorem ipsum\nfunc (li *LoremIpsum) Words(count int) string {\n return strings.Join(li.WordList(count), " ")\n}\n\n// Sentence returns full sentence of lorem ipsum\nfunc (li *LoremIpsum) Sentence() string {\n l := int(li.gauss(24.46, 5.08))\n words := li.words[:l]\n return li.punctuate(words)\n}\n\n// SentenceList returns list of sentences of lorem ipsum\nfunc (li *LoremIpsum) SentenceList(count int) []string {\n var sentences []string\n sentences = make([]string, count)\n for idx := range sentences {\n sentences[idx] = li.Sentence()\n li.shuffle()\n }\n return sentences\n}\n\n// Sentences returns sentences of lorem ipsum\nfunc (li *LoremIpsum) Sentences(count int) string {\n return strings.Join(li.SentenceList(count), " ")\n}\n\n// Paragraph returns full paragraph of lorem ipsum\nfunc (li *LoremIpsum) Paragraph() string {\n return li.Sentences(int(li.gauss(5.8, 1.93)))\n}\n\n// ParagraphList returns list of paragraphs of lorem ipsum\nfunc (li *LoremIpsum) ParagraphList(count int) []string {\n var paragraphs []string\n paragraphs = make([]string, count)\n for idx := range paragraphs {\n paragraphs[idx] = li.Paragraph()\n }\n return paragraphs\n}\n\n// Paragraphs returns paragraphs of lorem ipsum\nfunc (li *LoremIpsum) Paragraphs(count int) string {\n return strings.Join(li.ParagraphList(count), "\\\\n")\n}\n',Coverage:6.666666666666667,IsDir:!1,Files:{},Indent:0},{Idx:1,Name:"lib/func.go",Body:'package example\n\nfunc LibFunction(a bool) bool {\n if a {\n return false\n }\n\n return true\n}\n',Coverage:66.66666666666666,IsDir:!1,Files:{},Indent:0},{Idx:2,Name:"lib/inner/func.go",Body:'package example\n\nfunc LibInnerFunction(a bool) bool {\n if a {\n return false\n }\n\n return true\n}\n',Coverage:66.66666666666666,IsDir:!1,Files:{},Indent:3},{Idx:3,Name:"shuffle.go",Body:'package example\n\nimport (\n "math/rand"\n)\n\n// int31n returns, as an int32, a non-negative pseudo-random number in [0,n).\n// n must be > 0, but int31n does not check this; the caller must ensure it.\n// int31n exists because Int31n is inefficient, but Go 1 compatibility\n// requires that the stream of values produced by math/rand remain unchanged.\n// int31n can thus only be used internally, by newly introduced APIs.\n//\n// For implementation details, see:\n// http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction\n// http://lemire.me/blog/2016/06/30/fast-random-shuffling\nfunc (li *LoremIpsum) int31n(n int32) int32 {\n v := li.rng.Uint32()\n prod := uint64(v) * uint64(n)\n low := uint32(prod)\n if low < uint32(n) {\n thresh := uint32(-n) % uint32(n)\n for low < thresh {\n v = li.rng.Uint32()\n prod = uint64(v) * uint64(n)\n low = uint32(prod)\n }\n }\n return int32(prod >> 32)\n}\n\n// Shuffle pseudo-randomizes the order of elements.\n// n is the number of elements. Shuffle panics if n < 0.\n// swap swaps the elements with indexes i and j.\nfunc (li *LoremIpsum) shuffleWords(n int, swap func(i, j int)) {\n if n < 0 {\n panic("invalid argument to Shuffle")\n }\n // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle\n // Shuffle really ought not be called with n that doesn\'t fit in 32 bits.\n // Not only will it take a very long time, but with 2\xb3\xb9! possible permutations,\n // there\'s no way that any PRNG can have a big enough internal state to\n // generate even a minuscule percentage of the possible permutations.\n // Nevertheless, the right API signature accepts an int n, so handle it as best we can.\n i := n - 1\n for ; i > 1<<31-1-1; i-- {\n j := int(rand.Int63n(int64(i + 1)))\n swap(i, j)\n }\n for ; i > 0; i-- {\n j := int(li.int31n(int32(i + 1)))\n swap(i, j)\n }\n}\n\n// Shuffle the words\nfunc (li *LoremIpsum) shuffle() {\n var words []string\n\n if !li.first {\n words = make([]string, len(LoremIpsumWords))\n copy(words, LoremIpsumWords[:])\n } else {\n words = make([]string, len(Rest))\n copy(words, Rest)\n }\n li.shuffleWords(len(words), func(i int, j int) {\n words[i], words[j] = words[j], words[i]\n })\n if li.first {\n b := make([]string, len(Beg))\n copy(b, Beg)\n // words, b = b, words\n // words = append(words, b...)\n words = append(b, words...)\n }\n li.words = words\n li.first = false\n}\n',Coverage:0,IsDir:!1,Files:{},Indent:0},{Idx:4,Name:"util.go",Body:'package example\n\nimport (\n "math"\n "strings"\n)\n\nfunc (li *LoremIpsum) gauss(mean, stdDev float64) float64 {\n x := li.rng.Float64()\n y := li.rng.Float64()\n z := math.Sqrt(-2*math.Log(x)) * math.Cos(2*math.Pi*y)\n return z*stdDev + mean\n}\n\nfunc (li *LoremIpsum) punctuate(sentence []string) string {\n count := len(sentence)\n if count > 4 {\n mean := math.Log(float64(count)) / math.Log(6.0)\n stdDev := mean / 6\n commas := int(li.gauss(mean, stdDev))\n for i := 1; i < commas; i++ {\n idx := int(float64(i) * float64(count) / (float64(commas) + 1))\n if idx > 0 && idx < (count-1) {\n sentence[idx] = sentence[idx] + ","\n }\n }\n }\n\n first := strings.Split(sentence[0], "")\n first[0] = strings.ToUpper(first[0])\n sentence[0] = strings.Join(first, "")\n\n lastIdx := count - 1\n sentence[lastIdx] = sentence[lastIdx] + "."\n\n return strings.Join(sentence, " ")\n}\n',Coverage:0,IsDir:!1,Files:{},Indent:0}],Set:!0}},123:function(e,n){var t;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e=[],n=0;n