[
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2020 skanehira\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# github-tui\nThis is a TUI Client for GitHub.\n**Still Under Development**\n\nIf you are using Vim, you can use [gh.vim](https://github.com/skanehira/gh.vim) instead.\n\n![](https://i.gyazo.com/d7c8ca82e0aeb947f82c10b08d3eba35.png)\n\n## Features\n### Implemented\n- Issue\n  - list\n  - create\n  - close\n  - open\n  - open browser\n  - preview\n  - edit\n- Issue comment\n  - list\n  - preview\n  - delete\n  - edit\n  - add\n  - quote reply\n\n### Still Under Development\n- Issue\n  - add assignees, labels, projects, milestone\n  - remove assignees, labels, projects, milestone\n- PR\n  - list\n  - edit comment\n  - add comment\n  - delete comment\n  - diff\n  - create\n  - close\n  - change base\n  - merge\n- Github Actions\n  - re-run\n  - list\n  - log\n- File tree\n  - preview\n  - open browser\n  - preview\n- Project\n  - columns\n  - open(if type is issue, pr)\n  - add\n  - remove\n  - move\n  - open browser\n- config\n  - set default editor\n  - set user keybindings\n\n## Installation\n\n```sh\n$ git clone https://github.com/skanehira/github-tui\n$ go install ./cmd/ght\n```\n\n## Settings\nAt first, please set personal access [token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) and email in config.yaml.\n\n```yaml\ngithub:\n  token: xxxxxxxxxxxxxxxxx\n```\n\nThe config.yaml path must be in the bellow place.\n\n| OS         | place                                               |\n|------------|-----------------------------------------------------|\n| Window     | `%AppData%¥ght¥config.yaml`                         |\n| Mac        | `$HOME/Library/Application Support/ght/config.yaml` |\n| Linux/Unix | `$HOME/.config/ght/config.yaml`                     |\n\n## Usage\n\n```sh\n# current repository\n$ ght\n\n# specified repository\n$ ght owner/repo\n```\n\n### Keybindings\n\n| UI       | Keybinding           | Description                      |\n|----------|----------------------|----------------------------------|\n| Common   | `j`/`down arrow`     | Move down by one row.            |\n| Common   | `k`/`up arrow`       | Move up by one row.              |\n| Common   | `g`/`home`           | Move to the top.                 |\n| Common   | `G`/`end`            | Move to the bottom.              |\n| Common   | `Ctrl-F`/`page down` | Move down by one page.           |\n| Common   | `Ctrl-B`/`page up`   | Move up by one page.             |\n| Common   | `Ctrl-N`             | Move next UI.                    |\n| Common   | `Ctrl-P`             | Move previous UI.                |\n| Common   | `Ctrl-C`             | Finish app.                      |\n| Common   | `Ctrl-G`             | Focus to Issues                  |\n| Common   | `Ctrl-T`             | Focus to Filters                 |\n| Filters  | `Enter`              | Search with enter query.         |\n| Issues   | `h`/`left arrow`     | Move left by one column.         |\n| Issues   | `l`/`right arrow`    | Move right by one column.        |\n| Issues   | `Ctrl-J`             | Check issue and move down.       |\n| Issues   | `Ctrl-K`             | Check issue and move up.         |\n| Issues   | `e`                  | Edit and update issue body.      |\n| Issues   | `o`                  | Open checked issue.              |\n| Issues   | `c`                  | Close checked issue.             |\n| Issues   | `Ctrl-O`             | Open checked issue on browser.   |\n| Issues   | `/`                  | filter with enter words          |\n| Issues   | `n`                  | Create new issue.                |\n| Issues   | `f`                  | Fetch more issue.                |\n| Comments | `h`/`left arrow`     | Move left by one column.         |\n| Comments | `l`/`right arrow`    | Move right by one column.        |\n| Comments | `Ctrl-J`             | Check comment and move down.     |\n| Comments | `Ctrl-K`             | Check comment and move up.       |\n| Comments | `Ctrl-O`             | Open checked comment on browser. |\n| Comments | `n`                  | Add new issue comment.           |\n| Comments | `e`                  | Edit and update comment body.    |\n| Comments | `r`                  | Quote reply comment.             |\n| Comments | `/`                  | filter with enter words          |\n| Preview  | `/`                  | search with enter words          |\n| Preview  | `n`                  | move next word                   |\n| Preview  | `N`                  | move previous word               |\n| Preview  | `o`                  | change to full screen            |\n\n### Note\nWhen you creating issue, you can specify multiple labels, projects and assignees with `,`.\nFor instance, when you specify 2 labels then must input `label1,label2`.\n\n![](https://i.gyazo.com/fb665369057c5f096517a24e606e7884.png)\n\nWhen you edit issue body with `Edit Body` button then `$EDITOR` be used.\nIf `$EDITOR` is empty or not set, `vim` wll be used.\n\n## Author\nskanehira\n"
  },
  {
    "path": "cmd/ght/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/skanehira/ght/config\"\n\t\"github.com/skanehira/ght/github\"\n\t\"github.com/skanehira/ght/ui\"\n)\n\ntype Repo struct {\n\tOwner string\n\tName  string\n}\n\nfunc main() {\n\tconfig.Init()\n\tgetRepoInfo()\n\tgithub.NewClient(config.GitHub.Token)\n\tif err := ui.New().Start(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc getRepoInfo() {\n\tflag.Parse()\n\tif len(flag.Args()) > 0 {\n\t\targs := strings.Split(flag.Arg(0), \"/\")\n\t\tif len(args) < 2 {\n\t\t\tlog.Fatal(\"invalid args\")\n\t\t}\n\t\tconfig.GitHub.Owner = args[0]\n\t\tconfig.GitHub.Repo = args[1]\n\t} else {\n\t\trepo, err := getOwnerRepo()\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"invalid repo: %s\", err)\n\t\t}\n\t\tconfig.GitHub.Owner = repo.Owner\n\t\tconfig.GitHub.Repo = repo.Name\n\t}\n}\n\nfunc getOwnerRepo() (*Repo, error) {\n\tif _, err := exec.LookPath(\"git\"); err != nil {\n\t\treturn nil, err\n\t}\n\tcmd := exec.Command(\"git\", \"remote\", \"get-url\", \"origin\")\n\tout, err := cmd.CombinedOutput()\n\n\tresult := strings.TrimRight(string(out), \"\\r\\n\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn parseRemote(result)\n}\n\nfunc parseRemote(remote string) (*Repo, error) {\n\tif strings.HasSuffix(remote, \".git\") {\n\t\tremote = strings.TrimRight(remote, \".git\")\n\t}\n\tvar ownerRepo []string\n\tif strings.HasPrefix(remote, \"ssh\") {\n\t\tp := strings.Split(remote, \"/\")\n\t\tif len(p) < 1 {\n\t\t\treturn nil, fmt.Errorf(\"cannot get owner/repo from remote: %s\", remote)\n\t\t}\n\t\townerRepo = p[len(p)-2:]\n\t} else if strings.HasPrefix(remote, \"git\") {\n\t\tp := strings.Split(remote, \":\")\n\t\tif len(p) < 1 {\n\t\t\treturn nil, fmt.Errorf(\"cannot get owner/repo from remote: %s\", remote)\n\t\t}\n\t\townerRepo = strings.Split(p[1], \"/\")\n\t} else if strings.HasPrefix(remote, \"http\") || strings.HasPrefix(remote, \"https\") {\n\t\tp := strings.Split(remote, \"/\")\n\t\tif len(p) < 1 {\n\t\t\treturn nil, fmt.Errorf(\"cannot get owner/repo from remote: %s\", remote)\n\t\t}\n\t\townerRepo = p[len(p)-2:]\n\t}\n\n\trepo := Repo{\n\t\tOwner: ownerRepo[0],\n\t\tName:  ownerRepo[1],\n\t}\n\n\treturn &repo, nil\n}\n"
  },
  {
    "path": "config/config.go",
    "content": "package config\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/goccy/go-yaml\"\n)\n\ntype github struct {\n\tOwner string\n\tRepo  string\n\tToken string `yaml:\"token\"`\n}\n\ntype app struct {\n\tFile string `yaml:\"file\"`\n}\n\nconst readThisMessage = \"read this https://github.com/skanehira/github-tui?tab=readme-ov-file#settings to know more\"\n\nvar (\n\tGitHub github\n\tApp    app\n)\n\nfunc Init() {\n\tconfigDir, err := os.UserConfigDir()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlogFile := filepath.Join(configDir, \"ght\", \"debug.log\")\n\toutput, err := os.Create(logFile)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlog.SetOutput(io.MultiWriter(output, os.Stderr))\n\n\tconfigFile := filepath.Join(configDir, \"ght\", \"config.yaml\")\n\n\tb, err := os.ReadFile(configFile)\n\tif err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\tlog.Fatalf(\"Could not find configuration file, %s\", readThisMessage)\n\t}\n\n\tvar conf struct {\n\t\tGitHub github `yaml:\"github\"`\n\t}\n\n\tif err := yaml.Unmarshal(b, &conf); err != nil {\n\t\tlog.Fatalf(\"cannot deserialize config file: %s\", err)\n\t}\n\n\tif conf.GitHub.Token == \"\" {\n\t\tlog.Fatalf(\"github token is empty, %s\", readThisMessage)\n\t}\n\n\tApp.File = configFile\n\tGitHub = conf.GitHub\n}\n"
  },
  {
    "path": "domain/assignees.go",
    "content": "package domain\n\nimport \"github.com/gdamore/tcell/v2\"\n\ntype AssignableUser struct {\n\tLogin string\n}\n\nfunc (a *AssignableUser) Key() string {\n\treturn a.Login\n}\n\nfunc (a *AssignableUser) Fields() []Field {\n\treturn []Field{\n\t\t{Text: a.Login, Color: tcell.ColorFuchsia},\n\t}\n}\n"
  },
  {
    "path": "domain/comment.go",
    "content": "package domain\n\nimport \"github.com/gdamore/tcell/v2\"\n\ntype Comment struct {\n\tID        string\n\tAuthor    string\n\tUpdatedAt string\n\tURL       string\n\tBody      string\n}\n\nfunc (c *Comment) Key() string {\n\treturn c.ID\n}\n\nfunc (c *Comment) Fields() []Field {\n\tf := []Field{\n\t\t{Text: c.Author, Color: tcell.ColorYellow},\n\t\t{Text: c.UpdatedAt, Color: tcell.ColorWhite},\n\t}\n\n\treturn f\n}\n"
  },
  {
    "path": "domain/error.go",
    "content": "package domain\n\nimport \"errors\"\n\nvar (\n\tErrCommentBodyIsEmpty = errors.New(\"comment body is empty\")\n\tErrNotFoundComment    = errors.New(\"not found comment\")\n\tErrNotFoundIssue      = errors.New(\"not found issue\")\n)\n"
  },
  {
    "path": "domain/issue.go",
    "content": "package domain\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gdamore/tcell/v2\"\n)\n\ntype Issue struct {\n\tID        string\n\tRepo      string\n\tRepoOwner string\n\tNumber    string\n\tState     string\n\tTitle     string\n\tBody      string\n\tAuthor    string\n\tURL       string\n\tLabels    []Item\n\tAssignees []Item\n\tComments  []Item\n\tMileStone []Item\n\tProjects  []Item\n}\n\nfunc (i *Issue) Key() string {\n\treturn i.ID\n}\n\nfunc (i *Issue) Fields() []Field {\n\tstateColor := tcell.ColorGreen\n\tif i.State == \"CLOSED\" {\n\t\tstateColor = tcell.ColorRed\n\t}\n\n\tf := []Field{\n\t\t{Text: fmt.Sprintf(\"%s/%s\", i.RepoOwner, i.Repo), Color: tcell.ColorLightSalmon},\n\t\t{Text: i.Number, Color: tcell.ColorBlue},\n\t\t{Text: i.State, Color: stateColor},\n\t\t{Text: i.Author, Color: tcell.ColorYellow},\n\t\t{Text: i.Title, Color: tcell.ColorWhite},\n\t}\n\n\treturn f\n}\n"
  },
  {
    "path": "domain/item.go",
    "content": "package domain\n\nimport \"github.com/gdamore/tcell/v2\"\n\ntype Item interface {\n\tKey() string\n\tFields() []Field\n}\n\ntype Field struct {\n\tText  string\n\tColor tcell.Color\n}\n"
  },
  {
    "path": "domain/label.go",
    "content": "package domain\n\nimport \"github.com/gdamore/tcell/v2\"\n\ntype Label struct {\n\tName        string\n\tDescription string\n}\n\nfunc (l *Label) Key() string {\n\treturn l.Name\n}\n\nfunc (l *Label) Fields() []Field {\n\treturn []Field{\n\t\t{Text: l.Name, Color: tcell.ColorLightYellow},\n\t}\n}\n"
  },
  {
    "path": "domain/milestone.go",
    "content": "package domain\n\nimport \"github.com/gdamore/tcell/v2\"\n\ntype Milestone struct {\n\tID          string\n\tTitle       string\n\tState       string\n\tDescription string\n\tURL         string\n}\n\nfunc (m *Milestone) Key() string {\n\treturn m.Title\n}\n\nfunc (m *Milestone) Fields() []Field {\n\treturn []Field{\n\t\t{Text: m.Title, Color: tcell.ColorGreen},\n\t}\n}\n"
  },
  {
    "path": "domain/project.go",
    "content": "package domain\n\nimport \"github.com/gdamore/tcell/v2\"\n\ntype Project struct {\n\tName string\n\tURL  string\n}\n\nfunc (p *Project) Key() string {\n\treturn p.Name\n}\n\nfunc (p *Project) Fields() []Field {\n\treturn []Field{\n\t\t{Text: p.Name, Color: tcell.ColorLightSalmon},\n\t}\n}\n"
  },
  {
    "path": "github/client.go",
    "content": "package github\n\nimport (\n\t\"context\"\n\n\t\"github.com/shurcooL/githubv4\"\n\t\"golang.org/x/oauth2\"\n)\n\nvar client *githubv4.Client\n\nfunc NewClient(token string) {\n\tsrc := oauth2.StaticTokenSource(\n\t\t&oauth2.Token{AccessToken: token},\n\t)\n\thttpClient := oauth2.NewClient(context.Background(), src)\n\tclient = githubv4.NewClient(httpClient)\n}\n\nfunc CreateIssue(input githubv4.CreateIssueInput) error {\n\tvar m MutateCreateIssue\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n\nfunc GetRepos(variables map[string]interface{}) (*Repositories, error) {\n\tvar q struct {\n\t\tRepositoryOwner struct {\n\t\t\tRepositories `graphql:\"repositories(first: $first, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC})\"`\n\t\t} `graphql:\"repositoryOwner(login: $login)\"`\n\t}\n\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &q.RepositoryOwner.Repositories, nil\n}\n\nfunc GetRepo(variables map[string]interface{}) (*Repository, error) {\n\tvar q struct {\n\t\tRepository `graphql:\"repository(owner: $owner, name: $name)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &q.Repository, nil\n}\n\nfunc GetIssues(variables map[string]interface{}) (*Issues, error) {\n\tvar q struct {\n\t\tSearch Issues `graphql:\"search(query: $query, type: ISSUE, first: $first, after: $cursor)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\n\tissues := &Issues{\n\t\tNodes:    q.Search.Nodes,\n\t\tPageInfo: q.Search.PageInfo,\n\t}\n\treturn issues, nil\n}\n\nfunc GetIssue(variables map[string]interface{}) (*Issue, error) {\n\tvar q struct {\n\t\tRepository struct {\n\t\t\tIssue *Issue `graphql:\"issue(number: $number)\"`\n\t\t} `graphql:\"repository(owner: $owner, name: $name)\"`\n\t}\n\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.Repository.Issue, nil\n}\n\nfunc GetIssueTemplates(variables map[string]interface{}) ([]IssueTemplate, error) {\n\tvar q struct {\n\t\tRepository struct {\n\t\t\tIssueTemplates []IssueTemplate\n\t\t} `graphql:\"repository(name: $name, owner: $owner)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.Repository.IssueTemplates, nil\n}\n\nfunc ReopenIssue(id string) error {\n\tinput := githubv4.ReopenIssueInput{\n\t\tIssueID: githubv4.String(id),\n\t}\n\n\tvar m MutateOpenIsseue\n\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n\nfunc CloseIssue(id string) error {\n\tinput := githubv4.CloseIssueInput{\n\t\tIssueID: githubv4.String(id),\n\t}\n\n\tvar m MutateCoseIssue\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n\nfunc GetRepoLabels(variables map[string]interface{}) (*Labels, error) {\n\tvar q struct {\n\t\tRepository struct {\n\t\t\tLabels `graphql:\"labels(first: $first, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC})\"`\n\t\t} `graphql:\"repository(name: $name, owner: $owner)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &q.Repository.Labels, nil\n}\n\nfunc GetRepoMillestones(variables map[string]interface{}) (*Milestones, error) {\n\tvar q struct {\n\t\tRepository struct {\n\t\t\tMilestones `graphql:\"milestones(first: $first, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC})\"`\n\t\t} `graphql:\"repository(name: $name, owner: $owner)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &q.Repository.Milestones, nil\n}\n\nfunc GetRepoProjects(variables map[string]interface{}) (*Projects, error) {\n\tvar q struct {\n\t\tRepository struct {\n\t\t\tProjects `graphql:\"projects(first: $first, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC})\"`\n\t\t} `graphql:\"repository(name: $name, owner: $owner)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &q.Repository.Projects, nil\n}\n\nfunc GetRepoAssignableUsers(variables map[string]interface{}) (*AssignableUsers, error) {\n\tvar q struct {\n\t\tRepository struct {\n\t\t\tAssignableUsers `graphql:\"assignableUsers(first: $first, after: $cursor)\"`\n\t\t} `graphql:\"repository(name: $name, owner: $owner)\"`\n\t}\n\tif err := client.Query(context.Background(), &q, variables); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &q.Repository.AssignableUsers, nil\n}\n\nfunc DeleteIssueComment(id string) error {\n\tvar m MutateDeleteComment\n\tinput := githubv4.DeleteIssueCommentInput{\n\t\tID: githubv4.ID(id),\n\t}\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n\nfunc UpdateIssue(input githubv4.UpdateIssueInput) error {\n\tvar m MutateUpdateIssue\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n\nfunc UpdateIssueComment(input githubv4.UpdateIssueCommentInput) error {\n\tvar m MutateUpdateIssueComment\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n\nfunc AddIssueComment(input githubv4.AddCommentInput) error {\n\tvar m MutateAddIssueComment\n\treturn client.Mutate(context.Background(), &m, input, nil)\n}\n"
  },
  {
    "path": "github/mutation_comment.go",
    "content": "package github\n\nimport \"github.com/shurcooL/githubv4\"\n\ntype MutateDeleteComment struct {\n\tDeleteIssueComment struct {\n\t\tClientMutationId githubv4.String\n\t} `graphql:\"deleteIssueComment(input: $input)\"`\n}\n\ntype MutateUpdateIssueComment struct {\n\tUpdateIssueComment struct {\n\t\tClientMutationId githubv4.String\n\t} `graphql:\"updateIssueComment(input: $input)\"`\n}\n"
  },
  {
    "path": "github/mutation_issue.go",
    "content": "package github\n\nimport \"github.com/shurcooL/githubv4\"\n\ntype MutateOpenIsseue struct {\n\tReopenIssue struct {\n\t\tIssue struct {\n\t\t\tID githubv4.String\n\t\t}\n\t} `graphql:\"reopenIssue(input: $input)\"`\n}\n\ntype MutateCoseIssue struct {\n\tCloseIssue struct {\n\t\tIssue struct {\n\t\t\tID githubv4.String\n\t\t}\n\t} `graphql:\"closeIssue(input: $input)\"`\n}\n\ntype MutateCreateIssue struct {\n\tCreateIssue struct {\n\t\tIssue struct {\n\t\t\tID githubv4.String\n\t\t}\n\t} `graphql:\"createIssue(input: $input)\"`\n}\n\ntype MutateUpdateIssue struct {\n\tUpdateIssue struct {\n\t\tIssue struct {\n\t\t\tID githubv4.ID\n\t\t}\n\t} `graphql:\"updateIssue(input: $input)\"`\n}\n\ntype MutateAddIssueComment struct {\n\tAddIssueComment struct {\n\t\tClientMutationID githubv4.String\n\t} `graphql:\"addComment(input: $input)\"`\n}\n"
  },
  {
    "path": "github/query.go",
    "content": "package github\n\nimport \"github.com/shurcooL/githubv4\"\n\ntype PageInfo struct {\n\tEndCursor   githubv4.String\n\tHasNextPage githubv4.Boolean\n}\n"
  },
  {
    "path": "github/query_assignees.go",
    "content": "package github\n\nimport (\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n)\n\ntype AssignableUser struct {\n\tLogin githubv4.String\n}\n\nfunc (a *AssignableUser) ToDomain() *domain.AssignableUser {\n\tassignableUser := &domain.AssignableUser{\n\t\tLogin: string(a.Login),\n\t}\n\treturn assignableUser\n}\n\ntype AssignableUsers struct {\n\tNodes []struct {\n\t\tID    githubv4.ID\n\t\tLogin githubv4.String\n\t}\n\tPageInfo PageInfo\n}\n"
  },
  {
    "path": "github/query_comment.go",
    "content": "package github\n\nimport (\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n)\n\ntype Comment struct {\n\tID     githubv4.String\n\tAuthor struct {\n\t\tLogin githubv4.String\n\t}\n\tUpdatedAt githubv4.DateTime\n\tBody      githubv4.String\n\tURL       githubv4.URI\n}\n\nfunc (c *Comment) ToDomain() *domain.Comment {\n\tcomment := &domain.Comment{\n\t\tID:        string(c.ID),\n\t\tAuthor:    string(c.Author.Login),\n\t\tUpdatedAt: c.UpdatedAt.Local().Format(\"2006/01/02 15:04:05\"),\n\t\tURL:       c.URL.String(),\n\t\tBody:      string(c.Body),\n\t}\n\treturn comment\n}\n"
  },
  {
    "path": "github/query_issue.go",
    "content": "package github\n\nimport (\n\t\"reflect\"\n\t\"strconv\"\n\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n)\n\ntype Issue struct {\n\tID         githubv4.String\n\tRepository struct {\n\t\tID    githubv4.String\n\t\tOwner struct {\n\t\t\tLogin githubv4.String\n\t\t}\n\t\tName githubv4.String\n\t}\n\tNumber githubv4.Int\n\tBody   githubv4.String\n\tState  githubv4.String\n\tAuthor struct {\n\t\tLogin githubv4.String\n\t}\n\tTitle     githubv4.String\n\tURL       githubv4.URI\n\tLabels    Labels `graphql:\"labels(first: 10)\"`\n\tAssignees struct {\n\t\tNodes []AssignableUser\n\t} `graphql:\"assignees(first: 10)\"`\n\tProjectCards struct {\n\t\tNodes []struct {\n\t\t\tProject Project\n\t\t}\n\t} `graphql:\"projectCards(first: 10)\"`\n\tMilestone Milestone\n\tComments  struct {\n\t\tNodes []Comment\n\t} `graphql:\"comments(first: 100)\"`\n}\n\nfunc (i *Issue) ToDomain() *domain.Issue {\n\tissue := &domain.Issue{\n\t\tID:        string(i.ID),\n\t\tRepo:      string(i.Repository.Name),\n\t\tRepoOwner: string(i.Repository.Owner.Login),\n\t\tNumber:    strconv.Itoa(int(i.Number)),\n\t\tState:     string(i.State),\n\t\tAuthor:    string(i.Author.Login),\n\t\tURL:       i.URL.String(),\n\t\tTitle:     string(i.Title),\n\t\tBody:      string(i.Body),\n\t}\n\n\tlabels := make([]domain.Item, len(i.Labels.Nodes))\n\tfor i, label := range i.Labels.Nodes {\n\t\tlabels[i] = label.ToDomain()\n\t}\n\tissue.Labels = labels\n\n\tassignees := make([]domain.Item, len(i.Assignees.Nodes))\n\tfor i, a := range i.Assignees.Nodes {\n\t\tassignees[i] = a.ToDomain()\n\t}\n\tissue.Assignees = assignees\n\n\tcomments := make([]domain.Item, len(i.Comments.Nodes))\n\tfor i, comment := range i.Comments.Nodes {\n\t\tcomments[i] = comment.ToDomain()\n\t}\n\tissue.Comments = comments\n\n\tif !reflect.ValueOf(i.Milestone).IsZero() {\n\t\tissue.MileStone = append(issue.MileStone, i.Milestone.ToDomain())\n\t}\n\n\tprojects := make([]domain.Item, len(i.ProjectCards.Nodes))\n\tfor i, card := range i.ProjectCards.Nodes {\n\t\tprojects[i] = card.Project.ToDomain()\n\t}\n\tissue.Projects = projects\n\treturn issue\n}\n\ntype Issues struct {\n\tNodes []struct {\n\t\tIssue Issue `graphql:\"... on Issue\"`\n\t}\n\tPageInfo PageInfo\n}\n\ntype IssueTemplate struct {\n\tAbout githubv4.String\n\tBody  githubv4.String\n\tName  githubv4.String\n\tTitle githubv4.String\n}\n"
  },
  {
    "path": "github/query_label.go",
    "content": "package github\n\nimport (\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n)\n\ntype Label struct {\n\tID          githubv4.ID\n\tName        githubv4.String\n\tDescription githubv4.String\n\tColor       githubv4.String\n}\n\nfunc (l *Label) ToDomain() *domain.Label {\n\tlabel := &domain.Label{\n\t\tName:        string(l.Name),\n\t\tDescription: string(l.Description),\n\t}\n\treturn label\n}\n\ntype Labels struct {\n\tNodes    []Label\n\tPageInfo PageInfo\n}\n"
  },
  {
    "path": "github/query_milestone.go",
    "content": "package github\n\nimport (\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n)\n\ntype Milestone struct {\n\tID          githubv4.ID\n\tTitle       githubv4.String\n\tState       githubv4.String\n\tDescription githubv4.String\n\tURL         githubv4.URI\n}\n\nfunc (m *Milestone) ToDomain() *domain.Milestone {\n\tmilestone := &domain.Milestone{\n\t\tID:          m.ID.(string),\n\t\tTitle:       string(m.Title),\n\t\tState:       string(m.State),\n\t\tDescription: string(m.Description),\n\t\tURL:         m.URL.String(),\n\t}\n\treturn milestone\n}\n\ntype Milestones struct {\n\tNodes    []Milestone\n\tPageInfo PageInfo\n}\n"
  },
  {
    "path": "github/query_project.go",
    "content": "package github\n\nimport (\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n)\n\ntype Project struct {\n\tID   githubv4.ID\n\tName githubv4.String\n\tURL  githubv4.URI\n}\n\nfunc (p *Project) ToDomain() *domain.Project {\n\tproject := &domain.Project{\n\t\tName: string(p.Name),\n\t\tURL:  p.URL.String(),\n\t}\n\treturn project\n}\n\ntype Projects struct {\n\tNodes    []Project\n\tPageInfo PageInfo\n}\n"
  },
  {
    "path": "github/query_repository.go",
    "content": "package github\n\nimport \"github.com/shurcooL/githubv4\"\n\ntype Repository struct {\n\tID               githubv4.ID\n\tName             githubv4.String\n\tNameWithOwner    githubv4.String\n\tCreatedAt        githubv4.DateTime\n\tDefaultBranchRef struct {\n\t\tName githubv4.String\n\t}\n\tDescription githubv4.String\n\tLicenseInfo struct {\n\t\tName githubv4.String\n\t}\n\tStargazerCount githubv4.Int\n\tURL            githubv4.URI\n\tSSHURL         githubv4.String\n}\n\ntype Repositories struct {\n\tNodes    []Repository\n\tPageInfo PageInfo\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/skanehira/ght\n\ngo 1.15\n\nrequire (\n\tgithub.com/atotto/clipboard v0.1.2\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/gdamore/tcell/v2 v2.2.0\n\tgithub.com/goccy/go-yaml v1.8.3\n\tgithub.com/lucasb-eyer/go-colorful v1.2.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.6 // indirect\n\tgithub.com/rivo/tview v0.0.0-20210312174852-ae9464cc3598\n\tgithub.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b\n\tgithub.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect\n\tgolang.org/x/net v0.0.0-20201026091529-146b70c837a4 // indirect\n\tgolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43\n\tgolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208\n\tgolang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=\ngithub.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=\ngithub.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=\ngithub.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=\ngithub.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=\ngithub.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=\ngithub.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDro=\ngithub.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=\ngithub.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U4=\ngithub.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/goccy/go-yaml v1.8.3 h1:VGzw2KWSUyQX0yXai02S0nttBc+Oa4Kvh6RCFoxt8SE=\ngithub.com/goccy/go-yaml v1.8.3/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=\ngithub.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=\ngithub.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=\ngithub.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rivo/tview v0.0.0-20210217110421-8a8f78a6dd01 h1:rtCzDXdaqhiRakJsz0bUj+3sOUjw82bJDcJrAzQ0u+M=\ngithub.com/rivo/tview v0.0.0-20210217110421-8a8f78a6dd01/go.mod h1:n2q/ydglZJ1kqxiNrnYO+FaX1H14vA0wKyIo953QakU=\ngithub.com/rivo/tview v0.0.0-20210312174852-ae9464cc3598 h1:AbRrGXhagPRDItERv7nauBUUPi7Ma3IGIj9FqkQKW6k=\ngithub.com/rivo/tview v0.0.0-20210312174852-ae9464cc3598/go.mod h1:VzCN9WX13RF88iH2CaGkmdHOlsy1ZZQcTmNwROqC+LI=\ngithub.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b h1:0/ecDXh/HTHRtSDSFnD2/Ta1yQ5J76ZspVY4u0/jGFk=\ngithub.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=\ngithub.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU=\ngithub.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs=\ngolang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210218155724-8ebf48af031b h1:lAZ0/chPUDWwjqosYR0X4M490zQhMsiJ4K3DbA7o+3g=\ngolang.org/x/sys v0.0.0-20210218155724-8ebf48af031b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48 h1:70qalHWW1n9yoI8B8zEQxFJO/D6NUWIX8SNmJO+rvNw=\ngolang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=\ngolang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss=\ngopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "ui/assignees.go",
    "content": "package ui\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nvar AssigneesUI *SelectUI\n\nfunc NewAssignableUI() {\n\t//getList := func(cursor *string) ([]List, github.PageInfo) {\n\t//\tv := map[string]interface{}{\n\t//\t\t\"owner\":  githubv4.String(config.GitHub.Owner),\n\t//\t\t\"name\":   githubv4.String(config.GitHub.Repo),\n\t//\t\t\"first\":  githubv4.Int(100),\n\t//\t\t\"cursor\": (*githubv4.String)(cursor),\n\t//\t}\n\t//\tresp, err := github.GetRepoAssignableUsers(v)\n\t//\tif err != nil {\n\t//\t\treturn nil, github.PageInfo{}\n\t//\t}\n\n\t//\tassignees := make([]List, len(resp.Nodes))\n\t//\tfor i, p := range resp.Nodes {\n\t//\t\tassignees[i] = &AssignableUser{\n\t//\t\t\tLogin: string(p.Login),\n\t//\t\t}\n\t//\t}\n\t//\treturn assignees, resp.PageInfo\n\t//}\n\n\tsetOpt := func(ui *SelectUI) {\n\t\tui.capture = func(event *tcell.EventKey) *tcell.EventKey {\n\t\t\treturn event\n\t\t}\n\t}\n\n\tui := NewSelectListUI(UIKindAssignee, tcell.ColorFuchsia, setOpt)\n\tAssigneesUI = ui\n}\n"
  },
  {
    "path": "ui/comments.go",
    "content": "package ui\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/domain\"\n\t\"github.com/skanehira/ght/github\"\n\t\"github.com/skanehira/ght/utils\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar CommentUI *SelectUI\n\nfunc NewCommentUI() {\n\tsetOpt := func(ui *SelectUI) {\n\t\tui.capture = func(event *tcell.EventKey) *tcell.EventKey {\n\t\t\tswitch event.Rune() {\n\t\t\tcase 'd':\n\t\t\t\tdeleteComment()\n\t\t\tcase 'n':\n\t\t\t\titem := IssueUI.GetSelect()\n\t\t\t\tif item == nil {\n\t\t\t\t\treturn event\n\t\t\t\t}\n\t\t\t\tif err := createComment(item, \"\"); err != nil {\n\t\t\t\t\tUI.Message(err.Error(), func() {\n\t\t\t\t\t\tUI.app.SetFocus(CommentUI)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\tcase 'e':\n\t\t\t\tif err := editComment(); err != nil {\n\t\t\t\t\tUI.Message(err.Error(), func() {\n\t\t\t\t\t\tUI.app.SetFocus(CommentUI)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\tcase 'r':\n\t\t\t\tif err := quoteReply(); err != nil {\n\t\t\t\t\tUI.Message(err.Error(), func() {\n\t\t\t\t\t\tUI.app.SetFocus(CommentUI)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch event.Key() {\n\t\t\tcase tcell.KeyCtrlO:\n\t\t\t\tfor _, comment := range getSelectedComments() {\n\t\t\t\t\tif err := utils.Open(comment.URL); err != nil {\n\t\t\t\t\t\tlog.Println(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCommentUI.ClearSelected()\n\t\t\t\tCommentUI.UpdateView()\n\t\t\t}\n\n\t\t\treturn event\n\t\t}\n\n\t\tui.header = []string{\n\t\t\t\"\",\n\t\t\t\"Author\",\n\t\t\t\"UpdatedAt\",\n\t\t}\n\t\tui.hasHeader = len(ui.header) > 0\n\t}\n\n\tui := NewSelectListUI(UIKindComment, tcell.ColorYellow, setOpt)\n\n\tui.SetSelectionChangedFunc(func(row, col int) {\n\t\tif row > 0 {\n\t\t\tCommentViewUI.updateView(ui.items[row-1].(*domain.Comment).Body)\n\t\t}\n\t})\n\n\tCommentUI = ui\n}\n\nfunc quoteReply() error {\n\titem := CommentUI.GetSelect()\n\tif item == nil {\n\t\treturn domain.ErrNotFoundComment\n\t}\n\n\tcomment := item.(*domain.Comment)\n\tlines := strings.Split(comment.Body, \"\\n\")\n\tfor i := range lines {\n\t\tlines[i] = fmt.Sprintf(\"> %s\", lines[i])\n\t}\n\n\tbody := strings.Join(lines, \"\\n\")\n\n\titem = IssueUI.GetSelect()\n\tif item == nil {\n\t\treturn domain.ErrNotFoundIssue\n\t}\n\tif err := createComment(item, body); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc createComment(item domain.Item, body string) error {\n\tif err := editCommentBody(&body); err != nil {\n\t\treturn err\n\t}\n\n\tinput := githubv4.AddCommentInput{\n\t\tSubjectID: githubv4.ID(item.Key()),\n\t\tBody:      githubv4.String(body),\n\t}\n\n\tif err := github.AddIssueComment(input); err != nil {\n\t\treturn err\n\t}\n\n\tif err := updateCommentUI(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc deleteComment() {\n\tUI.Confirm(\"Do you want to delete comments?\", \"Yes\", func() error {\n\t\tcomments := getSelectedComments()\n\t\tif len(comments) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar eg errgroup.Group\n\t\tfor _, comment := range comments {\n\t\t\tid := comment.ID\n\t\t\teg.Go(func() error {\n\t\t\t\treturn github.DeleteIssueComment(id)\n\t\t\t})\n\t\t}\n\n\t\t// When all the processing is completed this error be returned\n\t\t// because if some of delete action be success, need to update view\n\t\tdeleteErr := eg.Wait()\n\t\tif deleteErr != nil {\n\t\t\tlog.Println(deleteErr)\n\t\t}\n\n\t\tif err := updateCommentUI(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn deleteErr\n\t}, func() {\n\t\tUI.app.SetFocus(CommentUI)\n\t})\n}\n\nfunc editComment() error {\n\titem := CommentUI.GetSelect()\n\tif item == nil {\n\t\treturn domain.ErrNotFoundComment\n\t}\n\n\tcomment := item.(*domain.Comment)\n\toldBody := comment.Body\n\n\tif err := editCommentBody(&comment.Body); err != nil {\n\t\treturn err\n\t}\n\n\t// if comment body is not changed, do nothing\n\tif oldBody == comment.Body {\n\t\treturn nil\n\t}\n\n\tinput := githubv4.UpdateIssueCommentInput{\n\t\tID:   githubv4.ID(comment.ID),\n\t\tBody: githubv4.String(comment.Body),\n\t}\n\n\tif err := github.UpdateIssueComment(input); err != nil {\n\t\treturn err\n\t}\n\n\tif err := updateCommentUI(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc editCommentBody(body *string) (err error) {\n\tUI.app.Suspend(func() {\n\t\terr = utils.Edit(body)\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif *body == \"\" {\n\t\treturn domain.ErrCommentBodyIsEmpty\n\t}\n\treturn\n}\n\nfunc getSelectedComments() []*domain.Comment {\n\tvar comments []*domain.Comment\n\tif len(CommentUI.selected) == 0 {\n\t\tdata := CommentUI.GetSelect()\n\t\tcomments = append(comments, data.(*domain.Comment))\n\t} else {\n\t\tfor _, item := range CommentUI.selected {\n\t\t\tcomments = append(comments, item.(*domain.Comment))\n\t\t}\n\t}\n\treturn comments\n}\n\nfunc updateCommentUI() error {\n\titem := IssueUI.GetSelect()\n\tif item == nil {\n\t\treturn domain.ErrNotFoundIssue\n\t}\n\toldIssue := item.(*domain.Issue)\n\n\tnumber, err := strconv.Atoi(oldIssue.Number)\n\tif err != nil {\n\t\treturn err\n\t}\n\tm := map[string]interface{}{\n\t\t\"owner\":  githubv4.String(oldIssue.RepoOwner),\n\t\t\"name\":   githubv4.String(oldIssue.Repo),\n\t\t\"number\": githubv4.Int(number),\n\t}\n\n\tissue, err := github.GetIssue(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnewIssue := issue.ToDomain()\n\tIssueUI.UpdateItem(newIssue)\n\n\tif len(newIssue.Comments) > 0 {\n\t\tCommentUI.SetList(newIssue.Comments)\n\t\tCommentViewUI.updateView(newIssue.Comments[0].(*domain.Comment).Body)\n\t} else {\n\t\tCommentUI.ClearView()\n\t\tCommentViewUI.Clear()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "ui/filter.go",
    "content": "package ui\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/rivo/tview\"\n)\n\nvar IssueFilterUI *FilterUI\n\ntype (\n\tSetFilterOpt func(ui *FilterUI)\n\tFilterUI     struct {\n\t\t*tview.InputField\n\t}\n)\n\nfunc NewFilterUI() {\n\tui := &FilterUI{\n\t\tInputField: tview.NewInputField().SetLabel(\"Filters\").SetLabelWidth(8),\n\t}\n\tui.SetBorderPadding(0, 0, 1, 0)\n\n\tui.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {\n\t\tswitch event.Key() {\n\t\tcase tcell.KeyEnter:\n\t\t\tgo IssueUI.GetList()\n\t\t}\n\t\treturn event\n\t})\n\tIssueFilterUI = ui\n}\n\nfunc (ui *FilterUI) SetQuery(query string) {\n\tui.SetText(query)\n}\n\nfunc (ui *FilterUI) GetQuery() string {\n\treturn ui.GetText()\n}\n\nfunc (ui *FilterUI) focus() {\n}\n\nfunc (ui *FilterUI) blur() {\n}\n"
  },
  {
    "path": "ui/issues.go",
    "content": "package ui\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/atotto/clipboard\"\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/rivo/tview\"\n\t\"github.com/shurcooL/githubv4\"\n\t\"github.com/skanehira/ght/config\"\n\t\"github.com/skanehira/ght/domain\"\n\t\"github.com/skanehira/ght/github\"\n\t\"github.com/skanehira/ght/utils\"\n)\n\nvar IssueUI *SelectUI\n\nfunc NewIssueUI() {\n\topt := func(ui *SelectUI) {\n\t\t// initial query\n\t\tqueries := []string{\n\t\t\tfmt.Sprintf(\"repo:%s/%s\", config.GitHub.Owner, config.GitHub.Repo),\n\t\t\t\"state:open\",\n\t\t}\n\n\t\tIssueFilterUI.SetQuery(strings.Join(queries, \" \"))\n\n\t\tui.getList = func(cursor *string) ([]domain.Item, *github.PageInfo) {\n\t\t\tvar queries []string\n\t\t\tquery := IssueFilterUI.GetQuery()\n\n\t\t\tif !strings.Contains(query, \"is:issue\") {\n\t\t\t\tqueries = append(queries, \"is:issue\")\n\t\t\t}\n\n\t\t\tfor _, q := range strings.Split(query, \" \") {\n\t\t\t\t// execlude Pull request\n\t\t\t\tif strings.Contains(q, \"type:pr\") || strings.Contains(q, \"is:pr\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tqueries = append(queries, q)\n\t\t\t}\n\t\t\tquery = strings.Join(queries, \" \")\n\t\t\tIssueFilterUI.SetQuery(query)\n\n\t\t\tv := map[string]interface{}{\n\t\t\t\t\"query\":  githubv4.String(query),\n\t\t\t\t\"first\":  githubv4.Int(30),\n\t\t\t\t\"cursor\": (*githubv4.String)(cursor),\n\t\t\t}\n\t\t\tresp, err := github.GetIssues(v)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t\treturn nil, nil\n\t\t\t}\n\n\t\t\tissues := make([]domain.Item, len(resp.Nodes))\n\t\t\tfor i, node := range resp.Nodes {\n\t\t\t\tissues[i] = node.Issue.ToDomain()\n\t\t\t}\n\t\t\treturn issues, &resp.PageInfo\n\t\t}\n\n\t\tui.capture = func(event *tcell.EventKey) *tcell.EventKey {\n\t\t\tswitch event.Rune() {\n\t\t\tcase 'y':\n\t\t\t\tyankIssueURLs()\n\t\t\tcase 'o':\n\t\t\t\tgo openIssues()\n\t\t\tcase 'c':\n\t\t\t\tgo closeIssues()\n\t\t\tcase 'n':\n\t\t\t\tcreateIssueForm()\n\t\t\tcase 'e':\n\t\t\t\teditIssue()\n\t\t\t}\n\t\t\tswitch event.Key() {\n\t\t\tcase tcell.KeyCtrlO:\n\t\t\t\topenBrowser()\n\t\t\t}\n\n\t\t\treturn event\n\t\t}\n\n\t\tui.header = []string{\n\t\t\t\"\",\n\t\t\t\"Repo\",\n\t\t\t\"Number\",\n\t\t\t\"State\",\n\t\t\t\"Author\",\n\t\t\t\"Title\",\n\t\t}\n\n\t\tui.hasHeader = len(ui.header) > 0\n\t}\n\n\tui := NewSelectListUI(UIKindIssue, tcell.ColorBlue, opt)\n\n\tui.SetSelectionChangedFunc(func(row, col int) {\n\t\tupdateUIRelatedIssue(ui, row)\n\t})\n\n\tIssueUI = ui\n}\n\nfunc getSelectedIssues() []*domain.Issue {\n\tvar issues []*domain.Issue\n\tif len(IssueUI.selected) == 0 {\n\t\tdata := IssueUI.GetSelect()\n\t\tif data != nil {\n\t\t\tissues = append(issues, data.(*domain.Issue))\n\t\t}\n\t} else {\n\t\tfor _, item := range IssueUI.selected {\n\t\t\tissues = append(issues, item.(*domain.Issue))\n\t\t}\n\t}\n\treturn issues\n}\n\nfunc yankIssueURLs() {\n\tvar urls []string\n\tfor _, issue := range getSelectedIssues() {\n\t\turls = append(urls, issue.URL)\n\t}\n\n\turl := strings.Join(urls, \"\\n\")\n\tif err := clipboard.WriteAll(url); err != nil {\n\t\tlog.Println(err)\n\t}\n\tIssueUI.ClearSelected()\n\tIssueUI.UpdateView()\n}\n\nfunc openIssues() {\n\tvar wg sync.WaitGroup\n\tfor _, issue := range getSelectedIssues() {\n\t\twg.Add(1)\n\t\tgo func(issue *domain.Issue) {\n\t\t\tdefer wg.Done()\n\t\t\tif err := github.ReopenIssue(issue.ID); err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tissue.State = \"OPEN\"\n\t\t}(issue)\n\t}\n\twg.Wait()\n\tIssueUI.ClearSelected()\n\tIssueUI.UpdateView()\n}\n\nfunc closeIssues() {\n\tvar wg sync.WaitGroup\n\tfor _, issue := range getSelectedIssues() {\n\t\twg.Add(1)\n\t\tgo func(issue *domain.Issue) {\n\t\t\tdefer wg.Done()\n\t\t\tif err := github.CloseIssue(issue.ID); err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tissue.State = \"CLOSED\"\n\t\t}(issue)\n\t}\n\twg.Wait()\n\tIssueUI.ClearSelected()\n\tIssueUI.UpdateView()\n}\n\nfunc openBrowser() {\n\tfor _, issue := range getSelectedIssues() {\n\t\tif err := utils.Open(issue.URL); err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\t}\n\tIssueUI.ClearSelected()\n\tIssueUI.UpdateView()\n\n}\n\nfunc createIssueForm() {\n\t// repo\n\tvar repo string\n\tinput := IssueFilterUI.GetQuery()\n\tfor _, word := range strings.Split(input, \" \") {\n\t\tif strings.Contains(word, \"repo:\") {\n\t\t\trepo = strings.TrimPrefix(word, \"repo:\")\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif repo == \"\" {\n\t\treturn\n\t}\n\n\tform := tview.NewForm()\n\tform.SetBorder(true)\n\tform.SetTitle(\"New issue\")\n\tform.SetTitleAlign(tview.AlignLeft)\n\tinputWidth := 70\n\n\trepoInput := tview.NewInputField().SetLabel(\"Repository\").\n\t\tSetText(repo).SetLabelWidth(inputWidth).\n\t\tSetAcceptanceFunc(func(textToCheck string, lastChar rune) bool {\n\t\t\treturn false\n\t\t})\n\trepoInput.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {\n\t\tif event.Key() == tcell.KeyCtrlN {\n\t\t\tform.SetFocus(2)\n\t\t}\n\t\treturn event\n\t})\n\n\tform.AddFormItem(repoInput)\n\n\ts := strings.Split(repoInput.GetText(), \"/\")\n\towner := s[0]\n\tname := s[1]\n\n\tvar repoID githubv4.ID\n\tresp, err := github.GetRepo(map[string]interface{}{\n\t\t\"owner\": githubv4.String(owner),\n\t\t\"name\":  githubv4.String(name),\n\t})\n\tif err != nil {\n\t\tUI.Message(err.Error(), func() {\n\t\t\tUI.app.SetFocus(IssueUI)\n\t\t})\n\t\treturn\n\t}\n\trepoID = resp.ID\n\n\tform.SetFocus(1)\n\n\t// autocomplete for assignees, labels, projects, milestones\n\tautocompleteFunc := func(text string, items []string) []string {\n\t\tif text == \"\" {\n\t\t\treturn nil\n\t\t}\n\n\t\twords := strings.Split(text, \",\")\n\t\tword := words[len(words)-1]\n\n\t\tvar results []string\n\t\tfor _, l := range items {\n\t\t\tvar isDuplicate bool\n\t\t\tfor _, w := range words[:len(words)-1] {\n\t\t\t\tif w == l {\n\t\t\t\t\tisDuplicate = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isDuplicate {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif strings.Contains(strings.ToLower(l), strings.ToLower(word)) {\n\t\t\t\twords = append(words[:len(words)-1], l)\n\t\t\t\tresults = append(results, strings.Join(words, \",\"))\n\t\t\t}\n\t\t}\n\t\treturn results\n\t}\n\n\t// graphql query variables\n\tv := map[string]interface{}{\n\t\t\"owner\":  githubv4.String(owner),\n\t\t\"name\":   githubv4.String(name),\n\t\t\"first\":  githubv4.Int(100),\n\t\t\"cursor\": (*githubv4.String)(nil),\n\t}\n\n\t// title\n\ttitleInput := tview.NewInputField().SetLabel(\"Title\").SetLabelWidth(inputWidth)\n\tform.AddFormItem(titleInput)\n\n\t// assignees\n\tassigneesInput := tview.NewInputField().SetLabel(\"Assignees\").SetLabelWidth(inputWidth)\n\tform.AddFormItem(assigneesInput)\n\tuserMap := map[string]githubv4.ID{}\n\tgo func() {\n\t\tresp, err := github.GetRepoAssignableUsers(v)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(resp.Nodes) == 0 {\n\t\t\tUI.app.QueueUpdateDraw(func() {\n\t\t\t\tform.RemoveFormItem(2)\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tvar users []string\n\t\tfor _, u := range resp.Nodes {\n\t\t\tname := string(u.Login)\n\t\t\tuserMap[name] = u.ID\n\t\t\tusers = append(users, name)\n\t\t}\n\t\tassigneesInput.SetAutocompleteFunc(func(text string) []string {\n\t\t\treturn autocompleteFunc(text, users)\n\t\t})\n\t}()\n\n\t// labels\n\tlabelInput := tview.NewInputField().SetLabel(\"Labels\").SetLabelWidth(inputWidth)\n\tlabelMap := map[string]githubv4.ID{}\n\tform.AddFormItem(labelInput)\n\tgo func() {\n\t\tresp, err := github.GetRepoLabels(v)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(resp.Nodes) == 0 {\n\t\t\tUI.app.QueueUpdateDraw(func() {\n\t\t\t\tform.RemoveFormItem(3)\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tvar labels []string\n\t\tfor _, l := range resp.Nodes {\n\t\t\tname := string(l.Name)\n\t\t\tlabelMap[name] = l.ID\n\t\t\tlabels = append(labels, name)\n\t\t}\n\t\tlabelInput.SetAutocompleteFunc(func(text string) []string {\n\t\t\treturn autocompleteFunc(text, labels)\n\t\t})\n\t}()\n\n\t// projects\n\tprojectInput := tview.NewInputField().SetLabel(\"Projects\").SetLabelWidth(inputWidth)\n\tprojectMap := map[string]githubv4.ID{}\n\tform.AddFormItem(projectInput)\n\tgo func() {\n\t\tresp, err := github.GetRepoProjects(v)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(resp.Nodes) == 0 {\n\t\t\tUI.app.QueueUpdateDraw(func() {\n\t\t\t\tform.RemoveFormItem(4)\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tvar projects []string\n\t\tfor _, project := range resp.Nodes {\n\t\t\tname := string(project.Name)\n\t\t\tprojectMap[name] = project.ID\n\t\t\tprojects = append(projects, name)\n\t\t}\n\n\t\tprojectInput.SetAutocompleteFunc(func(text string) []string {\n\t\t\treturn autocompleteFunc(text, projects)\n\t\t})\n\t}()\n\n\t// milestones\n\tmilestoneDropDown := tview.NewDropDown().SetLabel(\"MileStone\").SetLabelWidth(inputWidth)\n\tvar milestoneID *githubv4.ID\n\tform.AddFormItem(milestoneDropDown)\n\tgo func() {\n\t\tresp, err := github.GetRepoMillestones(v)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(resp.Nodes) == 0 {\n\t\t\tUI.app.QueueUpdateDraw(func() {\n\t\t\t\tform.RemoveFormItem(5)\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tmilestones := map[string]*githubv4.ID{}\n\t\tvar titles []string\n\t\tfor _, milestone := range resp.Nodes {\n\t\t\ttitle := string(milestone.Title)\n\t\t\tmilestones[title] = &milestone.ID\n\t\t\ttitles = append(titles, title)\n\t\t}\n\t\tmilestoneDropDown.SetOptions(titles, func(text string, index int) {\n\t\t\tmilestoneID = milestones[text]\n\t\t})\n\t}()\n\n\tvar issueBody string\n\ttemplateDropDown := tview.NewDropDown().SetLabel(\"Template\").SetLabelWidth(inputWidth)\n\tgo func() {\n\t\tv := map[string]interface{}{\n\t\t\t\"owner\": githubv4.String(owner),\n\t\t\t\"name\":  githubv4.String(name),\n\t\t}\n\t\tresp, err := github.GetIssueTemplates(v)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn\n\t\t}\n\t\tif len(resp) == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tissueTemplates := map[string]string{}\n\t\tvar names []string\n\t\tfor _, te := range resp {\n\t\t\tissueTemplates[string(te.Name)] = string(te.Body)\n\t\t\tnames = append(names, string(te.Name))\n\t\t}\n\n\t\ttemplateDropDown.SetOptions(names, func(text string, index int) {\n\t\t\tissueBody = issueTemplates[text]\n\t\t})\n\t\tUI.app.QueueUpdateDraw(func() {\n\t\t\tform.AddFormItem(templateDropDown)\n\t\t})\n\t}()\n\n\tform.AddButton(\"Edit Body\", func() {\n\t\tUI.app.Suspend(func() {\n\t\t\tif err := utils.Edit(&issueBody); err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t})\n\tform.AddButton(\"Create\", func() {\n\t\tinput := githubv4.CreateIssueInput{\n\t\t\tTitle:        githubv4.String(titleInput.GetText()),\n\t\t\tRepositoryID: repoID,\n\t\t}\n\t\tif milestoneID != nil {\n\t\t\tinput.MilestoneID = milestoneID\n\t\t}\n\n\t\t// get assignee users\n\t\tvar userIDs []githubv4.ID\n\t\tif text := assigneesInput.GetText(); text != \"\" {\n\t\t\tfor _, name := range strings.Split(text, \",\") {\n\t\t\t\tif name == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tuserIDs = append(userIDs, userMap[name])\n\t\t\t}\n\t\t\tinput.AssigneeIDs = &userIDs\n\t\t}\n\n\t\t// get labels\n\t\tvar labelIDs []githubv4.ID\n\t\tif text := labelInput.GetText(); text != \"\" {\n\t\t\tfor _, name := range strings.Split(text, \",\") {\n\t\t\t\tif name == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlabelIDs = append(labelIDs, labelMap[name])\n\t\t\t}\n\t\t\tinput.LabelIDs = &labelIDs\n\t\t}\n\n\t\t// get projects\n\t\tvar projectIDs []githubv4.ID\n\t\tif text := projectInput.GetText(); text != \"\" {\n\t\t\tfor _, name := range strings.Split(text, \",\") {\n\t\t\t\tif name == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tprojectIDs = append(projectIDs, projectMap[name])\n\t\t\t}\n\t\t\tinput.ProjectIDs = &projectIDs\n\t\t}\n\n\t\tbody := githubv4.String(issueBody)\n\t\tinput.Body = &body\n\n\t\tif err := github.CreateIssue(input); err != nil {\n\t\t\tUI.Message(err.Error(), func() {\n\t\t\t\tUI.pages.SwitchToPage(\"form\").ShowPage(\"main\")\n\t\t\t})\n\t\t} else {\n\t\t\tUI.pages.RemovePage(\"form\").ShowPage(\"main\")\n\t\t\tUI.app.SetFocus(IssueUI)\n\t\t\tgo func() {\n\t\t\t\ttime.Sleep(1 * time.Second)\n\t\t\t\tIssueUI.GetList()\n\t\t\t}()\n\t\t}\n\t})\n\tform.AddButton(\"Cancel\", func() {\n\t\tUI.pages.RemovePage(\"form\").ShowPage(\"main\")\n\t\tUI.app.SetFocus(IssueUI)\n\t})\n\n\tform.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {\n\t\tswitch event.Key() {\n\t\tcase tcell.KeyCtrlN:\n\t\t\tk := tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone)\n\t\t\tUI.app.QueueEvent(k)\n\t\tcase tcell.KeyCtrlP:\n\t\t\tk := tcell.NewEventKey(tcell.KeyBacktab, 0, tcell.ModNone)\n\t\t\tUI.app.QueueEvent(k)\n\t\t}\n\t\treturn event\n\t})\n\n\tUI.pages.AddAndSwitchToPage(\"form\", UI.Modal(form, 100, 19), true).ShowPage(\"main\")\n}\n\nfunc editIssue() {\n\titem := IssueUI.GetSelect()\n\tif item == nil {\n\t\treturn\n\t}\n\tissue := item.(*domain.Issue)\n\n\tfocus := func() {\n\t\tUI.app.SetFocus(IssueUI)\n\t}\n\n\tvar err error\n\told := issue.Body\n\tUI.app.Suspend(func() {\n\t\terr = utils.Edit(&issue.Body)\n\t})\n\tif err != nil {\n\t\tUI.Message(err.Error(), focus)\n\t\treturn\n\t}\n\n\t// if issue body not edited do nothing\n\tif old == issue.Body {\n\t\treturn\n\t}\n\n\tinput := githubv4.UpdateIssueInput{\n\t\tID:   githubv4.ID(issue.ID),\n\t\tBody: githubv4.NewString(githubv4.String(issue.Body)),\n\t}\n\tif err := github.UpdateIssue(input); err != nil {\n\t\tUI.Message(err.Error(), focus)\n\t\treturn\n\t}\n\n\tIssueViewUI.updateView(issue.Body)\n}\n\nfunc updateUIRelatedIssue(ui *SelectUI, row int) {\n\tif row > 0 && row <= len(ui.items) {\n\t\tissue := ui.items[row-1].(*domain.Issue)\n\t\tIssueViewUI.updateView(issue.Body)\n\n\t\tif len(issue.Comments) > 0 {\n\t\t\tCommentUI.SetList(issue.Comments)\n\t\t\tCommentViewUI.updateView(issue.Comments[0].(*domain.Comment).Body)\n\t\t} else {\n\t\t\tCommentUI.ClearView()\n\t\t\tCommentViewUI.Clear()\n\t\t}\n\n\t\tif len(issue.Assignees) > 0 {\n\t\t\tAssigneesUI.SetList(issue.Assignees)\n\t\t} else {\n\t\t\tAssigneesUI.ClearView()\n\t\t}\n\n\t\tif len(issue.Labels) > 0 {\n\t\t\tLabelUI.SetList(issue.Labels)\n\t\t} else {\n\t\t\tLabelUI.ClearView()\n\t\t}\n\n\t\tif len(issue.MileStone) > 0 {\n\t\t\tMilestoneUI.SetList(issue.MileStone)\n\t\t} else {\n\t\t\tMilestoneUI.ClearView()\n\t\t}\n\n\t\tif len(issue.Projects) > 0 {\n\t\t\tProjectUI.SetList(issue.Projects)\n\t\t} else {\n\t\t\tProjectUI.ClearView()\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ui/labels.go",
    "content": "package ui\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nvar LabelUI *SelectUI\n\nfunc NewLabelsUI() {\n\t//getList := func(cursor *string) ([]List, github.PageInfo) {\n\t//\tv := map[string]interface{}{\n\t//\t\t\"owner\":  githubv4.String(config.GitHub.Owner),\n\t//\t\t\"name\":   githubv4.String(config.GitHub.Repo),\n\t//\t\t\"first\":  githubv4.Int(100),\n\t//\t\t\"cursor\": (*githubv4.String)(cursor),\n\t//\t}\n\t//\tresp, err := github.GetRepoLabels(v)\n\t//\tif err != nil {\n\t//\t\tlog.Println(err)\n\t//\t\treturn nil, github.PageInfo{}\n\t//\t}\n\n\t//\tlabels := make([]List, len(resp.Nodes))\n\t//\tfor i, l := range resp.Nodes {\n\t//\t\tname := string(l.Name)\n\t//\t\tdescription := string(l.Description)\n\t//\t\tlabels[i] = &Label{\n\t//\t\t\tName:        name,\n\t//\t\t\tDescription: description,\n\t//\t\t}\n\t//\t}\n\t//\treturn labels, resp.PageInfo\n\t//}\n\n\tsetOpt := func(ui *SelectUI) {\n\t\tui.capture = func(event *tcell.EventKey) *tcell.EventKey {\n\t\t\treturn event\n\t\t}\n\t}\n\n\tui := NewSelectListUI(UIKindLabel, tcell.ColorLightYellow, setOpt)\n\tLabelUI = ui\n}\n"
  },
  {
    "path": "ui/milestones.go",
    "content": "package ui\n\nimport (\n\t\"log\"\n\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/skanehira/ght/domain\"\n\t\"github.com/skanehira/ght/utils\"\n)\n\nvar MilestoneUI *SelectUI\n\nfunc NewMilestoneUI() {\n\t//getList := func(cursor *string) ([]List, github.PageInfo) {\n\t//\tv := map[string]interface{}{\n\t//\t\t\"owner\":  githubv4.String(config.GitHub.Owner),\n\t//\t\t\"name\":   githubv4.String(config.GitHub.Repo),\n\t//\t\t\"first\":  githubv4.Int(100),\n\t//\t\t\"cursor\": (*githubv4.String)(cursor),\n\t//\t}\n\t//\tresp, err := github.GetRepoMillestones(v)\n\t//\tif err != nil {\n\t//\t\treturn nil, github.PageInfo{}\n\t//\t}\n\n\t//\tmilestones := make([]List, len(resp.Nodes))\n\t//\tfor i, m := range resp.Nodes {\n\t//\t\tmilestones[i] = &Milestone{\n\t//\t\t\tTitle: string(m.Title),\n\t//\t\t}\n\t//\t}\n\n\t//\treturn milestones, resp.PageInfo\n\t//}\n\n\tsetOpt := func(ui *SelectUI) {\n\n\t\tui.capture = func(event *tcell.EventKey) *tcell.EventKey {\n\t\t\tswitch event.Key() {\n\t\t\tcase tcell.KeyCtrlO:\n\t\t\t\tvar urls []string\n\t\t\t\tif len(MilestoneUI.selected) == 0 {\n\t\t\t\t\tdata := MilestoneUI.GetSelect()\n\t\t\t\t\tif data != nil {\n\t\t\t\t\t\turls = append(urls, data.(*domain.Milestone).URL)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor _, s := range MilestoneUI.selected {\n\t\t\t\t\t\turls = append(urls, s.(*domain.Milestone).URL)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor _, url := range urls {\n\t\t\t\t\tif err := utils.Open(url); err != nil {\n\t\t\t\t\t\tlog.Println(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn event\n\t\t}\n\t}\n\n\tui := NewSelectListUI(UIKindMilestones, tcell.ColorGreen, setOpt)\n\tMilestoneUI = ui\n}\n"
  },
  {
    "path": "ui/projects.go",
    "content": "package ui\n\nimport (\n\t\"log\"\n\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/skanehira/ght/domain\"\n\t\"github.com/skanehira/ght/utils\"\n)\n\nvar ProjectUI *SelectUI\n\nfunc NewProjectUI() {\n\t//getList := func(cursor *string) ([]List, github.PageInfo) {\n\t//\tv := map[string]interface{}{\n\t//\t\t\"owner\":  githubv4.String(config.GitHub.Owner),\n\t//\t\t\"name\":   githubv4.String(config.GitHub.Repo),\n\t//\t\t\"first\":  githubv4.Int(100),\n\t//\t\t\"cursor\": (*githubv4.String)(cursor),\n\t//\t}\n\t//\tresp, err := github.GetRepoProjects(v)\n\t//\tif err != nil {\n\t//\t\treturn nil, github.PageInfo{}\n\t//\t}\n\n\t//\tprojects := make([]List, len(resp.Nodes))\n\t//\tfor i, m := range resp.Nodes {\n\t//\t\tprojects[i] = &Project{\n\t//\t\t\tName: string(m.Name),\n\t//\t\t}\n\t//\t}\n\n\t//\treturn projects, resp.PageInfo\n\t//}\n\n\tsetOpt := func(ui *SelectUI) {\n\t\tui.capture = func(event *tcell.EventKey) *tcell.EventKey {\n\t\t\tswitch event.Key() {\n\t\t\tcase tcell.KeyCtrlO:\n\t\t\t\tvar urls []string\n\t\t\t\tif len(ProjectUI.selected) == 0 {\n\t\t\t\t\tdata := ProjectUI.GetSelect()\n\t\t\t\t\tif data != nil {\n\t\t\t\t\t\turls = append(urls, data.(*domain.Project).URL)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor _, s := range ProjectUI.selected {\n\t\t\t\t\t\turls = append(urls, s.(*domain.Project).URL)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor _, url := range urls {\n\t\t\t\t\tif err := utils.Open(url); err != nil {\n\t\t\t\t\t\tlog.Println(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn event\n\t\t}\n\t}\n\n\tui := NewSelectListUI(UIKindProject, tcell.ColorLightSalmon, setOpt)\n\tProjectUI = ui\n}\n"
  },
  {
    "path": "ui/search.go",
    "content": "package ui\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/rivo/tview\"\n)\n\nvar SearchUI *searchUI\n\ntype SearchFunc func(text string)\n\ntype searchUI struct {\n\t*tview.InputField\n\tSearchFunc SearchFunc\n\tFocusFunc  func()\n}\n\nfunc NewSearchUI() {\n\tui := &searchUI{\n\t\tInputField: tview.NewInputField(),\n\t\tSearchFunc: func(text string) {},\n\t\tFocusFunc:  func() {},\n\t}\n\n\tui.SetDoneFunc(func(key tcell.Key) {\n\t\tui.FocusFunc()\n\t})\n\n\tSearchUI = ui\n}\n\nfunc (s *searchUI) SetSerachFunc(f SearchFunc) {\n\ts.SetChangedFunc(f)\n}\n\nfunc (s *searchUI) SetFocusFunc(f func()) {\n\ts.FocusFunc = f\n}\n\nfunc (s *searchUI) focus() {\n\n}\n\nfunc (s *searchUI) blur() {\n\n}\n"
  },
  {
    "path": "ui/select.go",
    "content": "package ui\n\nimport (\n\t\"strings\"\n\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/rivo/tview\"\n\t\"github.com/skanehira/ght/domain\"\n\t\"github.com/skanehira/ght/github\"\n)\n\nconst (\n\tunselected = \"\\u25ef\"\n\tselected   = \"\\u25c9\"\n)\n\ntype UIKind string\n\nconst (\n\tUIKindIssue       UIKind = \"issues\"\n\tUIKindAssignee           = \"assignees\"\n\tUIKindComment            = \"comments\"\n\tUIKindLabel              = \"labels\"\n\tUIKindMilestones         = \"milestones\"\n\tUIKindProject            = \"projects\"\n\tUIKindIssueView          = \"issue preview\"\n\tUIKindCommentView        = \"comment preview\"\n\tUIKindCommonView         = \"preview\"\n)\n\ntype (\n\tSetSelectUIOpt func(ui *SelectUI)\n\tGetListFunc    func(cursor *string) ([]domain.Item, *github.PageInfo)\n\tCaptureFunc    func(event *tcell.EventKey) *tcell.EventKey\n)\n\ntype SelectUI struct {\n\tuiKind      UIKind\n\tcursor      *string\n\thasNext     bool\n\tgetList     GetListFunc\n\tcapture     CaptureFunc\n\theader      []string\n\thasHeader   bool\n\toriginItems []domain.Item\n\titems       []domain.Item\n\tselected    map[string]domain.Item\n\tboxColor    tcell.Color\n\tsearchWord  string\n\t*tview.Table\n}\n\nfunc NewSelectListUI(uiKind UIKind, boxColor tcell.Color, setOpt SetSelectUIOpt) *SelectUI {\n\tui := &SelectUI{\n\t\tuiKind:   uiKind,\n\t\thasNext:  true,\n\t\tselected: make(map[string]domain.Item),\n\t\tboxColor: boxColor,\n\t\tTable:    tview.NewTable().SetSelectable(false, false),\n\t}\n\n\tui.SetBorder(true).SetTitle(string(uiKind)).SetTitleAlign(tview.AlignLeft)\n\tui.SetBorderColor(boxColor)\n\n\tsetOpt(ui)\n\n\tgo ui.Init()\n\treturn ui\n}\n\nfunc (ui *SelectUI) GetList() {\n\tif ui.getList != nil {\n\t\tlist, pageInfo := ui.getList(nil)\n\t\tif pageInfo != nil {\n\t\t\tui.hasNext = bool(pageInfo.HasNextPage)\n\t\t\tcursor := string(pageInfo.EndCursor)\n\t\t\tui.originItems = list\n\t\t\tui.cursor = &cursor\n\t\t\tui.Select(0, 0)\n\t\t\tui.UpdateView()\n\t\t}\n\t}\n}\n\nfunc (ui *SelectUI) SetList(list []domain.Item) {\n\tui.originItems = list\n\tui.selected = make(map[string]domain.Item)\n\tui.Select(0, 0)\n\tui.UpdateView()\n}\n\nfunc (ui *SelectUI) FetchList() {\n\tif ui.hasNext && ui.getList != nil {\n\t\tlist, pageInfo := ui.getList(ui.cursor)\n\t\tui.hasNext = bool(pageInfo.HasNextPage)\n\t\tcursor := string(pageInfo.EndCursor)\n\t\tui.originItems = append(ui.originItems, list...)\n\t\tui.cursor = &cursor\n\t\tui.UpdateView()\n\t}\n}\n\nfunc (ui *SelectUI) UpdateView() {\n\tUI.updater <- func() {\n\t\tui.Clear()\n\t\tfor i, h := range ui.header {\n\t\t\tui.SetCell(0, i, &tview.TableCell{\n\t\t\t\tText:            h,\n\t\t\t\tNotSelectable:   true,\n\t\t\t\tAlign:           tview.AlignLeft,\n\t\t\t\tColor:           tcell.ColorWhite,\n\t\t\t\tBackgroundColor: tcell.ColorDefault,\n\t\t\t\tAttributes:      tcell.AttrBold | tcell.AttrUnderline,\n\t\t\t})\n\t\t}\n\n\t\tif len(ui.originItems) < 1 {\n\t\t\treturn\n\t\t}\n\n\t\th := 0\n\t\tif ui.hasHeader {\n\t\t\th++\n\t\t\tui.SetFixed(1, 0)\n\t\t}\n\n\t\tselectColor := ui.originItems[0].Fields()[0].Color\n\n\t\tui.items = []domain.Item{}\n\t\tif ui.searchWord != \"\" {\n\t\t\tfor _, data := range ui.originItems {\n\t\t\t\tfor _, f := range data.Fields() {\n\t\t\t\t\tif strings.Contains(strings.ToLower(f.Text), strings.ToLower(ui.searchWord)) {\n\t\t\t\t\t\tui.items = append(ui.items, data)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tui.items = ui.originItems\n\t\t}\n\n\t\tfor i, data := range ui.items {\n\t\t\tif _, ok := ui.selected[data.Key()]; ok {\n\t\t\t\tui.SetCell(i+h, 0, tview.NewTableCell(selected).SetTextColor(selectColor))\n\t\t\t} else {\n\t\t\t\tui.SetCell(i+h, 0, tview.NewTableCell(unselected).SetTextColor(selectColor))\n\t\t\t}\n\t\t\tfor j, f := range data.Fields() {\n\t\t\t\tui.SetCell(i+h, j+1, tview.NewTableCell(f.Text).SetTextColor(f.Color))\n\t\t\t}\n\t\t}\n\t\tui.ScrollToBeginning()\n\n\t\t// when update filter, then update ui related issue primitives\n\t\tif ui.uiKind == UIKindIssue {\n\t\t\trow, _ := ui.GetSelection()\n\t\t\tif row == 0 {\n\t\t\t\trow = 1\n\t\t\t}\n\t\t\tupdateUIRelatedIssue(ui, row)\n\t\t}\n\t}\n}\n\nfunc (ui *SelectUI) Init() {\n\tui.GetList()\n\n\tsearchFunc := func(text string) {\n\t\tui.searchWord = text\n\t\tui.UpdateView()\n\t}\n\n\tui.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {\n\t\tswitch event.Key() {\n\t\tcase tcell.KeyCtrlJ:\n\t\t\trow, col := ui.GetSelection()\n\t\t\tmax := len(ui.items)\n\t\t\tif ui.hasHeader {\n\t\t\t\tmax++\n\t\t\t}\n\t\t\tif row < max {\n\t\t\t\tui.toggleSelected(row)\n\t\t\t}\n\n\t\t\tif row+1 < max {\n\t\t\t\tui.Select(row+1, col)\n\t\t\t}\n\t\tcase tcell.KeyCtrlK:\n\t\t\trow, col := ui.GetSelection()\n\t\t\tmin := 0\n\t\t\tif ui.hasHeader {\n\t\t\t\tmin++\n\t\t\t}\n\t\t\tif row > min {\n\t\t\t\tui.toggleSelected(row - 1)\n\t\t\t}\n\t\t\tif row > min {\n\t\t\t\tui.Select(row-1, col)\n\t\t\t}\n\t\t}\n\n\t\tswitch event.Rune() {\n\t\tcase 'f':\n\t\t\tgo ui.FetchList()\n\t\tcase '/':\n\t\t\tSearchUI.SetSerachFunc(searchFunc)\n\t\t\tSearchUI.SetFocusFunc(func() {\n\t\t\t\tUI.app.SetFocus(ui)\n\t\t\t})\n\t\t\tUI.app.SetFocus(SearchUI)\n\t\t}\n\n\t\treturn ui.capture(event)\n\t})\n}\n\nfunc (ui *SelectUI) toggleSelected(row int) {\n\tvar data domain.Item\n\tif ui.hasHeader {\n\t\tdata = ui.items[row-1]\n\t} else {\n\t\tdata = ui.items[row]\n\t}\n\tselectColor := ui.items[0].Fields()[0].Color\n\tif _, ok := ui.selected[data.Key()]; ok {\n\t\tdelete(ui.selected, data.Key())\n\t\tui.SetCell(row, 0, tview.NewTableCell(unselected).SetTextColor(selectColor))\n\t} else {\n\t\tui.selected[data.Key()] = data\n\t\tui.SetCell(row, 0, tview.NewTableCell(selected).SetTextColor(selectColor))\n\t}\n}\n\nfunc (ui *SelectUI) UpdateItem(item domain.Item) {\n\tfor i, t := range ui.originItems {\n\t\tif t.Key() == item.Key() {\n\t\t\tui.originItems[i] = item\n\t\t}\n\t}\n}\n\nfunc (ui *SelectUI) GetSelect() domain.Item {\n\trow, _ := ui.GetSelection()\n\tif ui.hasHeader {\n\t\trow = row - 1\n\t}\n\tif len(ui.items) > row {\n\t\tid := ui.items[row].Key()\n\t\tfor _, item := range ui.originItems {\n\t\t\tif item.Key() == id {\n\t\t\t\treturn item\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (ui *SelectUI) focus() {\n\tui.SetSelectable(true, false)\n}\n\nfunc (ui *SelectUI) blur() {\n\tui.SetSelectable(false, false)\n}\n\nfunc (ui *SelectUI) ClearView() {\n\tui.Clear()\n\tui.ClearSelected()\n}\n\nfunc (ui *SelectUI) ClearSelected() {\n\tui.selected = make(map[string]domain.Item)\n}\n"
  },
  {
    "path": "ui/ui.go",
    "content": "package ui\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/rivo/tview\"\n)\n\nvar (\n\tUI *ui\n)\n\ntype Primitive interface {\n\tfocus()\n\tblur()\n\ttview.Primitive\n}\n\ntype ui struct {\n\tapp          *tview.Application\n\tpages        *tview.Pages\n\tcurrent      int\n\tprimitives   []Primitive\n\tprimitiveLen int\n\tupdater      chan func()\n}\n\nfunc New() *ui {\n\tui := &ui{\n\t\tapp: tview.NewApplication(),\n\t}\n\n\tui.updater = make(chan func(), 100)\n\n\tUI = ui\n\n\treturn ui\n}\n\nfunc (ui *ui) canFocus() bool {\n\tfs := ui.app.GetFocus()\n\tif fs == nil {\n\t\treturn false\n\t}\n\tswitch fs.(type) {\n\tcase *FilterUI, *SelectUI, *ViewUI:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (ui *ui) toNextUI() {\n\tif !ui.canFocus() {\n\t\treturn\n\t}\n\tui.primitives[ui.current].blur()\n\tif ui.primitiveLen-1 > ui.current {\n\t\tui.current++\n\t} else {\n\t\tui.current = 0\n\t}\n\tp := ui.primitives[ui.current]\n\tp.focus()\n\tui.app.SetFocus(p)\n}\n\nfunc (ui *ui) toPrevUI() {\n\tif !ui.canFocus() {\n\t\treturn\n\t}\n\tui.primitives[ui.current].blur()\n\tif ui.current == 0 {\n\t\tui.current = ui.primitiveLen - 1\n\t} else {\n\t\tui.current--\n\t}\n\tp := ui.primitives[ui.current]\n\tp.focus()\n\tui.app.SetFocus(p)\n}\n\nfunc (ui *ui) Modal(p tview.Primitive, width, height int) tview.Primitive {\n\treturn tview.NewGrid().\n\t\tSetColumns(0, width, 0).\n\t\tSetRows(0, height, 0).\n\t\tAddItem(p, 1, 1, 1, 1, 0, 0, true)\n}\n\nfunc (ui *ui) Message(msg string, focusFunc func()) {\n\tmodal := tview.NewModal().\n\t\tSetText(msg).\n\t\tAddButtons([]string{\"OK\"}).\n\t\tSetDoneFunc(func(_ int, _ string) {\n\t\t\tui.pages.RemovePage(\"message\").ShowPage(\"main\")\n\t\t\tfocusFunc()\n\t\t})\n\tui.pages.AddAndSwitchToPage(\"message\", ui.Modal(modal, 80, 29), true).ShowPage(\"main\")\n}\n\nfunc (ui *ui) FullScreenPreview(contents string, focus func()) {\n\tCommonViewUI.SetText(contents).ScrollToBeginning()\n\tCommonViewUI.setFocus = focus\n\tgrid := tview.NewGrid().SetRows(0, 1).\n\t\tAddItem(CommonViewUI, 0, 0, 1, 1, 0, 0, true)\n\tui.pages.AddAndSwitchToPage(\"fullScreenPreview\", grid, true).ShowPage(\"main\")\n}\n\nfunc (ui *ui) Confirm(msg, doLabel string, doFunc func() error, focusFunc func()) {\n\tmodal := tview.NewModal().\n\t\tSetText(msg).\n\t\tAddButtons([]string{doLabel, \"Cancel\"}).\n\t\tSetDoneFunc(func(_ int, buttonLabel string) {\n\t\t\tui.pages.RemovePage(\"modal\").ShowPage(\"main\")\n\t\t\tfocusFunc()\n\t\t\tif buttonLabel == doLabel {\n\t\t\t\tif err := doFunc(); err != nil {\n\t\t\t\t\tui.Message(err.Error(), func() {\n\t\t\t\t\t\tfocusFunc()\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\tui.pages.AddAndSwitchToPage(\"modal\", ui.Modal(modal, 80, 29), true).ShowPage(\"main\")\n}\n\nfunc (ui *ui) Start() error {\n\tNewFilterUI()\n\tNewViewUI(UIKindIssueView)\n\tNewViewUI(UIKindCommentView)\n\tNewViewUI(UIKindCommonView)\n\tNewIssueUI()\n\tNewLabelsUI()\n\tNewMilestoneUI()\n\tNewProjectUI()\n\tNewAssignableUI()\n\tNewCommentUI()\n\tNewSearchUI()\n\n\tui.primitives = []Primitive{IssueFilterUI, AssigneesUI, LabelUI, MilestoneUI,\n\t\tProjectUI, IssueUI, IssueViewUI, CommentUI, CommentViewUI}\n\tui.primitiveLen = len(ui.primitives)\n\n\t// for readability\n\trow, col, rowSpan, colSpan := 0, 0, 0, 0\n\n\tgrid := tview.NewGrid().SetRows(1, 0, 0, 0, 0, 0, 0, 0, 0, 1).\n\t\tAddItem(IssueFilterUI, row, col, rowSpan+1, colSpan+3, 0, 0, true).\n\t\tAddItem(IssueUI, row+1, col+1, rowSpan+4, colSpan+3, 0, 0, true).\n\t\tAddItem(AssigneesUI, row+1, col, rowSpan+1, colSpan+1, 0, 0, true).\n\t\tAddItem(LabelUI, row+2, col, rowSpan+1, colSpan+1, 0, 0, true).\n\t\tAddItem(MilestoneUI, row+3, col, rowSpan+1, colSpan+1, 0, 0, true).\n\t\tAddItem(ProjectUI, row+4, col, rowSpan+1, colSpan+1, 0, 0, true).\n\t\tAddItem(CommentUI, row+5, col, rowSpan+4, colSpan+4, 0, 0, true).\n\t\tAddItem(IssueViewUI, row+1, col+4, rowSpan+4, colSpan+3, 0, 0, true).\n\t\tAddItem(CommentViewUI, row+5, col+4, rowSpan+4, colSpan+3, 0, 0, true).\n\t\tAddItem(SearchUI, row+9, col, rowSpan+1, colSpan+7, 0, 0, true)\n\n\tui.pages = tview.NewPages().\n\t\tAddAndSwitchToPage(\"main\", grid, true)\n\n\tui.app.SetRoot(ui.pages, true)\n\n\tui.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {\n\t\tswitch event.Key() {\n\t\tcase tcell.KeyCtrlN:\n\t\t\tUI.toNextUI()\n\t\tcase tcell.KeyCtrlP:\n\t\t\tUI.toPrevUI()\n\t\tcase tcell.KeyCtrlG:\n\t\t\tui.primitives[ui.current].blur()\n\t\t\tui.current = 5\n\t\t\tp := ui.primitives[ui.current]\n\t\t\tp.focus()\n\t\t\tui.app.SetFocus(IssueUI)\n\t\tcase tcell.KeyCtrlT:\n\t\t\tui.primitives[ui.current].blur()\n\t\t\tui.current = 0\n\t\t\tp := ui.primitives[ui.current]\n\t\t\tp.focus()\n\t\t\tui.app.SetFocus(IssueFilterUI)\n\t\t}\n\t\treturn event\n\t})\n\n\tui.current = 5\n\tui.app.SetFocus(IssueUI)\n\tIssueUI.focus()\n\n\tgo func() {\n\t\tfor f := range UI.updater {\n\t\t\tgo ui.app.QueueUpdateDraw(f)\n\t\t}\n\t}()\n\n\tif err := ui.app.Run(); err != nil {\n\t\tui.app.Stop()\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "ui/view.go",
    "content": "package ui\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/gdamore/tcell/v2\"\n\t\"github.com/rivo/tview\"\n\t\"github.com/skanehira/ght/utils\"\n)\n\nvar (\n\tIssueViewUI   *ViewUI\n\tCommentViewUI *ViewUI\n\tCommonViewUI  *ViewUI\n)\n\ntype ViewUI struct {\n\t*tview.TextView\n\tregionIndex  int\n\tregionLength int\n\tregionIDs    []string\n\tuiKind       UIKind\n\tsetFocus     func()\n}\n\nfunc NewViewUI(uiKind UIKind) {\n\tui := &ViewUI{\n\t\tTextView: tview.NewTextView(),\n\t\tuiKind:   uiKind,\n\t}\n\n\tui.SetBorder(true).SetTitle(string(uiKind)).SetTitleAlign(tview.AlignLeft)\n\tui.SetDynamicColors(true).SetWordWrap(false).SetRegions(true)\n\tui.SetBorderPadding(1, 1, 1, 1)\n\n\tvar setFocus func()\n\n\tswitch uiKind {\n\tcase UIKindIssueView:\n\t\tIssueViewUI = ui\n\t\tsetFocus = func() {\n\t\t\tUI.app.SetFocus(IssueViewUI)\n\t\t}\n\tcase UIKindCommentView:\n\t\tCommentViewUI = ui\n\t\tsetFocus = func() {\n\t\t\tUI.app.SetFocus(CommentViewUI)\n\t\t}\n\tcase UIKindCommonView:\n\t\tCommonViewUI = ui\n\t}\n\n\tsearchFunc := func(input string) {\n\t\ttext := ui.GetText(true)\n\t\tif input != \"\" {\n\t\t\tui.regionIDs, text = utils.Replace(text, input, `[#ff0000][\"%d\"]`+input+`[\"\"][white]`, -1)\n\t\t\tui.regionLength = len(ui.regionIDs)\n\t\t\tif ui.regionLength > 0 {\n\t\t\t\tui.regionIndex = 0\n\t\t\t\tui.Highlight(ui.regionIDs[0]).ScrollToHighlight()\n\t\t\t}\n\t\t}\n\n\t\tgo ui.updateView(text)\n\t}\n\n\tui.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {\n\t\tswitch event.Rune() {\n\t\tcase '/':\n\t\t\tSearchUI.SetText(\"\")\n\t\t\tSearchUI.SetSerachFunc(searchFunc)\n\t\t\tSearchUI.SetFocusFunc(func() {\n\t\t\t\tUI.app.SetFocus(ui)\n\t\t\t})\n\t\t\tUI.app.SetFocus(SearchUI)\n\t\tcase 'n':\n\t\t\tif ui.regionLength > 0 {\n\t\t\t\tui.regionIndex = (ui.regionIndex + 1) % ui.regionLength\n\t\t\t\tui.Highlight(strconv.Itoa(ui.regionIndex)).ScrollToHighlight()\n\t\t\t}\n\t\tcase 'N':\n\t\t\tif ui.regionLength > 0 {\n\t\t\t\tui.regionIndex = (ui.regionIndex - 1 + ui.regionLength) % ui.regionLength\n\t\t\t\tui.Highlight(strconv.Itoa(ui.regionIndex)).ScrollToHighlight()\n\t\t\t}\n\t\tcase 'o':\n\t\t\tif ui.uiKind == UIKindCommonView {\n\t\t\t\tUI.pages.SwitchToPage(\"main\")\n\t\t\t\tui.setFocus()\n\t\t\t\treturn event\n\t\t\t}\n\t\t\tUI.FullScreenPreview(ui.GetText(true), setFocus)\n\t\t}\n\n\t\t//switch event.Key() {\n\t\t//}\n\t\treturn event\n\t})\n\n}\n\nfunc (ui *ViewUI) updateView(text string) {\n\tUI.updater <- func() {\n\t\tui.SetText(text).ScrollToBeginning()\n\t\t//out, err := glamour.Render(text, \"dark\")\n\t\t//if err != nil {\n\t\t//\tout = err.Error()\n\t\t//}\n\t\t//ui.SetText(tview.TranslateANSI(out)).ScrollToBeginning()\n\t}\n}\n\nfunc (v *ViewUI) focus() {}\n\nfunc (v *ViewUI) blur() {}\n"
  },
  {
    "path": "utils/open.go",
    "content": "package utils\n\nimport (\n\t\"errors\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nfunc Open(url string) error {\n\targs := []string{}\n\tswitch runtime.GOOS {\n\tcase \"windows\":\n\t\tr := strings.NewReplacer(\"&\", \"^&\")\n\t\targs = []string{\"cmd\", \"start\", \"/\", r.Replace(url)}\n\tcase \"linux\":\n\t\targs = []string{\"xdg-open\", url}\n\tcase \"darwin\":\n\t\targs = []string{\"open\", url}\n\t}\n\n\tout, err := exec.Command(args[0], args[1:]...).CombinedOutput()\n\tif err != nil {\n\t\treturn errors.New(string(out))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "utils/strings.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\n// Replace is customized for this project\n// https://github.com/golang/go/blob/a8942d2cffd80c68febe1c908a0eb464d2f5bb40/src/strings/strings.go#L924\nfunc Replace(s, old, new string, n int) ([]string, string) {\n\tif old == new || n == 0 {\n\t\treturn nil, s // avoid allocation\n\t}\n\n\t// Compute number of replacements.\n\tif m := strings.Count(s, old); m == 0 {\n\t\treturn nil, s // avoid allocation\n\t} else if n < 0 || m < n {\n\t\tn = m\n\t}\n\n\t// Apply replacements to buffer.\n\tt := make([]byte, len(s)+n*(len(new)-len(old)))\n\tw := 0\n\tstart := 0\n\tvar regionIDs []string\n\tfor i := 0; i < n; i++ {\n\t\tj := start\n\t\tif len(old) == 0 {\n\t\t\tif i > 0 {\n\t\t\t\t_, wid := utf8.DecodeRuneInString(s[start:])\n\t\t\t\tj += wid\n\t\t\t}\n\t\t} else {\n\t\t\tj += strings.Index(s[start:], old)\n\t\t}\n\t\tw += copy(t[w:], s[start:j])\n\t\tw += copy(t[w:], fmt.Sprintf(new, i))\n\t\tregionIDs = append(regionIDs, strconv.Itoa(i))\n\t\tstart = j + len(old)\n\t}\n\tw += copy(t[w:], s[start:])\n\treturn regionIDs, string(t[0:w])\n}\n"
  },
  {
    "path": "utils/utils.go",
    "content": "package utils\n\nimport (\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\nfunc Edit(contents *string) error {\n\tf, err := ioutil.TempFile(\"\", \"*.md\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer os.Remove(f.Name())\n\n\tif *contents != \"\" {\n\t\tif _, err := io.Copy(f, strings.NewReader(*contents)); err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\t}\n\tf.Close()\n\n\teditor := os.Getenv(\"EDITOR\")\n\tif editor == \"\" {\n\t\teditor = \"vim\"\n\t}\n\tcmd := exec.Command(editor, f.Name())\n\tcmd.Stdin = os.Stdin\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tif err := cmd.Run(); err != nil {\n\t\treturn err\n\t}\n\n\tb, err := ioutil.ReadFile(f.Name())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnewContents := string(b)\n\t*contents = newContents\n\treturn nil\n}\n"
  }
]