├── ui
├── .gitignore
├── fs_dev.go
├── fs.go
├── webpack.config.js
├── src
│ ├── ducks.js
│ ├── styles.css
│ ├── index.js
│ └── sidePanel.js
└── package.json
├── vendor
└── github.com
│ ├── pkg
│ └── browser
│ │ ├── browser_darwin.go
│ │ ├── browser_linux.go
│ │ ├── browser_windows.go
│ │ ├── browser_unsupported.go
│ │ ├── browser_openbsd.go
│ │ ├── example_test.go
│ │ ├── examples
│ │ └── Open
│ │ │ └── main.go
│ │ ├── README.md
│ │ ├── LICENSE
│ │ └── browser.go
│ └── elazarl
│ └── go-bindata-assetfs
│ ├── doc.go
│ ├── LICENSE
│ ├── README.md
│ ├── go-bindata-assetfs
│ └── main.go
│ └── assetfs.go
├── cmd
└── ev
│ ├── tmpl.go
│ └── ev.go
├── Gopkg.lock
├── Gopkg.toml
├── README.md
└── ev.go
/ui/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/browser_darwin.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | func openBrowser(url string) error {
4 | return runCmd("open", url)
5 | }
6 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/browser_linux.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | func openBrowser(url string) error {
4 | return runCmd("xdg-open", url)
5 | }
6 |
--------------------------------------------------------------------------------
/ui/fs_dev.go:
--------------------------------------------------------------------------------
1 | // +build dev
2 |
3 | package ui
4 |
5 | import "net/http"
6 |
7 | // FS serves the actual files from the local folder in dev mode.
8 | var FS = http.Dir("ui/dist/")
9 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/browser_windows.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | func openBrowser(url string) error {
8 | r := strings.NewReplacer("&", "^&")
9 | return runCmd("cmd", "/c", "start", r.Replace(url))
10 | }
11 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/browser_unsupported.go:
--------------------------------------------------------------------------------
1 | // +build !linux,!windows,!darwin,!openbsd
2 |
3 | package browser
4 |
5 | import (
6 | "fmt"
7 | "runtime"
8 | )
9 |
10 | func openBrowser(url string) error {
11 | return fmt.Errorf("openBrowser: unsupported operating system: %v", runtime.GOOS)
12 | }
13 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/browser_openbsd.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | import (
4 | "errors"
5 | "os/exec"
6 | )
7 |
8 | func openBrowser(url string) error {
9 | err := runCmd("xdg-open", url)
10 | if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
11 | return errors.New("xdg-open: command not found - install xdg-utils from ports(8)")
12 | }
13 | return err
14 | }
15 |
--------------------------------------------------------------------------------
/vendor/github.com/elazarl/go-bindata-assetfs/doc.go:
--------------------------------------------------------------------------------
1 | // assetfs allows packages to serve static content embedded
2 | // with the go-bindata tool with the standard net/http package.
3 | //
4 | // See https://github.com/jteeuwen/go-bindata for more information
5 | // about embedding binary data with go-bindata.
6 | //
7 | // Usage example, after running
8 | // $ go-bindata data/...
9 | // use:
10 | // http.Handle("/",
11 | // http.FileServer(
12 | // &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
13 | package assetfs
14 |
--------------------------------------------------------------------------------
/cmd/ev/tmpl.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "html/template"
6 | )
7 |
8 | func toJSON(v interface{}) template.JS {
9 | js, _ := json.Marshal(v)
10 | return template.JS(js)
11 | }
12 |
13 | const templateString = `
14 |
ev
15 |
16 | `
17 |
18 | var indexTemplate = template.Must(
19 | template.New("index").
20 | Funcs(template.FuncMap{"json": toJSON}).
21 | Parse(templateString))
22 |
--------------------------------------------------------------------------------
/ui/fs.go:
--------------------------------------------------------------------------------
1 | // +build !dev
2 |
3 | // Package ui generates binary data from the files inside the dist/ folder
4 | // to be included into the final binary, or to be served during development.
5 | package ui // import "gbbr.io/ev/ui"
6 |
7 | import assetfs "github.com/elazarl/go-bindata-assetfs"
8 |
9 | //go:generate webpack --display=errors-only
10 | //go:generate go-bindata -o bindata.go -prefix=dist/ -pkg ui dist/
11 |
12 | // FS serves the binary embedded http.Filesystem.
13 | var FS = &assetfs.AssetFS{
14 | Asset: Asset,
15 | AssetDir: AssetDir,
16 | AssetInfo: AssetInfo,
17 | Prefix: "",
18 | }
19 |
--------------------------------------------------------------------------------
/Gopkg.lock:
--------------------------------------------------------------------------------
1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
2 |
3 |
4 | [[projects]]
5 | branch = "master"
6 | name = "github.com/elazarl/go-bindata-assetfs"
7 | packages = ["."]
8 | revision = "30f82fa23fd844bd5bb1e5f216db87fd77b5eb43"
9 |
10 | [[projects]]
11 | branch = "master"
12 | name = "github.com/pkg/browser"
13 | packages = ["."]
14 | revision = "c90ca0c84f15f81c982e32665bffd8d7aac8f097"
15 |
16 | [solve-meta]
17 | analyzer-name = "dep"
18 | analyzer-version = 1
19 | inputs-digest = "4c0d3af6fd91c6beccd2157dcf458de9561d00269f6595c084b14a6fa5fdcb6e"
20 | solver-name = "gps-cdcl"
21 | solver-version = 1
22 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/example_test.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | import "strings"
4 |
5 | func ExampleOpenFile() {
6 | OpenFile("index.html")
7 | }
8 |
9 | func ExampleOpenReader() {
10 | // https://github.com/rust-lang/rust/issues/13871
11 | const quote = `There was a night when winds from unknown spaces
12 | whirled us irresistibly into limitless vacum beyond all thought and entity.
13 | Perceptions of the most maddeningly untransmissible sort thronged upon us;
14 | perceptions of infinity which at the time convulsed us with joy, yet which
15 | are now partly lost to my memory and partly incapable of presentation to others.`
16 | r := strings.NewReader(quote)
17 | OpenReader(r)
18 | }
19 |
20 | func ExampleOpenURL() {
21 | const url = "http://golang.org/"
22 | OpenURL(url)
23 | }
24 |
--------------------------------------------------------------------------------
/Gopkg.toml:
--------------------------------------------------------------------------------
1 |
2 | # Gopkg.toml example
3 | #
4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
5 | # for detailed Gopkg.toml documentation.
6 | #
7 | # required = ["github.com/user/thing/cmd/thing"]
8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
9 | #
10 | # [[constraint]]
11 | # name = "github.com/user/project"
12 | # version = "1.0.0"
13 | #
14 | # [[constraint]]
15 | # name = "github.com/user/project2"
16 | # branch = "dev"
17 | # source = "github.com/myfork/project2"
18 | #
19 | # [[override]]
20 | # name = "github.com/x/y"
21 | # version = "2.4.0"
22 |
23 |
24 | [[constraint]]
25 | branch = "master"
26 | name = "github.com/elazarl/go-bindata-assetfs"
27 |
28 | [[constraint]]
29 | branch = "master"
30 | name = "github.com/pkg/browser"
31 |
--------------------------------------------------------------------------------
/ui/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | entry: './src/index.js',
5 | devtool: 'inline-source-map',
6 |
7 | devServer: {
8 | contentBase: './dist'
9 | },
10 |
11 | resolve: {
12 | modules: ['src', 'node_modules'],
13 | },
14 |
15 | module: {
16 | rules: [
17 | {
18 | test: /\.js$/,
19 | exclude: /(node_modules)/,
20 | use: {
21 | loader: 'babel-loader',
22 | options: {
23 | presets: ['env', 'react'],
24 | plugins: ['transform-object-rest-spread']
25 | }
26 | }
27 | },
28 | {
29 | test: /\.css$/,
30 | use: [
31 | { loader: "style-loader" },
32 | { loader: "css-loader" }
33 | ]
34 | }
35 | ]
36 | },
37 |
38 | output: {
39 | filename: 'bundle.js',
40 | path: path.resolve(__dirname, 'dist'),
41 | publicPath: '/dist/'
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/examples/Open/main.go:
--------------------------------------------------------------------------------
1 | // Open is a simple example of the github.com/pkg/browser package.
2 | //
3 | // Usage:
4 | //
5 | // # Open a file in a browser window
6 | // Open $FILE
7 | //
8 | // # Open a URL in a browser window
9 | // Open $URL
10 | //
11 | // # Open the contents of stdin in a browser window
12 | // cat $SOMEFILE | Open
13 | package main
14 |
15 | import (
16 | "flag"
17 | "fmt"
18 | "log"
19 | "os"
20 |
21 | "github.com/pkg/browser"
22 | )
23 |
24 | func usage() {
25 | fmt.Fprintf(os.Stderr, "Usage:\n %s [file]\n", os.Args[0])
26 | flag.PrintDefaults()
27 | }
28 |
29 | func init() {
30 | flag.Usage = usage
31 | flag.Parse()
32 | }
33 |
34 | func check(err error) {
35 | if err != nil {
36 | log.Fatal(err)
37 | }
38 | }
39 |
40 | func main() {
41 | args := flag.Args()
42 | switch len(args) {
43 | case 0:
44 | check(browser.OpenReader(os.Stdin))
45 | case 1:
46 | check(browser.OpenFile(args[0]))
47 | default:
48 | usage()
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ui/src/ducks.js:
--------------------------------------------------------------------------------
1 | // ducks: https://github.com/erikras/ducks-modular-redux
2 |
3 | export const next = () => ({type: "EV_NEXT"});
4 | export const prev = () => ({type: "EV_PREV"});
5 | export const goto = (i) => ({type: "EV_GOTO", payload: i});
6 |
7 | export default function reducer(state = {}, action) {
8 | const {index, history} = state;
9 |
10 | switch (action.type) {
11 | case "EV_NEXT":
12 | if (index === history.length - 1) {
13 | return state;
14 | }
15 | return {
16 | ...state,
17 | index: index + 1
18 | };
19 | case "EV_PREV":
20 | if (index === 0) {
21 | return state;
22 | }
23 | return {
24 | ...state,
25 | index: index - 1
26 | };
27 | case "EV_GOTO":
28 | return {
29 | ...state,
30 | index: action.payload
31 | };
32 | }
33 | return state;
34 | }
35 |
--------------------------------------------------------------------------------
/ui/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | overflow-y: scroll;
5 | }
6 |
7 | .d2h-tag.d2h-changed-tag {
8 | display: none;
9 | }
10 |
11 | .panel {
12 | position: fixed;
13 | top: 0;
14 | right: 0;
15 | width: 700px;
16 | height: auto;
17 | max-height: 100%;
18 | overflow-y: scroll;
19 | background: white;
20 | opacity: 0.8;
21 | border: 1px solid gray;
22 | padding: 10px;
23 | }
24 |
25 | .panel .nav {
26 | margin-bottom: 10px;
27 | user-select: none;
28 | }
29 |
30 | .panel .nav a {
31 | margin-right: 5px;
32 | }
33 |
34 | .panel .nav a.pull-right {
35 | float: right;
36 | }
37 |
38 | .panel.collapsed {
39 | height: 20px;
40 | overflow: hidden;
41 | }
42 |
43 | .panel.collapsed .details {
44 | display: none;
45 | }
46 |
47 | .panel #chart {
48 | height: 150px;
49 | margin: 15px 0;
50 | }
51 |
52 | a {
53 | cursor: pointer;
54 | color: #07c;
55 | }
56 |
57 | .txt pre {
58 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
59 | font-size: 14px;
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## ev
2 | explore the evolution of a function in your browser.
3 |
4 | ### installation
5 |
6 | ```
7 | go get gbbr.io/ev/cmd/...
8 | ```
9 |
10 | ### usage
11 |
12 | ```
13 | usage: ev :
14 | ```
15 | The command will open the browser showing snapshots of how the function `funcname` from `file` evolved in time throughout various git commits. I created it to better help me understand a codebase while trying to learn more about the implementation of Go's standard library. It can be used with any programming language.
16 |
17 | Below is an example screenshot viewing the `IndexAny` function from Go's `bytes` package.
18 |
19 | 
20 |
21 | See a [demo](https://youtu.be/GqfDZX7xLUQ) of it, or try it out yourself!
22 |
23 | ---
24 |
25 | Note that `ev` uses `git log -L::` syntax, meaning that it also comes with its limitations. More specifically, if the file has multiple functions sharing the same name (ie. both method and function) it will only refer to the first occurrence starting from the top of the file.
26 |
--------------------------------------------------------------------------------
/ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "contabi",
3 | "version": "beta",
4 | "description": "Contabi UI",
5 | "main": "index.js",
6 | "author": "Gabriel Aszalos",
7 | "license": "MIT",
8 | "scripts": {
9 | "start": "webpack-dev-server --open --history-api-fallback",
10 | "build": "webpack"
11 | },
12 | "devDependencies": {
13 | "babel-core": "^6.25.0",
14 | "babel-eslint": "^7.2.3",
15 | "babel-loader": "^7.1.1",
16 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
17 | "babel-preset-env": "^1.6.0",
18 | "babel-preset-react": "^6.24.1",
19 | "css-loader": "^0.28.4",
20 | "eslint": "^4.2.0",
21 | "eslint-loader": "^1.9.0",
22 | "eslint-plugin-import": "^2.7.0",
23 | "eslint-plugin-react": "^7.1.0",
24 | "style-loader": "^0.18.2",
25 | "webpack": "^3.3.0",
26 | "webpack-dev-server": "^2.5.1"
27 | },
28 | "dependencies": {
29 | "c3": "^0.4.18",
30 | "classnames": "^2.2.5",
31 | "diff2html": "^2.3.0",
32 | "moment": "^2.18.1",
33 | "react": "^15.6.1",
34 | "react-dom": "^15.6.1",
35 | "react-redux": "^5.0.5",
36 | "redux": "^3.0.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ui/src/index.js:
--------------------------------------------------------------------------------
1 | import './styles.css';
2 | import 'diff2html/dist/diff2html.css';
3 |
4 | import React, {Component} from 'react';
5 | import {render} from 'react-dom';
6 | import reducer from './ducks';
7 | import {Provider} from 'react-redux';
8 | import {createStore} from 'redux';
9 | import {Diff2Html as diff} from 'diff2html';
10 | import SidePanel from './sidePanel';
11 | import {connect} from 'react-redux';
12 |
13 | class DiffViewComponent extends Component {
14 | render() {
15 | const __html = diff.getPrettyHtml(this.props.diff);
16 | return ;
17 | }
18 | }
19 |
20 | const DiffView = connect(({history, index}) => ({diff: history[index].Diff}))(DiffViewComponent);
21 |
22 | document.addEventListener('DOMContentLoaded', () => {
23 | const store = createStore(reducer, {index: 0, history: window.GIT_HISTORY});
24 |
25 | render(
26 |
27 |
28 |
29 |
30 |
31 | ,
32 | document.getElementById('app-root')
33 | );
34 | });
35 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/README.md:
--------------------------------------------------------------------------------
1 |
2 | # browser
3 | import "github.com/pkg/browser"
4 |
5 | Package browser provides helpers to open files, readers, and urls in a browser window.
6 |
7 | The choice of which browser is started is entirely client dependant.
8 |
9 |
10 |
11 |
12 |
13 | ## Variables
14 | ``` go
15 | var Stderr io.Writer = os.Stderr
16 | ```
17 | Stderr is the io.Writer to which executed commands write standard error.
18 |
19 | ``` go
20 | var Stdout io.Writer = os.Stdout
21 | ```
22 | Stdout is the io.Writer to which executed commands write standard output.
23 |
24 |
25 | ## func OpenFile
26 | ``` go
27 | func OpenFile(path string) error
28 | ```
29 | OpenFile opens new browser window for the file path.
30 |
31 |
32 | ## func OpenReader
33 | ``` go
34 | func OpenReader(r io.Reader) error
35 | ```
36 | OpenReader consumes the contents of r and presents the
37 | results in a new browser window.
38 |
39 |
40 | ## func OpenURL
41 | ``` go
42 | func OpenURL(url string) error
43 | ```
44 | OpenURL opens a new browser window pointing to url.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | - - -
55 | Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
56 |
--------------------------------------------------------------------------------
/cmd/ev/ev.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "os"
8 | "strings"
9 |
10 | "gbbr.io/ev"
11 | "gbbr.io/ev/ui"
12 | "github.com/pkg/browser"
13 | )
14 |
15 | var funcName, fileName string
16 | var parsedLog []*ev.Commit
17 |
18 | func init() {
19 | log.SetFlags(0)
20 | log.SetPrefix("ev: ")
21 | if len(os.Args) <= 1 {
22 | usageAndExit()
23 | }
24 | parts := strings.Split(os.Args[1], ":")
25 | if len(parts) != 2 {
26 | usageAndExit()
27 | }
28 | funcName, fileName = parts[0], parts[1]
29 | }
30 |
31 | func usageAndExit() {
32 | fmt.Println(`usage: ev :`)
33 | os.Exit(0)
34 | }
35 |
36 | func index(w http.ResponseWriter, req *http.Request) {
37 | if err := indexTemplate.Execute(w, parsedLog); err != nil {
38 | log.Fatal(err)
39 | }
40 | }
41 |
42 | func main() {
43 | var err error
44 | parsedLog, err = ev.Log(funcName, fileName)
45 | if err != nil {
46 | log.Fatal(err)
47 | }
48 | mux := http.NewServeMux()
49 | mux.Handle("/dist/", http.StripPrefix("/dist/", http.FileServer(ui.FS)))
50 | mux.HandleFunc("/", index)
51 | go func() {
52 | if err := http.ListenAndServe(":8888", mux); err != nil {
53 | log.Fatal(err)
54 | }
55 | }()
56 | browser.OpenURL("http://localhost:8888")
57 | select {}
58 | }
59 |
--------------------------------------------------------------------------------
/vendor/github.com/elazarl/go-bindata-assetfs/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Elazar Leibovich
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Dave Cheney
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 |
--------------------------------------------------------------------------------
/vendor/github.com/elazarl/go-bindata-assetfs/README.md:
--------------------------------------------------------------------------------
1 | # go-bindata-assetfs
2 |
3 | Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`.
4 |
5 | [GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs)
6 |
7 | ### Installation
8 |
9 | Install with
10 |
11 | $ go get github.com/jteeuwen/go-bindata/...
12 | $ go get github.com/elazarl/go-bindata-assetfs/...
13 |
14 | ### Creating embedded data
15 |
16 | Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage,
17 | instead of running `go-bindata` run `go-bindata-assetfs`.
18 |
19 | The tool will create a `bindata_assetfs.go` file, which contains the embedded data.
20 |
21 | A typical use case is
22 |
23 | $ go-bindata-assetfs data/...
24 |
25 | ### Using assetFS in your code
26 |
27 | The generated file provides an `assetFS()` function that returns a `http.Filesystem`
28 | wrapping the embedded files. What you usually want to do is:
29 |
30 | http.Handle("/", http.FileServer(assetFS()))
31 |
32 | This would run an HTTP server serving the embedded files.
33 |
34 | ## Without running binary tool
35 |
36 | You can always just run the `go-bindata` tool, and then
37 |
38 | use
39 |
40 | import "github.com/elazarl/go-bindata-assetfs"
41 | ...
42 | http.Handle("/",
43 | http.FileServer(
44 | &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "data"}))
45 |
46 | to serve files embedded from the `data` directory.
47 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/browser/browser.go:
--------------------------------------------------------------------------------
1 | // Package browser provides helpers to open files, readers, and urls in a browser window.
2 | //
3 | // The choice of which browser is started is entirely client dependant.
4 | package browser
5 |
6 | import (
7 | "fmt"
8 | "io"
9 | "io/ioutil"
10 | "os"
11 | "os/exec"
12 | "path/filepath"
13 | )
14 |
15 | // Stdout is the io.Writer to which executed commands write standard output.
16 | var Stdout io.Writer = os.Stdout
17 |
18 | // Stderr is the io.Writer to which executed commands write standard error.
19 | var Stderr io.Writer = os.Stderr
20 |
21 | // OpenFile opens new browser window for the file path.
22 | func OpenFile(path string) error {
23 | path, err := filepath.Abs(path)
24 | if err != nil {
25 | return err
26 | }
27 | return OpenURL("file://" + path)
28 | }
29 |
30 | // OpenReader consumes the contents of r and presents the
31 | // results in a new browser window.
32 | func OpenReader(r io.Reader) error {
33 | f, err := ioutil.TempFile("", "browser")
34 | if err != nil {
35 | return fmt.Errorf("browser: could not create temporary file: %v", err)
36 | }
37 | if _, err := io.Copy(f, r); err != nil {
38 | f.Close()
39 | return fmt.Errorf("browser: caching temporary file failed: %v", err)
40 | }
41 | if err := f.Close(); err != nil {
42 | return fmt.Errorf("browser: caching temporary file failed: %v", err)
43 | }
44 | oldname := f.Name()
45 | newname := oldname + ".html"
46 | if err := os.Rename(oldname, newname); err != nil {
47 | return fmt.Errorf("browser: renaming temporary file failed: %v", err)
48 | }
49 | return OpenFile(newname)
50 | }
51 |
52 | // OpenURL opens a new browser window pointing to url.
53 | func OpenURL(url string) error {
54 | return openBrowser(url)
55 | }
56 |
57 | func runCmd(prog string, args ...string) error {
58 | cmd := exec.Command(prog, args...)
59 | cmd.Stdout = Stdout
60 | cmd.Stderr = Stderr
61 | return cmd.Run()
62 | }
63 |
--------------------------------------------------------------------------------
/vendor/github.com/elazarl/go-bindata-assetfs/go-bindata-assetfs/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "flag"
7 | "fmt"
8 | "os"
9 | "os/exec"
10 | "strings"
11 | )
12 |
13 | const bindatafile = "bindata.go"
14 |
15 | func isDebug(args []string) bool {
16 | flagset := flag.NewFlagSet("", flag.ContinueOnError)
17 | debug := flagset.Bool("debug", false, "")
18 | debugArgs := make([]string, 0)
19 | for _, arg := range args {
20 | if strings.HasPrefix(arg, "-debug") {
21 | debugArgs = append(debugArgs, arg)
22 | }
23 | }
24 | flagset.Parse(debugArgs)
25 | if debug == nil {
26 | return false
27 | }
28 | return *debug
29 | }
30 |
31 | func main() {
32 | if _, err := exec.LookPath("go-bindata"); err != nil {
33 | fmt.Println("Cannot find go-bindata executable in path")
34 | fmt.Println("Maybe you need: go get github.com/elazarl/go-bindata-assetfs/...")
35 | os.Exit(1)
36 | }
37 | cmd := exec.Command("go-bindata", os.Args[1:]...)
38 | cmd.Stdin = os.Stdin
39 | cmd.Stdout = os.Stdout
40 | cmd.Stderr = os.Stderr
41 | if err := cmd.Run(); err != nil {
42 | os.Exit(1)
43 | }
44 | in, err := os.Open(bindatafile)
45 | if err != nil {
46 | fmt.Fprintln(os.Stderr, "Cannot read", bindatafile, err)
47 | return
48 | }
49 | out, err := os.Create("bindata_assetfs.go")
50 | if err != nil {
51 | fmt.Fprintln(os.Stderr, "Cannot write 'bindata_assetfs.go'", err)
52 | return
53 | }
54 | debug := isDebug(os.Args[1:])
55 | r := bufio.NewReader(in)
56 | done := false
57 | for line, isPrefix, err := r.ReadLine(); err == nil; line, isPrefix, err = r.ReadLine() {
58 | if !isPrefix {
59 | line = append(line, '\n')
60 | }
61 | if _, err := out.Write(line); err != nil {
62 | fmt.Fprintln(os.Stderr, "Cannot write to 'bindata_assetfs.go'", err)
63 | return
64 | }
65 | if !done && !isPrefix && bytes.HasPrefix(line, []byte("import (")) {
66 | if debug {
67 | fmt.Fprintln(out, "\t\"net/http\"")
68 | } else {
69 | fmt.Fprintln(out, "\t\"github.com/elazarl/go-bindata-assetfs\"")
70 | }
71 | done = true
72 | }
73 | }
74 | if debug {
75 | fmt.Fprintln(out, `
76 | func assetFS() http.FileSystem {
77 | for k := range _bintree.Children {
78 | return http.Dir(k)
79 | }
80 | panic("unreachable")
81 | }`)
82 | } else {
83 | fmt.Fprintln(out, `
84 | func assetFS() *assetfs.AssetFS {
85 | assetInfo := func(path string) (os.FileInfo, error) {
86 | return os.Stat(path)
87 | }
88 | for k := range _bintree.Children {
89 | return &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: assetInfo, Prefix: k}
90 | }
91 | panic("unreachable")
92 | }`)
93 | }
94 | // Close files BEFORE remove calls (don't use defer).
95 | in.Close()
96 | out.Close()
97 | if err := os.Remove(bindatafile); err != nil {
98 | fmt.Fprintln(os.Stderr, "Cannot remove", bindatafile, err)
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/ev.go:
--------------------------------------------------------------------------------
1 | package ev // import "gbbr.io/ev"
2 | import (
3 | "bufio"
4 | "bytes"
5 | "errors"
6 | "fmt"
7 | "io"
8 | "os/exec"
9 | "strconv"
10 | "strings"
11 | "time"
12 | )
13 |
14 | // Commit holds an entry inside the git log output.
15 | type Commit struct {
16 | SHA string
17 | AuthorName string
18 | AuthorEmail string
19 | AuthorDate time.Time
20 | CommitterName string
21 | CommitterEmail string
22 | CommitterDate time.Time
23 | Msg string
24 | Diff string
25 | Changes int
26 | }
27 |
28 | // logReader executes the `git log -L::` command with a custom format
29 | // and returns an io.Reader which can read from the output.
30 | func logReader(re, fn string) (io.Reader, error) {
31 | cmd := exec.Command("git", "log",
32 | fmt.Sprintf("-L^:%s:%s", re, fn),
33 | `--date=unix`,
34 | `--pretty=format:HEADER:%H,%an,%ae,%ad,%cn,%ce,%cd%n%b%nEV_BODY_END`)
35 | var stdout, stderr bytes.Buffer
36 | cmd.Stdout = &stdout
37 | cmd.Stderr = &stderr
38 | if err := cmd.Run(); err != nil {
39 | return nil, errors.New(stderr.String())
40 | }
41 | return &stdout, nil
42 | }
43 |
44 | // Log parses the result of the `git log -L::` command and returns
45 | // a slice of commits. They are ordered in descending chronological order
46 | // and show the history of the function (or regexp) `re` inside the file `fn`.
47 | func Log(re, fn string) ([]*Commit, error) {
48 | r, err := logReader(re, fn)
49 | if err != nil {
50 | return nil, err
51 | }
52 | scn := bufio.NewScanner(r)
53 | list := make([]*Commit, 0)
54 | var (
55 | c *Commit
56 | diff bytes.Buffer
57 | msg bytes.Buffer
58 | )
59 | readingDiff := false
60 | for scn.Scan() {
61 | line := scn.Text()
62 | if strings.HasPrefix(line, "HEADER:") {
63 | readingDiff = false
64 | if c != nil {
65 | c.Diff = diff.String()
66 | c.Msg = msg.String()
67 | list = append(list, c)
68 | }
69 | c = new(Commit)
70 | err := readHeader(line[7:], c)
71 | if err != nil {
72 | return nil, err
73 | }
74 | diff.Truncate(0)
75 | msg.Truncate(0)
76 | continue
77 | }
78 | if line == "EV_BODY_END" {
79 | readingDiff = true
80 | continue
81 | }
82 | if readingDiff {
83 | if len(line) >= 1 && (line[0] == '-' || line[0] == '+') {
84 | c.Changes++
85 | }
86 | diff.WriteString(line)
87 | diff.WriteString("\r\n")
88 | } else {
89 | msg.WriteString(line)
90 | msg.WriteString("\r\n")
91 | }
92 | }
93 | if err := scn.Err(); err != nil {
94 | return nil, fmt.Errorf("parse: %s", err)
95 | }
96 | return list, nil
97 | }
98 |
99 | // epoch converts s to time.Time. s is expected to hold the number of
100 | // seconds since the epoch time.
101 | func epoch(s string) time.Time {
102 | i, err := strconv.ParseInt(s, 10, 64)
103 | if err != nil {
104 | return time.Now()
105 | }
106 | return time.Unix(i, 0)
107 | }
108 |
109 | // readHeader reads a git log header into c.
110 | func readHeader(line string, c *Commit) error {
111 | p := strings.Split(line, ",")
112 | if len(p) != 7 {
113 | return fmt.Errorf("bad header: %s\n", line)
114 | }
115 | c.SHA, c.AuthorName, c.AuthorEmail, c.AuthorDate,
116 | c.CommitterName, c.CommitterEmail, c.CommitterDate =
117 | p[0], p[1], p[2], epoch(p[3]), p[4], p[5], epoch(p[6])
118 | return nil
119 | }
120 |
--------------------------------------------------------------------------------
/ui/src/sidePanel.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {next, prev, goto} from './ducks';
3 | import {connect} from 'react-redux';
4 | import moment from 'moment';
5 | import 'c3/c3.min.css';
6 | import c3 from 'c3';
7 |
8 | class SidePanel extends Component {
9 | constructor(props) {
10 | super(props);
11 |
12 | this.state = {collapsed: false};
13 | this.toggleCollapsed = this.toggleCollapsed.bind(this);
14 | this.chart = null;
15 | }
16 |
17 | toggleCollapsed() {
18 | this.setState({collapsed: !this.state.collapsed});
19 | }
20 |
21 | componentDidMount() {
22 | let {history, goto, total} = this.props;
23 | let changes = ['Lines Changed'];
24 | let dates = ['x'];
25 |
26 | history.forEach(({Changes, CommitterDate}) => {
27 | changes.push(Changes);
28 | dates.push(moment(CommitterDate).toDate());
29 | });
30 |
31 | this.chart = c3.generate({
32 | bindto: '#chart',
33 | data: {
34 | x: 'x',
35 | columns: [dates, changes],
36 | onclick: ({index}) => goto(total - index - 1),
37 | selection: {
38 | enabled: true,
39 | multiple: false
40 | }
41 | },
42 | axis: {
43 | x: {
44 | type: 'timeseries',
45 | tick: {
46 | format: '%d-%m-%Y %H:%M'
47 | }
48 | }
49 | }
50 | });
51 |
52 | this.selectIndex(0);
53 | }
54 |
55 | selectIndex(i) {
56 | this.chart.select(null, [this.props.total - i - 1], true);
57 | }
58 |
59 | componentDidUpdate(prevProps) {
60 | const {index} = this.props;
61 |
62 | if (prevProps.index !== index && this.chart !== null) {
63 | this.selectIndex(index)
64 | }
65 | }
66 |
67 | render() {
68 | const {next, prev, total, entry, index} = this.props;
69 | const {collapsed} = this.state;
70 |
71 | return (
72 |
73 |
80 |
81 |
SHA: {entry.SHA}
82 |
83 | Author: {entry.AuthorName} <{entry.AuthorEmail}>
84 | {moment(entry.AuthorDate).fromNow()}
85 |
86 |
87 | Committer: {entry.CommitterName} <{entry.CommitterEmail}>
88 | {moment(entry.CommitterDate).fromNow()}
89 |
90 |
91 |
92 |
93 |
94 | );
95 | }
96 | }
97 |
98 | export default connect(({history, index}) => ({
99 | entry: history[index],
100 | total: history.length,
101 | history,
102 | index
103 | }), {next, prev, goto})(SidePanel);
104 |
--------------------------------------------------------------------------------
/vendor/github.com/elazarl/go-bindata-assetfs/assetfs.go:
--------------------------------------------------------------------------------
1 | package assetfs
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "io"
7 | "io/ioutil"
8 | "net/http"
9 | "os"
10 | "path"
11 | "path/filepath"
12 | "strings"
13 | "time"
14 | )
15 |
16 | var (
17 | defaultFileTimestamp = time.Now()
18 | )
19 |
20 | // FakeFile implements os.FileInfo interface for a given path and size
21 | type FakeFile struct {
22 | // Path is the path of this file
23 | Path string
24 | // Dir marks of the path is a directory
25 | Dir bool
26 | // Len is the length of the fake file, zero if it is a directory
27 | Len int64
28 | // Timestamp is the ModTime of this file
29 | Timestamp time.Time
30 | }
31 |
32 | func (f *FakeFile) Name() string {
33 | _, name := filepath.Split(f.Path)
34 | return name
35 | }
36 |
37 | func (f *FakeFile) Mode() os.FileMode {
38 | mode := os.FileMode(0644)
39 | if f.Dir {
40 | return mode | os.ModeDir
41 | }
42 | return mode
43 | }
44 |
45 | func (f *FakeFile) ModTime() time.Time {
46 | return f.Timestamp
47 | }
48 |
49 | func (f *FakeFile) Size() int64 {
50 | return f.Len
51 | }
52 |
53 | func (f *FakeFile) IsDir() bool {
54 | return f.Mode().IsDir()
55 | }
56 |
57 | func (f *FakeFile) Sys() interface{} {
58 | return nil
59 | }
60 |
61 | // AssetFile implements http.File interface for a no-directory file with content
62 | type AssetFile struct {
63 | *bytes.Reader
64 | io.Closer
65 | FakeFile
66 | }
67 |
68 | func NewAssetFile(name string, content []byte, timestamp time.Time) *AssetFile {
69 | if timestamp.IsZero() {
70 | timestamp = defaultFileTimestamp
71 | }
72 | return &AssetFile{
73 | bytes.NewReader(content),
74 | ioutil.NopCloser(nil),
75 | FakeFile{name, false, int64(len(content)), timestamp}}
76 | }
77 |
78 | func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
79 | return nil, errors.New("not a directory")
80 | }
81 |
82 | func (f *AssetFile) Size() int64 {
83 | return f.FakeFile.Size()
84 | }
85 |
86 | func (f *AssetFile) Stat() (os.FileInfo, error) {
87 | return f, nil
88 | }
89 |
90 | // AssetDirectory implements http.File interface for a directory
91 | type AssetDirectory struct {
92 | AssetFile
93 | ChildrenRead int
94 | Children []os.FileInfo
95 | }
96 |
97 | func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
98 | fileinfos := make([]os.FileInfo, 0, len(children))
99 | for _, child := range children {
100 | _, err := fs.AssetDir(filepath.Join(name, child))
101 | fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0, time.Time{}})
102 | }
103 | return &AssetDirectory{
104 | AssetFile{
105 | bytes.NewReader(nil),
106 | ioutil.NopCloser(nil),
107 | FakeFile{name, true, 0, time.Time{}},
108 | },
109 | 0,
110 | fileinfos}
111 | }
112 |
113 | func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
114 | if count <= 0 {
115 | return f.Children, nil
116 | }
117 | if f.ChildrenRead+count > len(f.Children) {
118 | count = len(f.Children) - f.ChildrenRead
119 | }
120 | rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
121 | f.ChildrenRead += count
122 | return rv, nil
123 | }
124 |
125 | func (f *AssetDirectory) Stat() (os.FileInfo, error) {
126 | return f, nil
127 | }
128 |
129 | // AssetFS implements http.FileSystem, allowing
130 | // embedded files to be served from net/http package.
131 | type AssetFS struct {
132 | // Asset should return content of file in path if exists
133 | Asset func(path string) ([]byte, error)
134 | // AssetDir should return list of files in the path
135 | AssetDir func(path string) ([]string, error)
136 | // AssetInfo should return the info of file in path if exists
137 | AssetInfo func(path string) (os.FileInfo, error)
138 | // Prefix would be prepended to http requests
139 | Prefix string
140 | }
141 |
142 | func (fs *AssetFS) Open(name string) (http.File, error) {
143 | name = path.Join(fs.Prefix, name)
144 | if len(name) > 0 && name[0] == '/' {
145 | name = name[1:]
146 | }
147 | if b, err := fs.Asset(name); err == nil {
148 | timestamp := defaultFileTimestamp
149 | if fs.AssetInfo != nil {
150 | if info, err := fs.AssetInfo(name); err == nil {
151 | timestamp = info.ModTime()
152 | }
153 | }
154 | return NewAssetFile(name, b, timestamp), nil
155 | }
156 | if children, err := fs.AssetDir(name); err == nil {
157 | return NewAssetDirectory(name, children, fs), nil
158 | } else {
159 | // If the error is not found, return an error that will
160 | // result in a 404 error. Otherwise the server returns
161 | // a 500 error for files not found.
162 | if strings.Contains(err.Error(), "not found") {
163 | return nil, os.ErrNotExist
164 | }
165 | return nil, err
166 | }
167 | }
168 |
--------------------------------------------------------------------------------