├── .github ├── release.yml └── workflows │ ├── ci.yml │ └── tagpr.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .octocov.yml ├── .tagpr ├── CHANGELOG.md ├── CREDITS ├── LICENSE ├── Makefile ├── README.md ├── _EXTRA_CREDITS ├── cmd ├── completion.go ├── config.go ├── convert.go ├── edit.go ├── export.go ├── fill.go ├── import.go ├── init.go ├── list.go ├── man.go ├── repo.go ├── root.go └── version.go ├── config └── config.go ├── format ├── alfred │ ├── alfred.go │ └── alfred_test.go ├── format.go ├── frgm │ ├── frgm.go │ └── frgm_test.go ├── history │ ├── history.go │ └── history_test.go ├── list │ ├── list.go │ └── list_test.go ├── md │ ├── md.go │ ├── md_test.go │ └── templates │ │ └── snippets.md.tmpl └── pet │ ├── pet.go │ └── pet_test.go ├── go.mod ├── go.sum ├── img ├── frgm.afdesign └── frgm.png ├── main.go ├── snippet ├── snippet.go └── snippet_test.go ├── testdata ├── md_write_1.md.golden └── pet.toml └── version └── version.go /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - tagpr 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | job-test: 11 | name: Test 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out source code 15 | uses: actions/checkout@v3 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v3 19 | with: 20 | go-version-file: go.mod 21 | 22 | - name: Run lint 23 | uses: reviewdog/action-golangci-lint@v2 24 | 25 | - name: Run test 26 | run: make ci 27 | 28 | - name: Run octocov 29 | uses: k1LoW/octocov-action@v0 30 | -------------------------------------------------------------------------------- /.github/workflows/tagpr.yml: -------------------------------------------------------------------------------- 1 | name: tagpr 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | tagpr: 9 | runs-on: ubuntu-latest 10 | outputs: 11 | tagpr-tag: ${{ steps.run-tagpr.outputs.tag }} 12 | env: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | steps: 15 | - name: Check out source code 16 | uses: actions/checkout@v3 17 | 18 | - id: run-tagpr 19 | name: Run tagpr 20 | uses: Songmu/tagpr@v1 21 | 22 | release: 23 | needs: tagpr 24 | if: needs.tagpr.outputs.tagpr-tag != '' 25 | runs-on: macos-latest 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | steps: 29 | - name: Check out source code 30 | uses: actions/checkout@v3 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Set up Go 35 | uses: actions/setup-go@v3 36 | with: 37 | go-version-file: go.mod 38 | cache: true 39 | 40 | - name: Setup 41 | run: | 42 | brew install goreleaser 43 | 44 | - name: Release 45 | run: | 46 | make release 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .go-version 2 | dist/ 3 | coverage.out 4 | /frgm 5 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | fast: false 3 | enable: 4 | - misspell 5 | - gosec 6 | - godot 7 | - revive 8 | linters-settings: 9 | errcheck: 10 | check-type-assertions: true 11 | misspell: 12 | locale: US 13 | ignore-words: [] 14 | revive: 15 | rules: 16 | - name: unexported-return 17 | disabled: true 18 | - name: exported 19 | disabled: false 20 | issues: 21 | # include: 22 | # - EXC0012 23 | # - EXC0014 24 | exclude: 25 | - SA3000 26 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod download 4 | - go mod tidy 5 | builds: 6 | - 7 | id: frgm-linux 8 | env: 9 | - CGO_ENABLED=0 10 | goos: 11 | - linux 12 | ldflags: 13 | - -s -w -X github.com/k1LoW/frgm.version={{.Version}} -X github.com/k1LoW/frgm.commit={{.FullCommit}} -X github.com/k1LoW/frgm.date={{.Date}} -X github.com/k1LoW/frgm/version.Version={{.Version}} 14 | - 15 | id: frgm-darwin 16 | env: 17 | - CGO_ENABLED=0 18 | goos: 19 | - darwin 20 | goarch: 21 | - arm64 22 | - amd64 23 | ldflags: 24 | - -s -w -X github.com/k1LoW/frgm.version={{.Version}} -X github.com/k1LoW/frgm.commit={{.FullCommit}} -X github.com/k1LoW/frgm.date={{.Date}} -X github.com/k1LoW/frgm/version.Version={{.Version}} 25 | archives: 26 | - 27 | id: frgm-archive 28 | name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' 29 | format_overrides: 30 | - goos: darwin 31 | format: zip 32 | files: 33 | - LICENSE 34 | - CREDITS 35 | - README.md 36 | - CHANGELOG.md 37 | rlcp: true 38 | checksum: 39 | name_template: 'checksums.txt' 40 | snapshot: 41 | name_template: "{{ .Version }}-next" 42 | changelog: 43 | skip: true 44 | nfpms: 45 | - 46 | id: frgm-nfpms 47 | file_name_template: "{{ .ProjectName }}_{{ .Version }}-1_{{ .Arch }}" 48 | builds: 49 | - frgm-linux 50 | homepage: https://github.com/k1LoW/frgm 51 | maintainer: Ken'ichiro Oyama 52 | description: frgm is a meta snippet (fragment) manager. 53 | license: MIT 54 | formats: 55 | - deb 56 | - rpm 57 | bindir: /usr/bin 58 | epoch: 1 59 | -------------------------------------------------------------------------------- /.octocov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | if: true 3 | codeToTestRatio: 4 | code: 5 | - '**/*.go' 6 | - '!**/*_test.go' 7 | test: 8 | - '**/*_test.go' 9 | testExecutionTime: 10 | if: true 11 | diff: 12 | datastores: 13 | - artifact://${GITHUB_REPOSITORY} 14 | comment: 15 | if: is_pull_request 16 | summary: 17 | if: true 18 | report: 19 | if: is_default_branch 20 | datastores: 21 | - artifact://${GITHUB_REPOSITORY} 22 | -------------------------------------------------------------------------------- /.tagpr: -------------------------------------------------------------------------------- 1 | # config file for the tagpr in git config format 2 | # The tagpr generates the initial configuration, which you can rewrite to suit your environment. 3 | # CONFIGURATIONS: 4 | # tagpr.releaseBranch 5 | # Generally, it is "main." It is the branch for releases. The pcpr tracks this branch, 6 | # creates or updates a pull request as a release candidate, or tags when they are merged. 7 | # 8 | # tagpr.versionFile 9 | # Versioning file containing the semantic version needed to be updated at release. 10 | # It will be synchronized with the "git tag". 11 | # Often this is a meta-information file such as gemspec, setup.cfg, package.json, etc. 12 | # Sometimes the source code file, such as version.go or Bar.pm, is used. 13 | # If you do not want to use versioning files but only git tags, specify the "-" string here. 14 | # You can specify multiple version files by comma separated strings. 15 | # 16 | # tagpr.vPrefix 17 | # Flag whether or not v-prefix is added to semver when git tagging. (e.g. v1.2.3 if true) 18 | # This is only a tagging convention, not how it is described in the version file. 19 | # 20 | # tagpr.changelog (Optional) 21 | # Flag whether or not changelog is added or changed during the release. 22 | # 23 | # tagpr.command (Optional) 24 | # Command to change files just before release. 25 | # 26 | # tagpr.tmplate (Optional) 27 | # Pull request template in go template format 28 | # 29 | # tagpr.release (Optional) 30 | # GitHub Release creation behavior after tagging [true, draft, false] 31 | # If this value is not set, the release is to be created. 32 | [tagpr] 33 | vPrefix = true 34 | releaseBranch = main 35 | versionFile = version/version.go 36 | command = "make prerelease_for_tagpr" 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v0.11.0](https://github.com/k1LoW/frgm/compare/v0.10.0...v0.11.0) - 2023-04-04 4 | - Use octocov by @k1LoW in https://github.com/k1LoW/frgm/pull/31 5 | - Fix dead img link by @kenchan in https://github.com/k1LoW/frgm/pull/32 6 | - Update CI settings by @k1LoW in https://github.com/k1LoW/frgm/pull/33 7 | - Update go version and pkgs by @k1LoW in https://github.com/k1LoW/frgm/pull/35 8 | - Support arm64 by @k1LoW in https://github.com/k1LoW/frgm/pull/36 9 | 10 | ## [v0.11.0](https://github.com/k1LoW/frgm/compare/v0.10.0...v0.11.0) - 2023-04-04 11 | - Use octocov by @k1LoW in https://github.com/k1LoW/frgm/pull/31 12 | - Fix dead img link by @kenchan in https://github.com/k1LoW/frgm/pull/32 13 | - Update CI settings by @k1LoW in https://github.com/k1LoW/frgm/pull/33 14 | - Update go version and pkgs by @k1LoW in https://github.com/k1LoW/frgm/pull/35 15 | - Support arm64 by @k1LoW in https://github.com/k1LoW/frgm/pull/36 16 | 17 | ## [v0.10.0](https://github.com/k1LoW/frgm/compare/v0.9.0...v0.10.0) (2020-10-31) 18 | 19 | * Add `--rm` option [#30](https://github.com/k1LoW/frgm/pull/30) ([k1LoW](https://github.com/k1LoW)) 20 | 21 | ## [v0.9.0](https://github.com/k1LoW/frgm/compare/v0.8.4...v0.9.0) (2020-09-30) 22 | 23 | * Add `frgm repo pull` [#29](https://github.com/k1LoW/frgm/pull/29) ([k1LoW](https://github.com/k1LoW)) 24 | 25 | ## [v0.8.4](https://github.com/k1LoW/frgm/compare/v0.8.3...v0.8.4) (2020-08-28) 26 | 27 | * Fix `too many open files` [#28](https://github.com/k1LoW/frgm/pull/28) ([k1LoW](https://github.com/k1LoW)) 28 | 29 | ## [v0.8.3](https://github.com/k1LoW/frgm/compare/v0.8.2...v0.8.3) (2020-07-26) 30 | 31 | * Update completion [#27](https://github.com/k1LoW/frgm/pull/27) ([k1LoW](https://github.com/k1LoW)) 32 | 33 | ## [v0.8.2](https://github.com/k1LoW/frgm/compare/v0.8.1...v0.8.2) (2020-07-10) 34 | 35 | * Fix multi-line command support (#25) [#26](https://github.com/k1LoW/frgm/pull/26) ([k1LoW](https://github.com/k1LoW)) 36 | 37 | ## [v0.8.1](https://github.com/k1LoW/frgm/compare/v0.8.0...v0.8.1) (2020-07-10) 38 | 39 | * `frgm list` supports a multi-line command [#25](https://github.com/k1LoW/frgm/pull/25) ([k1LoW](https://github.com/k1LoW)) 40 | 41 | ## [v0.8.0](https://github.com/k1LoW/frgm/compare/v0.7.2...v0.8.0) (2020-06-25) 42 | 43 | * Add `frgm repo add` [#24](https://github.com/k1LoW/frgm/pull/24) ([k1LoW](https://github.com/k1LoW)) 44 | 45 | ## [v0.7.2](https://github.com/k1LoW/frgm/compare/v0.7.1...v0.7.2) (2020-06-01) 46 | 47 | * Fix parse error complex content [#23](https://github.com/k1LoW/frgm/pull/23) ([k1LoW](https://github.com/k1LoW)) 48 | 49 | ## [v0.7.1](https://github.com/k1LoW/frgm/compare/v0.7.0...v0.7.1) (2020-05-31) 50 | 51 | * Update goccy/go-yaml to v1.5.1 [#22](https://github.com/k1LoW/frgm/pull/22) ([k1LoW](https://github.com/k1LoW)) 52 | 53 | ## [v0.7.0](https://github.com/k1LoW/frgm/compare/v0.6.1...v0.7.0) (2020-05-25) 54 | 55 | * Add `--force` to `frgm init` for CI [#21](https://github.com/k1LoW/frgm/pull/21) ([k1LoW](https://github.com/k1LoW)) 56 | 57 | ## [v0.6.1](https://github.com/k1LoW/frgm/compare/v0.6.0...v0.6.1) (2020-05-22) 58 | 59 | * More fix markdown format [#20](https://github.com/k1LoW/frgm/pull/20) ([k1LoW](https://github.com/k1LoW)) 60 | 61 | ## [v0.6.0](https://github.com/k1LoW/frgm/compare/v0.5.1...v0.6.0) (2020-05-21) 62 | 63 | * Fix markdown format [#19](https://github.com/k1LoW/frgm/pull/19) ([k1LoW](https://github.com/k1LoW)) 64 | 65 | ## [v0.5.1](https://github.com/k1LoW/frgm/compare/v0.5.0...v0.5.1) (2020-04-24) 66 | 67 | * Support `:output` in --format option [#18](https://github.com/k1LoW/frgm/pull/18) ([k1LoW](https://github.com/k1LoW)) 68 | 69 | ## [v0.5.0](https://github.com/k1LoW/frgm/compare/v0.4.0...v0.5.0) (2020-04-24) 70 | 71 | * Add command `frgm man [UID]` [#17](https://github.com/k1LoW/frgm/pull/17) ([k1LoW](https://github.com/k1LoW)) 72 | 73 | ## [v0.4.0](https://github.com/k1LoW/frgm/compare/v0.3.0...v0.4.0) (2020-04-18) 74 | 75 | * Add frgm convert format `history` [#16](https://github.com/k1LoW/frgm/pull/16) ([k1LoW](https://github.com/k1LoW)) 76 | * Add command `frgm convert` [#15](https://github.com/k1LoW/frgm/pull/15) ([k1LoW](https://github.com/k1LoW)) 77 | * Add Decoder interface [#14](https://github.com/k1LoW/frgm/pull/14) ([k1LoW](https://github.com/k1LoW)) 78 | 79 | ## [v0.3.0](https://github.com/k1LoW/frgm/compare/v0.2.1...v0.3.0) (2020-04-12) 80 | 81 | * Display output: on markdown document. [#13](https://github.com/k1LoW/frgm/pull/13) ([k1LoW](https://github.com/k1LoW)) 82 | * Support pet [#12](https://github.com/k1LoW/frgm/pull/12) ([k1LoW](https://github.com/k1LoW)) 83 | * Fix: write to non-snippet YAML files [#11](https://github.com/k1LoW/frgm/pull/11) ([k1LoW](https://github.com/k1LoW)) 84 | 85 | ## [v0.2.1](https://github.com/k1LoW/frgm/compare/v0.2.0...v0.2.1) (2020-04-09) 86 | 87 | 88 | ## [v0.2.0](https://github.com/k1LoW/frgm/compare/v0.1.3...v0.2.0) (2020-04-09) 89 | 90 | * Display labels on markdown document. [#10](https://github.com/k1LoW/frgm/pull/10) ([k1LoW](https://github.com/k1LoW)) 91 | 92 | ## [v0.1.3](https://github.com/k1LoW/frgm/compare/v0.1.2...v0.1.3) (2020-04-08) 93 | 94 | 95 | ## [v0.1.2](https://github.com/k1LoW/frgm/compare/v0.1.1...v0.1.2) (2020-04-08) 96 | 97 | 98 | ## [v0.1.1](https://github.com/k1LoW/frgm/compare/v0.1.0...v0.1.1) (2020-04-06) 99 | 100 | * Fix template package (html -> text) [#9](https://github.com/k1LoW/frgm/pull/9) ([k1LoW](https://github.com/k1LoW)) 101 | 102 | ## [v0.1.0](https://github.com/k1LoW/frgm/compare/d6c896ee8ad7...v0.1.0) (2020-04-06) 103 | 104 | * Add export format `md` [#8](https://github.com/k1LoW/frgm/pull/8) ([k1LoW](https://github.com/k1LoW)) 105 | * Add command `frgm init` [#7](https://github.com/k1LoW/frgm/pull/7) ([k1LoW](https://github.com/k1LoW)) 106 | * Fix: lstat : no such file or directory [#6](https://github.com/k1LoW/frgm/pull/6) ([k1LoW](https://github.com/k1LoW)) 107 | * Add command `frgm list` [#5](https://github.com/k1LoW/frgm/pull/5) ([k1LoW](https://github.com/k1LoW)) 108 | * Change command options [#4](https://github.com/k1LoW/frgm/pull/4) ([k1LoW](https://github.com/k1LoW)) 109 | * Add command `frgm import` [#3](https://github.com/k1LoW/frgm/pull/3) ([k1LoW](https://github.com/k1LoW)) 110 | * Fix file close bug [#2](https://github.com/k1LoW/frgm/pull/2) ([k1LoW](https://github.com/k1LoW)) 111 | * Add command `frgm fill` [#1](https://github.com/k1LoW/frgm/pull/1) ([k1LoW](https://github.com/k1LoW)) 112 | 113 | ## [v0.0.8](https://github.com/k1LoW/frgm/compare/v0.0.7...v0.0.8) (2020-04-03) 114 | 115 | * Fix: lstat : no such file or directory [#6](https://github.com/k1LoW/frgm/pull/6) ([k1LoW](https://github.com/k1LoW)) 116 | 117 | ## [v0.0.7](https://github.com/k1LoW/frgm/compare/v0.0.6...v0.0.7) (2020-04-03) 118 | 119 | * Add command `frgm list` [#5](https://github.com/k1LoW/frgm/pull/5) ([k1LoW](https://github.com/k1LoW)) 120 | 121 | ## [v0.0.6](https://github.com/k1LoW/frgm/compare/v0.0.5...v0.0.6) (2020-04-02) 122 | 123 | * Change command options [#4](https://github.com/k1LoW/frgm/pull/4) ([k1LoW](https://github.com/k1LoW)) 124 | * Add command `frgm import` [#3](https://github.com/k1LoW/frgm/pull/3) ([k1LoW](https://github.com/k1LoW)) 125 | 126 | ## [v0.0.5](https://github.com/k1LoW/frgm/compare/v0.0.4...v0.0.5) (2020-04-02) 127 | 128 | 129 | ## [v0.0.4](https://github.com/k1LoW/frgm/compare/v0.0.3...v0.0.4) (2020-04-02) 130 | 131 | * Fix file close bug [#2](https://github.com/k1LoW/frgm/pull/2) ([k1LoW](https://github.com/k1LoW)) 132 | 133 | ## [v0.0.3](https://github.com/k1LoW/frgm/compare/v0.0.2...v0.0.3) (2020-04-01) 134 | 135 | * Add command `frgm fill` [#1](https://github.com/k1LoW/frgm/pull/1) ([k1LoW](https://github.com/k1LoW)) 136 | 137 | ## [v0.0.2](https://github.com/k1LoW/frgm/compare/v0.0.1...v0.0.2) (2020-03-31) 138 | 139 | 140 | ## [v0.0.1](https://github.com/k1LoW/frgm/compare/d6c896ee8ad7...v0.0.1) (2020-03-31) 141 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2020 Ken'ichiro Oyama 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG = github.com/k1LoW/frgm 2 | COMMIT = $$(git describe --tags --always) 3 | OSNAME=${shell uname -s} 4 | ifeq ($(OSNAME),Darwin) 5 | DATE = $$(gdate --utc '+%Y-%m-%d_%H:%M:%S') 6 | else 7 | DATE = $$(date --utc '+%Y-%m-%d_%H:%M:%S') 8 | endif 9 | 10 | export GO111MODULE=on 11 | 12 | BUILD_LDFLAGS = -X $(PKG).commit=$(COMMIT) -X $(PKG).date=$(DATE) 13 | 14 | default: test 15 | 16 | ci: depsdev test 17 | 18 | test: 19 | go test ./... -coverprofile=coverage.out -covermode=count 20 | 21 | lint: 22 | golangci-lint run ./... 23 | 24 | build: 25 | go build -ldflags="$(BUILD_LDFLAGS)" 26 | 27 | depsdev: 28 | go install github.com/Songmu/ghch/cmd/ghch@latest 29 | go install github.com/Songmu/gocredits/cmd/gocredits@latest 30 | 31 | prerelease: 32 | git push origin main --tag 33 | ghch -w -N ${VER} 34 | gocredits -skip-missing -w . 35 | cat _EXTRA_CREDITS >> CREDITS 36 | git add CHANGELOG.md CREDITS 37 | git commit -m'Bump up version number' 38 | git tag ${VER} 39 | 40 | prerelease_for_tagpr: 41 | gocredits -skip-missing -w . 42 | cat _EXTRA_CREDITS >> CREDITS 43 | git add CHANGELOG.md CREDITS go.mod go.sum 44 | 45 | release: 46 | git push origin main --tag 47 | goreleaser --clean 48 | 49 | .PHONY: default test 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # frgm [![Build Status](https://github.com/k1LoW/frgm/workflows/build/badge.svg)](https://github.com/k1LoW/frgm/actions) 2 | 3 | `frgm` is a meta snippet (fragment) manager. 4 | 5 |

6 |
7 | frgm 8 |
9 |

10 | 11 | Key features of `frgm` are: 12 | 13 | - **[Can export snippets in other snippet tool format](#export-frgm-snippets-as--snippets--frgm-export-)**. 14 | - **[Can document snippets](#export-snippets-as-markdown-document)**. 15 | - **[Can use as command-line snippets](##use-frgm-snippets--frgm-list-)**. 16 | 17 | ## Usage 18 | 19 | ### Initialize frgm ( `frgm init` ) 20 | 21 | Initialize frgm. 22 | 23 | - Create or update config.toml 24 | - Set and create `global.snippets_path` ( create file or make directory ) 25 | 26 | ``` console 27 | $ frgm init 28 | ``` 29 | 30 | ### Write snippets ( `frgm edit` ) 31 | 32 | Write snippets. 33 | 34 | The format of the frgm snippet is the following, 35 | 36 | ``` yaml 37 | --- 38 | # Default group name of snippets 39 | # Default is snippets file name 40 | group: my-group 41 | snippets: 42 | # Unique identifier of snippet 43 | # Default is automatically generated 44 | - uid: frgm-1ca779b751a5 45 | # Group name of snippet 46 | # Default is default group name of snippets 47 | group: command 48 | # Name of snippet 49 | name: Delete branch already merged 50 | # Content (command) of snippet 51 | content: git branch --merged | grep -v master | xargs git branch -d 52 | # Description of snippet 53 | desc: | 54 | 1. lists the merged branches 55 | 2. delete all merged branches except the master branch 56 | ref: https://example.com/path/to/link 57 | # Labels 58 | labels: 59 | - git 60 | - cleanup 61 | - name: ping 62 | content: ping 8.8.8.8 63 | [...] 64 | ``` 65 | 66 | You can use the `frgm edit` command to edit snippets of `global.snippets_path` using the editor specified in $EDITOR. 67 | 68 | ``` console 69 | $ EDITOR=emacs frgm edit 70 | ``` 71 | 72 | ### Export frgm snippets as * snippets ( `frgm export` ) 73 | 74 | #### Export snippets as [Alfred](https://www.alfredapp.com/) snippets 75 | 76 | ``` console 77 | $ frgm export --to ~/Library/Application Support/Alfred/Alfred.alfredpreferences/snippets --format alfred 78 | ``` 79 | 80 | #### Export snippets as [pet](https://github.com/knqyf263/pet) snippets 81 | 82 | ``` console 83 | $ frgm export --to /path/to/pet.toml --format pet 84 | ``` 85 | 86 | #### Export snippets as Markdown document. 87 | 88 | ``` console 89 | $ frgm export --to /path/to/snippets.md --format md 90 | ``` 91 | 92 | **Key Mapping:** 93 | 94 | | frgm snippet key | Default / Required | [Alfred](https://www.alfredapp.com/) | [pet](https://github.com/knqyf263/pet) | Markdown | 95 | | --- | --- | --- | --- | --- | 96 | | `uid:` | Default is automatically generated | `uid` | - | use as link.hash | 97 | | `group:` | Default is default group name of snippets or file name | directory | - | use | 98 | | `name:` | required | `name` | `description:` | use | 99 | | `desc:` | | - | - | use | 100 | | `content:` | required | `snippet` | `command:` | use | 101 | | `output:` | | - | `output:` | use | 102 | | `labels:` | | `keyword` | `tag:` | use | 103 | 104 | #### Fill uid, group ( `frgm fill` ) 105 | 106 | Fill and freeze `uid:` and `group:`. 107 | 108 | ``` console 109 | $ frgm fill 110 | ``` 111 | 112 | **before:** 113 | 114 | ``` yaml 115 | # my-group.yml 116 | snippets: 117 | - name: ping 118 | content: ping 8.8.8.8 119 | ``` 120 | 121 | **after:** 122 | 123 | ``` yaml 124 | # my-group.yml 125 | snippets: 126 | - uid: frgm-6aa9d75f9d83 127 | group: my-group 128 | name: ping 129 | content: ping 8.8.8.8 130 | ``` 131 | 132 | ### Add snippets repository ( `frgm repo add` ) 133 | 134 | Add frgm snippets repository. 135 | 136 | ``` console 137 | $ frgm repo add https://github.com/k1LoW/sample-frgm-snippets.git 138 | ``` 139 | 140 | ### Execute `git pull` in all snippets repositories ( `frgm repo pull` ) 141 | 142 | Execute `git pull` in all snippets repositories. 143 | 144 | ``` console 145 | $ frgm repo pull 146 | ``` 147 | 148 | ### Import * snippets ( `frgm import` ) 149 | 150 | #### Import [Alfred](https://www.alfredapp.com/) snippets 151 | 152 | ``` console 153 | $ frgm import --from ~/Library/Application Support/Alfred/Alfred.alfredpreferences/snippets --format alfred 154 | ``` 155 | 156 | ### Convert * snippets ( `frgm convert` ) 157 | 158 | #### Convert `history` output into frgm snippets 159 | 160 | ``` console 161 | $ history | tail -1 162 | 21344 frgm convert --format history --group from-history # Delete merged branch 163 | $ history | tail -1 | frgm convert --format history --group from-history 164 | snippets: 165 | - uid: history-8cc0c8477ec0 166 | group: from-history 167 | name: Delete merged branch 168 | content: git branch --merged | grep -v master | xargs git branch -d 169 | ``` 170 | 171 | #### Convert [Alfred](https://www.alfredapp.com/) snippet file into frgm snippet 172 | 173 | ``` console 174 | $ cat /path/to/alfredsnippet.json | frgm convert --format alfred --group search 175 | snippets: 176 | - uid: frgm-46c29e119523 177 | group: search 178 | name: Search log file 179 | content: lsof -c nginx | grep -v .so | grep .log | awk '{print $9}' | sort | uniq 180 | labels: 181 | - log 182 | ``` 183 | 184 | #### Convert [pet](https://github.com/knqyf263/pet) snippets file into frgm snippets 185 | 186 | ``` console 187 | $ cat /path/to/pet-snippets.toml. | frgm convert --format pet --group pet 188 | snippets: 189 | - uid: pet-df7bb29f9681 190 | group: pet 191 | name: ping 192 | content: ping 8.8.8.8 193 | labels: 194 | - network 195 | - google 196 | - uid: pet-584a331fd6b0 197 | group: pet 198 | name: hello 199 | content: echo hello 200 | output: hello 201 | labels: 202 | - sample 203 | ``` 204 | 205 | ### Use frgm snippets ( `frgm list` ) 206 | 207 | ``` console 208 | $ frgm list 209 | ``` 210 | 211 | #### zsh auto-complete from snippets using [fzf](https://github.com/junegunn/fzf) (Ctrl+j) 212 | 213 | ``` zsh 214 | function fzf-select-snippets() { 215 | BUFFER=$(frgm list --format ':content # :name [:group :labels] :uid' | fzf --reverse --border --preview "echo {} | rev | cut -f 1 -d ' ' | rev | frgm man") 216 | CURSOR=$#BUFFER 217 | zle clear-screen 218 | } 219 | zle -N fzf-select-snippets 220 | bindkey '^j' fzf-select-snippets 221 | ``` 222 | 223 | #### zsh auto-complete from snippets using [peco](https://github.com/peco/peco) (Ctrl+j) 224 | 225 | ``` zsh 226 | function peco-select-snippets() { 227 | BUFFER=$(frgm list | peco --query "$LBUFFER") 228 | CURSOR=$#BUFFER 229 | zle clear-screen 230 | } 231 | zle -N peco-select-snippets 232 | bindkey '^j' peco-select-snippets 233 | ``` 234 | 235 | #### zsh auto-complete from history and snippets using [peco](https://github.com/peco/peco) (Ctrl+r) 236 | 237 | ``` zsh 238 | function peco-select-history-and-snippets() { 239 | BUFFER=$((history -r -n 1 & frgm list) | peco --query "$LBUFFER") 240 | CURSOR=$#BUFFER 241 | zle clear-screen 242 | } 243 | zle -N peco-select-history-and-snippets 244 | bindkey '^R' peco-select-history-and-snippets 245 | ``` 246 | 247 | ## Install 248 | 249 | **homebrew tap:** 250 | 251 | ```console 252 | $ brew install k1LoW/tap/frgm 253 | ``` 254 | 255 | **manually:** 256 | 257 | Download binany from [releases page](https://github.com/k1LoW/frgm/releases) 258 | 259 | **go get:** 260 | 261 | ```console 262 | $ go get github.com/k1LoW/frgm 263 | ``` 264 | -------------------------------------------------------------------------------- /_EXTRA_CREDITS: -------------------------------------------------------------------------------- 1 | 2 | github.com/whilp/git-urls 3 | https://github.com/whilp/git-urls 4 | ---------------------------------------------------------------- 5 | 6 | Copyright (c) 2015 Will Maier 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | ================================================================ 27 | -------------------------------------------------------------------------------- /cmd/completion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "fmt" 26 | "os" 27 | 28 | "github.com/spf13/cobra" 29 | ) 30 | 31 | var out string 32 | 33 | // completionCmd represents the completion command 34 | var completionCmd = &cobra.Command{ 35 | Use: "completion", 36 | Short: "Output shell completion code", 37 | Long: `Output shell completion code. 38 | To configure your shell to load completions for each session 39 | 40 | # bash 41 | echo '. <(frgm completion bash)' > ~/.bashrc 42 | 43 | # zsh 44 | frgm completion zsh > $fpath[1]/_frgm 45 | 46 | # fish 47 | frgm completion fish ~/.config/fish/completions/frgm.fish 48 | `, 49 | ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, 50 | Args: func(cmd *cobra.Command, args []string) error { 51 | if len(args) != 1 { 52 | return fmt.Errorf("accepts 1 arg, received %d", len(args)) 53 | } 54 | if err := cobra.OnlyValidArgs(cmd, args); err != nil { 55 | return err 56 | } 57 | return nil 58 | }, 59 | RunE: func(cmd *cobra.Command, args []string) error { 60 | var ( 61 | o *os.File 62 | err error 63 | ) 64 | sh := args[0] 65 | if out == "" { 66 | o = os.Stdout 67 | } else { 68 | o, err = os.Create(out) 69 | if err != nil { 70 | return err 71 | } 72 | } 73 | 74 | switch sh { 75 | case "bash": 76 | if err := cmd.Root().GenBashCompletion(o); err != nil { 77 | _ = o.Close() 78 | return err 79 | } 80 | case "zsh": 81 | if err := cmd.Root().GenZshCompletion(o); err != nil { 82 | _ = o.Close() 83 | return err 84 | } 85 | case "fish": 86 | if err := cmd.Root().GenFishCompletion(o, true); err != nil { 87 | _ = o.Close() 88 | return err 89 | } 90 | case "powershell": 91 | if err := cmd.Root().GenPowerShellCompletion(o); err != nil { 92 | _ = o.Close() 93 | return err 94 | } 95 | } 96 | if err := o.Close(); err != nil { 97 | return err 98 | } 99 | return nil 100 | }, 101 | } 102 | 103 | func init() { 104 | rootCmd.AddCommand(completionCmd) 105 | completionCmd.Flags().StringVarP(&out, "out", "o", "", "output file path") 106 | } 107 | -------------------------------------------------------------------------------- /cmd/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "errors" 26 | 27 | "github.com/k1LoW/frgm/config" 28 | "github.com/spf13/cobra" 29 | "github.com/spf13/viper" 30 | ) 31 | 32 | // configCmd represents the config command 33 | var configCmd = &cobra.Command{ 34 | Use: "config", 35 | Short: "Get and set frgm config", 36 | Long: `Get and set frgm config.`, 37 | RunE: runConfig, 38 | } 39 | 40 | func runConfig(cmd *cobra.Command, args []string) error { 41 | switch { 42 | case len(args) == 0: 43 | for k, v := range viper.AllSettings() { 44 | switch v := v.(type) { 45 | case map[string]interface{}: 46 | for kk, vv := range v { 47 | cmd.Printf("%s.%s=%v\n", k, kk, vv) 48 | } 49 | default: 50 | cmd.Printf("%s=%v\n", k, v) 51 | } 52 | } 53 | case len(args) == 1: 54 | cmd.Printf("%v\n", viper.Get(args[0])) 55 | case len(args) == 2: 56 | if err := config.Set(args[0], args[1]); err != nil { 57 | return err 58 | } 59 | if err := config.Save(); err != nil { 60 | return err 61 | } 62 | default: 63 | return errors.New("invalid arguments") 64 | } 65 | return nil 66 | } 67 | 68 | func init() { 69 | rootCmd.AddCommand(configCmd) 70 | } 71 | -------------------------------------------------------------------------------- /cmd/convert.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "errors" 26 | "fmt" 27 | "os" 28 | 29 | "github.com/k1LoW/frgm/config" 30 | "github.com/k1LoW/frgm/format" 31 | "github.com/k1LoW/frgm/format/alfred" 32 | "github.com/k1LoW/frgm/format/frgm" 33 | "github.com/k1LoW/frgm/format/history" 34 | "github.com/k1LoW/frgm/format/pet" 35 | "github.com/spf13/cobra" 36 | ) 37 | 38 | var group string 39 | 40 | // convertCmd represents the convert command 41 | var convertCmd = &cobra.Command{ 42 | Use: "convert", 43 | Short: "Convert other app snippet format to frgm snippet format using STDIN/STDOUT", 44 | Long: `Convert other app snippet format to frgm snippet format using STDIN/STDOUT.`, 45 | Args: func(cmd *cobra.Command, args []string) error { 46 | fi, err := os.Stdin.Stat() 47 | if err != nil { 48 | return err 49 | } 50 | if (fi.Mode() & os.ModeCharDevice) != 0 { 51 | return errors.New("frgm need STDIN. Please use pipe") 52 | } 53 | return nil 54 | }, 55 | RunE: runConvert, 56 | } 57 | 58 | func runConvert(cmd *cobra.Command, args []string) error { 59 | var ( 60 | decoder format.Decoder 61 | encoder format.Encoder 62 | ) 63 | encoder = frgm.New(config.GetStringSlice("global.ignore")) 64 | 65 | switch formatType { 66 | case "alfred": 67 | decoder = alfred.New(config.GetStringSlice("global.ignore")) 68 | case "pet": 69 | decoder = pet.New() 70 | case "history": 71 | decoder = history.New() 72 | default: 73 | return fmt.Errorf("unsupported format '%s'", formatType) 74 | } 75 | 76 | snippets, err := decoder.Decode(os.Stdin, group) 77 | if err != nil { 78 | return err 79 | } 80 | if err := encoder.Encode(os.Stdout, snippets); err != nil { 81 | return err 82 | } 83 | 84 | return nil 85 | } 86 | 87 | func init() { 88 | rootCmd.AddCommand(convertCmd) 89 | convertCmd.Flags().StringVarP(&group, "group", "g", "default", "group of snippets") 90 | convertCmd.Flags().StringVarP(&formatType, "format", "T", "alfred", "format of STDIN snippet") 91 | } 92 | -------------------------------------------------------------------------------- /cmd/edit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "errors" 26 | "os" 27 | "os/exec" 28 | 29 | "github.com/k1LoW/frgm/config" 30 | "github.com/mattn/go-tty" 31 | "github.com/spf13/cobra" 32 | ) 33 | 34 | // editCmd represents the edit command 35 | var editCmd = &cobra.Command{ 36 | Use: "edit", 37 | Short: "Edit global.snippets_path using $EDITOR", 38 | Long: `Edit global.snippets_path using $EDITOR.`, 39 | RunE: runEdit, 40 | } 41 | 42 | func runEdit(cmd *cobra.Command, args []string) error { 43 | e := os.Getenv("EDITOR") 44 | if e == "" { 45 | return errors.New("$EDITOR is not set") 46 | } 47 | tty, err := tty.Open() 48 | if err != nil { 49 | return err 50 | } 51 | defer tty.Close() 52 | c := exec.Command(e, config.GetString("global.snippets_path")) // #nosec 53 | c.Stdin = tty.Input() 54 | c.Stdout = tty.Output() 55 | c.Stderr = tty.Output() 56 | if err := c.Run(); err != nil { 57 | return err 58 | } 59 | return nil 60 | } 61 | 62 | func init() { 63 | rootCmd.AddCommand(editCmd) 64 | } 65 | -------------------------------------------------------------------------------- /cmd/export.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "fmt" 26 | "os" 27 | "path/filepath" 28 | 29 | "github.com/k1LoW/frgm/config" 30 | "github.com/k1LoW/frgm/format" 31 | "github.com/k1LoW/frgm/format/alfred" 32 | "github.com/k1LoW/frgm/format/frgm" 33 | "github.com/k1LoW/frgm/format/md" 34 | "github.com/k1LoW/frgm/format/pet" 35 | "github.com/spf13/cobra" 36 | ) 37 | 38 | // exportCmd represents the export command 39 | var exportCmd = &cobra.Command{ 40 | Use: "export", 41 | Short: "Export frgm snippets as other app snippets", 42 | Long: `Export frgm snippets as othre app snippets.`, 43 | RunE: runExport, 44 | } 45 | 46 | func runExport(cmd *cobra.Command, args []string) error { 47 | var ( 48 | loader format.Loader 49 | exporter format.Exporter 50 | ) 51 | loader = frgm.New(config.GetStringSlice("global.ignore")) 52 | 53 | switch formatType { 54 | case "alfred": 55 | exporter = alfred.New(config.GetStringSlice("global.ignore")) 56 | case "pet": 57 | exporter = pet.New() 58 | case "md": 59 | exporter = md.New(config.GetStringSlice("global.ignore")) 60 | default: 61 | return fmt.Errorf("unsupported format '%s'", formatType) 62 | } 63 | 64 | if srcPath == "" { 65 | srcPath = config.GetString("global.snippets_path") 66 | } 67 | 68 | snippets, err := loader.Load(srcPath) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | if err := snippets.Validate(); err != nil { 74 | return err 75 | } 76 | 77 | if rmDest { 78 | files, err := os.ReadDir(destPath) 79 | if err != nil { 80 | return err 81 | } 82 | for _, f := range files { 83 | if err := os.RemoveAll(filepath.Join(destPath, f.Name())); err != nil { 84 | return err 85 | } 86 | } 87 | } 88 | 89 | err = exporter.Export(snippets, destPath) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | return nil 95 | } 96 | 97 | func init() { 98 | config.Load() 99 | rootCmd.AddCommand(exportCmd) 100 | exportCmd.Flags().StringVarP(&srcPath, "from", "f", config.GetString("global.snippets_path"), "frgm snippets path") 101 | exportCmd.Flags().StringVarP(&destPath, "to", "t", "", "export snippets path") 102 | exportCmd.Flags().StringVarP(&formatType, "format", "T", "alfred", "export format of snippet") 103 | exportCmd.Flags().BoolVarP(&rmDest, "rm", "", false, "remove all files in export snippets path before exporting snippets") 104 | } 105 | -------------------------------------------------------------------------------- /cmd/fill.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "github.com/k1LoW/frgm/config" 26 | "github.com/k1LoW/frgm/format/frgm" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | // fillCmd represents the fill command 31 | var fillCmd = &cobra.Command{ 32 | Use: "fill", 33 | Short: "Fill in the blanks in current snippets", 34 | Long: `Fill in the blanks in current snippets.`, 35 | RunE: runFill, 36 | } 37 | 38 | func runFill(cmd *cobra.Command, args []string) error { 39 | srcPath = config.GetString("global.snippets_path") 40 | exporter := frgm.New(config.GetStringSlice("global.ignore")) 41 | snippets, err := exporter.Load(srcPath) 42 | if err != nil { 43 | return err 44 | } 45 | err = exporter.Export(snippets, srcPath) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func init() { 54 | rootCmd.AddCommand(fillCmd) 55 | } 56 | -------------------------------------------------------------------------------- /cmd/import.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "fmt" 26 | 27 | "github.com/k1LoW/frgm/config" 28 | "github.com/k1LoW/frgm/format" 29 | "github.com/k1LoW/frgm/format/alfred" 30 | "github.com/k1LoW/frgm/format/frgm" 31 | "github.com/spf13/cobra" 32 | ) 33 | 34 | // importCmd represents the import command 35 | var importCmd = &cobra.Command{ 36 | Use: "import", 37 | Short: "Import other app snippets as frgm snippets", 38 | Long: `Import other app snippets as frgm snippets.`, 39 | RunE: runImport, 40 | } 41 | 42 | func runImport(cmd *cobra.Command, args []string) error { 43 | destPath = config.GetString("global.snippets_path") 44 | var ( 45 | loader format.Loader 46 | exporter format.Exporter 47 | ) 48 | switch formatType { 49 | case "alfred": 50 | loader = alfred.New(config.GetStringSlice("global.ignore")) 51 | default: 52 | return fmt.Errorf("unsupported format '%s'", formatType) 53 | } 54 | exporter = frgm.New(config.GetStringSlice("global.ignore")) 55 | 56 | snippets, err := loader.Load(srcPath) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | if err := snippets.Validate(); err != nil { 62 | return err 63 | } 64 | 65 | err = exporter.Export(snippets, destPath) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | return nil 71 | } 72 | 73 | func init() { 74 | rootCmd.AddCommand(importCmd) 75 | importCmd.Flags().StringVarP(&srcPath, "from", "f", "", "import snippets path") 76 | importCmd.Flags().StringVarP(&formatType, "format", "T", "alfred", "import snippets format of snippet") 77 | } 78 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "fmt" 26 | "os" 27 | "path/filepath" 28 | 29 | "github.com/Songmu/prompter" 30 | "github.com/k1LoW/frgm/config" 31 | "github.com/k1LoW/frgm/format/frgm" 32 | "github.com/spf13/cobra" 33 | ) 34 | 35 | var force bool 36 | 37 | // initCmd represents the init command 38 | var initCmd = &cobra.Command{ 39 | Use: "init", 40 | Short: "Initialize frgm", 41 | Long: `Initialize frgm.`, 42 | RunE: runInit, 43 | } 44 | 45 | func runInit(cmd *cobra.Command, args []string) error { 46 | path := config.GetString("global.snippets_path") 47 | if !force { 48 | path = prompter.Prompt("Enter snippet path (dir or .yml file) [global.snippets_path]", path) 49 | } 50 | if err := config.Set("global.snippets_path", path); err != nil { 51 | return err 52 | } 53 | _, err := os.Stat(path) 54 | if err != nil { 55 | ext := filepath.Ext(path) 56 | if frgm.AllowExts.Contains(ext) { 57 | var yn bool 58 | if force { 59 | yn = true 60 | } else { 61 | yn = prompter.YN(fmt.Sprintf("Create new snippet file (%s) ?", path), true) 62 | } 63 | if yn { 64 | if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { 65 | return err 66 | } 67 | if err := os.WriteFile(path, []byte("---\nsnippets: []\n"), 0600); err != nil { 68 | return err 69 | } 70 | } 71 | } else { 72 | var yn bool 73 | if force { 74 | yn = true 75 | } else { 76 | yn = prompter.YN(fmt.Sprintf("Create snippets directory (%s)?", path), true) 77 | } 78 | if yn { 79 | if err := os.MkdirAll(path, 0700); err != nil { 80 | return err 81 | } 82 | } 83 | } 84 | cmd.Printf("Create %s\n", path) 85 | } 86 | 87 | if err := config.Save(); err != nil { 88 | return err 89 | } 90 | 91 | return nil 92 | } 93 | 94 | func init() { 95 | rootCmd.AddCommand(initCmd) 96 | initCmd.Flags().BoolVarP(&force, "force", "f", false, "force init") 97 | } 98 | -------------------------------------------------------------------------------- /cmd/list.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "os" 26 | 27 | "github.com/k1LoW/frgm/config" 28 | "github.com/k1LoW/frgm/format/frgm" 29 | "github.com/k1LoW/frgm/format/list" 30 | "github.com/spf13/cobra" 31 | ) 32 | 33 | var listFormat string 34 | 35 | // listCmd represents the list command 36 | var listCmd = &cobra.Command{ 37 | Use: "list", 38 | Short: "List snippets", 39 | Long: `List snippets.`, 40 | RunE: runList, 41 | } 42 | 43 | func runList(cmd *cobra.Command, args []string) error { 44 | srcPath = config.GetString("global.snippets_path") 45 | loader := frgm.New(config.GetStringSlice("global.ignore")) 46 | snippets, err := loader.Load(srcPath) 47 | if err != nil { 48 | return err 49 | } 50 | encoder := list.New(listFormat) 51 | return encoder.Encode(os.Stdout, snippets) 52 | } 53 | 54 | func init() { 55 | rootCmd.AddCommand(listCmd) 56 | listCmd.Flags().StringVarP(&listFormat, "format", "", list.DefaultFormat, "list format") 57 | } 58 | -------------------------------------------------------------------------------- /cmd/man.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "io" 26 | "os" 27 | "strings" 28 | "text/template" 29 | 30 | "github.com/k1LoW/frgm/config" 31 | "github.com/k1LoW/frgm/format/frgm" 32 | "github.com/labstack/gommon/color" 33 | "github.com/mattn/go-isatty" 34 | "github.com/pkg/errors" 35 | "github.com/spf13/cobra" 36 | ) 37 | 38 | // manCmd represents the man command 39 | var manCmd = &cobra.Command{ 40 | Use: "man [UID]", 41 | Short: "Show command man", 42 | Long: `Show command man.`, 43 | Args: func(cmd *cobra.Command, args []string) error { 44 | if len(args) == 1 { 45 | return nil 46 | } 47 | if isatty.IsTerminal(os.Stdin.Fd()) { 48 | return errors.New("accepts 1 arg(s), received 0") 49 | } 50 | return nil 51 | }, 52 | RunE: runMan, 53 | } 54 | 55 | func runMan(cmd *cobra.Command, args []string) error { 56 | var uid string 57 | if len(args) == 1 { 58 | uid = args[0] 59 | } else { 60 | stdin, err := io.ReadAll(os.Stdin) 61 | if err != nil { 62 | return err 63 | } 64 | uid = strings.TrimSpace(string(stdin)) 65 | } 66 | loader := frgm.New(config.GetStringSlice("global.ignore")) 67 | 68 | if srcPath == "" { 69 | srcPath = config.GetString("global.snippets_path") 70 | } 71 | snippets, err := loader.Load(srcPath) 72 | if err != nil { 73 | return err 74 | } 75 | s, err := snippets.FindByUID(uid) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | tmpl := template.Must(template.New("man").Funcs(funcs()).Parse(`{{ "NAME" | bold }} 81 | {{ .snippet.Name }} 82 | 83 | {{ "CONTENT" | bold }} 84 | {{ .snippet.Content | nlindent | bold }} 85 | 86 | {{ if ne .snippet.Output "" }}{{ "OUTPUT" | bold }} 87 | {{ .snippet.Output | nlindent }} 88 | 89 | {{ end }}{{ if ne .snippet.Desc "" }}{{ "DESCRIPTION" | bold }} 90 | {{ .snippet.Desc | nlindent }} 91 | 92 | {{ end }}{{ if ne .snippet.Group "" }}{{ "GROUP" | bold }} 93 | {{ .snippet.Group }} 94 | 95 | {{ end }}{{ if gt (len .snippet.Labels) 0 }}{{ "LABELS" | bold }} 96 | {{ .snippet.Labels | labels}} 97 | 98 | {{ end }}{{ if ne .snippet.LoadPath "" }}{{ "LOAD PATH" | bold }} 99 | {{ .snippet.LoadPath }} 100 | {{ end }} 101 | `)) 102 | 103 | templateData := map[string]interface{}{ 104 | "snippet": s, 105 | } 106 | err = tmpl.Execute(os.Stdout, templateData) 107 | if err != nil { 108 | return errors.WithStack(err) 109 | } 110 | return nil 111 | } 112 | 113 | func funcs() map[string]interface{} { 114 | return map[string]interface{}{ 115 | "nlindent": func(text string) string { 116 | r := strings.NewReplacer("\n", "\n ", "\r\n", "\r\n ", "\r", "\r ") 117 | return r.Replace(text) 118 | }, 119 | "bold": func(text string) string { 120 | return color.Bold(text) 121 | }, 122 | "labels": func(labels []string) string { 123 | return strings.Join(labels, ", ") 124 | }, 125 | } 126 | } 127 | 128 | func init() { 129 | config.Load() 130 | rootCmd.AddCommand(manCmd) 131 | manCmd.Flags().StringVarP(&srcPath, "from", "f", config.GetString("global.snippets_path"), "frgm snippets path") 132 | } 133 | -------------------------------------------------------------------------------- /cmd/repo.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "fmt" 26 | "os" 27 | "os/exec" 28 | "path/filepath" 29 | "strings" 30 | 31 | "github.com/Songmu/prompter" 32 | "github.com/k1LoW/frgm/config" 33 | "github.com/spf13/cobra" 34 | giturl "github.com/whilp/git-urls" 35 | "github.com/x-motemen/ghq/cmdutil" 36 | ) 37 | 38 | // repoCmd represents the repo command 39 | var repoCmd = &cobra.Command{ 40 | Use: "repo", 41 | Short: "Manage snippets repositories", 42 | Long: `Manage snippets repositories.`, 43 | } 44 | 45 | var repoAddCmd = &cobra.Command{ 46 | Use: "add [REPO_URL]", 47 | Short: "Add snippets repository to 'global.snippets_path'", 48 | Long: `Add snippets repository 'global.snippets_path'.`, 49 | Args: cobra.ExactArgs(1), 50 | RunE: func(cmd *cobra.Command, args []string) error { 51 | repoURL := args[0] 52 | rootPath := config.GetString("global.snippets_path") 53 | _, err := exec.LookPath("ghq") 54 | if err == nil && prompter.YN("Do you use ghq?", true) { 55 | return addUsingGhq(cmd, repoURL, rootPath) 56 | } 57 | return addDirect(cmd, repoURL, rootPath) 58 | }, 59 | } 60 | 61 | var repoPullCmd = &cobra.Command{ 62 | Use: "pull", 63 | Short: "Execute 'git pull' in all snippets repositories", 64 | Long: `Execute 'git pull' in all snippets repositories.`, 65 | Args: cobra.NoArgs, 66 | RunE: func(cmd *cobra.Command, args []string) error { 67 | rootPath := config.GetString("global.snippets_path") 68 | files, err := os.ReadDir(rootPath) 69 | if err != nil { 70 | return err 71 | } 72 | for _, d := range files { 73 | dotGitDir := filepath.Join(rootPath, d.Name(), ".git") 74 | if _, err := os.Stat(dotGitDir); err != nil { 75 | continue 76 | } 77 | repoPath := filepath.Join(rootPath, d.Name()) 78 | cmd.Println(fmt.Sprintf("Pull %s", repoPath)) 79 | c := exec.Command("git", "pull") 80 | c.Stdout = os.Stderr 81 | c.Stderr = os.Stderr 82 | c.Dir = repoPath 83 | if err := cmdutil.RunCommand(c, true); err != nil { 84 | return err 85 | } 86 | } 87 | return nil 88 | }, 89 | } 90 | 91 | func addDirect(cmd *cobra.Command, repoURL, rootPath string) error { 92 | u, err := giturl.Parse(repoURL) 93 | if err != nil { 94 | return err 95 | } 96 | repoPath := filepath.Join(rootPath, strings.ReplaceAll(filepath.Join(u.Host, strings.TrimRight(u.Path, ".git")), "/", "__")) 97 | if _, err := os.Stat(repoPath); err == nil { 98 | return fmt.Errorf("%s already exists", repoPath) 99 | } 100 | if err := os.MkdirAll(repoPath, 0750); err != nil { 101 | return err 102 | } 103 | return cmdutil.Run("git", "clone", repoURL, repoPath) 104 | } 105 | 106 | func addUsingGhq(cmd *cobra.Command, repoURL, rootPath string) error { 107 | u, err := giturl.Parse(repoURL) 108 | if err != nil { 109 | return err 110 | } 111 | repoPath := filepath.Join(rootPath, strings.ReplaceAll(filepath.Join(u.Host, strings.TrimRight(u.Path, ".git")), "/", "__")) 112 | if _, err := os.Stat(repoPath); err == nil { 113 | return fmt.Errorf("%s already exists", repoPath) 114 | } 115 | _ = cmdutil.Run("ghq", "get", repoURL) 116 | o, err := exec.Command("ghq", "list", "--full-path").Output() 117 | if err != nil { 118 | return err 119 | } 120 | var ghqPath string 121 | for _, path := range strings.Split(string(o), "\n") { 122 | if strings.Contains(path, filepath.Join(u.Host, strings.TrimRight(u.Path, ".git"))) { 123 | ghqPath = path 124 | break 125 | } 126 | } 127 | return cmdutil.Run("ln", "-s", ghqPath, repoPath) 128 | } 129 | 130 | func init() { 131 | rootCmd.AddCommand(repoCmd) 132 | repoCmd.AddCommand(repoAddCmd) 133 | repoCmd.AddCommand(repoPullCmd) 134 | } 135 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "os" 26 | 27 | "github.com/k1LoW/frgm/config" 28 | "github.com/spf13/cobra" 29 | ) 30 | 31 | var ( 32 | srcPath string 33 | destPath string 34 | formatType string 35 | rmDest bool 36 | ) 37 | 38 | var rootCmd = &cobra.Command{ 39 | Use: "frgm", 40 | Short: "frgm is a meta snippet (fragment) manager", 41 | Long: `frgm is a meta snippet (fragment) manager.`, 42 | } 43 | 44 | func Execute() { 45 | rootCmd.SetOut(os.Stdout) 46 | rootCmd.SetErr(os.Stderr) 47 | if err := rootCmd.Execute(); err != nil { 48 | rootCmd.PrintErrln(err) 49 | os.Exit(1) 50 | } 51 | } 52 | 53 | func init() { 54 | cobra.OnInitialize(config.Load) 55 | } 56 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package cmd 23 | 24 | import ( 25 | "github.com/k1LoW/frgm/version" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // versionCmd represents the version command 30 | var versionCmd = &cobra.Command{ 31 | Use: "version", 32 | Short: "Print frgm version", 33 | Long: `Print frgm version.`, 34 | Run: func(cmd *cobra.Command, args []string) { 35 | cmd.Println(version.Version) 36 | }, 37 | } 38 | 39 | func init() { 40 | rootCmd.AddCommand(versionCmd) 41 | } 42 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | // avairable config and default 11 | var configs = map[string]interface{}{ 12 | "global.snippets_path": filepath.Join(dataPath(), "snippets"), 13 | "global.ignore": []string{".git"}, 14 | } 15 | 16 | // Load config.toml 17 | func Load() { 18 | for k, v := range configs { 19 | viper.SetDefault(k, v) 20 | } 21 | viper.SetConfigType("toml") 22 | viper.SetConfigName("config") 23 | viper.AddConfigPath(configPath()) 24 | _ = viper.ReadInConfig() 25 | } 26 | 27 | // Set value to config 28 | func Set(k string, v interface{}) error { 29 | viper.Set(k, v) 30 | return nil 31 | } 32 | 33 | // Get value from config 34 | func Get(k string) interface{} { 35 | return viper.Get(k) 36 | } 37 | 38 | // GetString value from config 39 | func GetString(k string) string { 40 | return viper.GetString(k) 41 | } 42 | 43 | // GetStringSlice value from config 44 | func GetStringSlice(k string) []string { 45 | return viper.GetStringSlice(k) 46 | } 47 | 48 | // Save config.toml 49 | func Save() error { 50 | if err := os.MkdirAll(configPath(), 0700); err != nil { 51 | return err 52 | } 53 | return viper.WriteConfigAs(filepath.Join(configPath(), "config.toml")) 54 | } 55 | 56 | func configPath() string { 57 | p := os.Getenv("XDG_CONFIG_HOME") 58 | if p == "" { 59 | home := os.Getenv("HOME") 60 | p = filepath.Join(home, ".config") 61 | } 62 | return filepath.Join(p, "frgm") 63 | } 64 | 65 | func dataPath() string { 66 | p := os.Getenv("XDG_DATA_HOME") 67 | if p == "" { 68 | home := os.Getenv("HOME") 69 | p = filepath.Join(home, ".local", "share") 70 | } 71 | return filepath.Join(p, "frgm") 72 | } 73 | -------------------------------------------------------------------------------- /format/alfred/alfred.go: -------------------------------------------------------------------------------- 1 | package alfred 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/k1LoW/frgm/format" 13 | "github.com/k1LoW/frgm/snippet" 14 | "github.com/karrick/godirwalk" 15 | gitignore "github.com/sabhiram/go-gitignore" 16 | ) 17 | 18 | const DefaultDest = "~/Library/Application Support/Alfred/Alfred.alfredpreferences/snippets" 19 | 20 | var allowExts = format.Exts{".json"} 21 | 22 | type Alfred struct { 23 | ignore []string 24 | } 25 | 26 | type Snippet struct { 27 | AlfredSnippet AlfredSnippet `json:"alfredsnippet"` 28 | } 29 | 30 | type AlfredSnippet struct { 31 | UID string `json:"uid"` 32 | Name string `json:"name"` 33 | Snippet string `json:"snippet"` 34 | Keyword string `json:"keyword"` 35 | } 36 | 37 | // New return *Alfred 38 | func New(ignore []string) *Alfred { 39 | return &Alfred{ 40 | ignore: ignore, 41 | } 42 | } 43 | 44 | func NewSnippet(u, n, s, k string) Snippet { 45 | return Snippet{ 46 | AlfredSnippet: AlfredSnippet{ 47 | UID: u, 48 | Name: n, 49 | Snippet: s, 50 | Keyword: k, 51 | }, 52 | } 53 | } 54 | 55 | func (a *Alfred) Load(src string) (snippet.Snippets, error) { 56 | snippets := snippet.Snippets{} 57 | d, err := os.Stat(src) 58 | if err != nil { 59 | return snippets, err 60 | } 61 | if !d.IsDir() { 62 | return snippets, fmt.Errorf("%s is not directory", src) 63 | } 64 | i := gitignore.CompileIgnoreLines(a.ignore...) 65 | err = godirwalk.Walk(src, &godirwalk.Options{ 66 | FollowSymbolicLinks: true, 67 | Callback: func(path string, de *godirwalk.Dirent) error { 68 | b, err := de.IsDirOrSymlinkToDir() 69 | if err != nil { 70 | return err 71 | } 72 | if b { 73 | return nil 74 | } 75 | if i.MatchesPath(path) { 76 | return nil 77 | } 78 | file, err := os.Open(filepath.Clean(path)) 79 | if err != nil { 80 | return err 81 | } 82 | fn := file.Name() 83 | ext := filepath.Ext(fn) 84 | if !allowExts.Contains(ext) { 85 | return nil 86 | } 87 | group := filepath.Base(filepath.Dir(path)) 88 | s, err := a.Decode(file, group) 89 | if err != nil { 90 | return err 91 | } 92 | s.AddLoadPath(path) 93 | snippets = append(snippets, s...) 94 | return nil 95 | }, 96 | }) 97 | 98 | if err != nil { 99 | return snippets, err 100 | } 101 | 102 | return snippets, nil 103 | } 104 | 105 | func (a *Alfred) Decode(in io.Reader, group string) (snippet.Snippets, error) { 106 | snippets := snippet.Snippets{} 107 | buf, err := io.ReadAll(in) 108 | if err != nil { 109 | return snippets, err 110 | } 111 | if !bytes.Contains(buf, []byte("{")) { 112 | return snippets, nil 113 | } 114 | as := &Snippet{} 115 | if err := json.Unmarshal(buf, as); err != nil { 116 | return snippets, err 117 | } 118 | snippets = append(snippets, snippet.New(as.AlfredSnippet.UID, group, as.AlfredSnippet.Name, as.AlfredSnippet.Snippet, "", "", strings.Split(as.AlfredSnippet.Keyword, " "))) 119 | return snippets, nil 120 | } 121 | 122 | func (a *Alfred) Export(snippets snippet.Snippets, dest string) error { 123 | snippets.ClearLoadPath() 124 | current, err := a.Load(dest) 125 | if err != nil { 126 | return err 127 | } 128 | for _, s := range snippets { 129 | path := filepath.Join(dest, s.Group, fmt.Sprintf("%s.json", s.UID)) 130 | cs, err := current.FindByUID(s.UID) 131 | if err == nil { 132 | if cs.Group != s.Group { 133 | return fmt.Errorf("group is different for existing snippet and new snippet. Delete or move the existing snippet or match the group of the new snippet\n%s\n---\n%s", cs, s) 134 | } 135 | path = cs.LoadPath 136 | } 137 | as := NewSnippet(s.UID, s.Name, s.Content, strings.Join(s.Labels, " ")) 138 | if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { 139 | return err 140 | } 141 | file, err := os.OpenFile(filepath.Clean(path), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 142 | if err != nil { 143 | return err 144 | } 145 | encoder := json.NewEncoder(file) 146 | encoder.SetIndent("", " ") 147 | if err := encoder.Encode(as); err != nil { 148 | return err 149 | } 150 | if err := file.Close(); err != nil { 151 | return err 152 | } 153 | } 154 | return nil 155 | } 156 | -------------------------------------------------------------------------------- /format/alfred/alfred_test.go: -------------------------------------------------------------------------------- 1 | package alfred 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/google/go-cmp/cmp" 8 | "github.com/k1LoW/frgm/snippet" 9 | ) 10 | 11 | func TestDecode(t *testing.T) { 12 | tests := []struct { 13 | in string 14 | want snippet.Snippets 15 | }{ 16 | { 17 | in: ` 18 | `, 19 | want: snippet.Snippets{}, 20 | }, 21 | { 22 | in: `{ 23 | "alfredsnippet": { 24 | "uid": "frgm-46c29e119523", 25 | "name": "ping", 26 | "snippet": "ping 8.8.8.8", 27 | "keyword": "network google" 28 | } 29 | } 30 | `, 31 | want: snippet.Snippets{ 32 | &snippet.Snippet{ 33 | UID: "frgm-46c29e119523", 34 | Group: "default", 35 | Content: "ping 8.8.8.8", 36 | Name: "ping", 37 | Output: "", 38 | Labels: []string{"network", "google"}, 39 | }, 40 | }, 41 | }, 42 | } 43 | for _, tt := range tests { 44 | in := bytes.NewBufferString(tt.in) 45 | a := New([]string{}) 46 | got, err := a.Decode(in, "default") 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | if diff := cmp.Diff(got, tt.want, nil); diff != "" { 51 | t.Errorf("got %v\nwant %v", got, tt.want) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /format/format.go: -------------------------------------------------------------------------------- 1 | package format 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "text/template" 8 | 9 | "github.com/k1LoW/frgm/snippet" 10 | ) 11 | 12 | type Loader interface { 13 | Load(src string) (snippet.Snippets, error) 14 | } 15 | 16 | type Exporter interface { 17 | Export(snippets snippet.Snippets, dest string) error 18 | } 19 | 20 | type Decoder interface { 21 | Decode(in io.Reader, group string) (snippet.Snippets, error) 22 | } 23 | 24 | type Encoder interface { 25 | Encode(out io.Writer, snippets snippet.Snippets) error 26 | } 27 | 28 | type Exts []string 29 | 30 | func (exts Exts) Contains(t string) bool { 31 | for _, e := range exts { 32 | if e == t { 33 | return true 34 | } 35 | } 36 | return false 37 | } 38 | 39 | func Funcs() map[string]interface{} { 40 | return template.FuncMap{ 41 | "nl2mdnl": func(text string) string { 42 | r := strings.NewReplacer("\r\n", " \n", "\n", " \n", "\r", " \n") 43 | return r.Replace(text) 44 | }, 45 | "label_join": func(l []string) string { 46 | return fmt.Sprintf("%s", strings.Join(l, " ")) 47 | }, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /format/frgm/frgm.go: -------------------------------------------------------------------------------- 1 | package frgm 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | 11 | "github.com/k1LoW/frgm/format" 12 | "github.com/k1LoW/frgm/snippet" 13 | "github.com/karrick/godirwalk" 14 | gitignore "github.com/sabhiram/go-gitignore" 15 | "gopkg.in/jmhodges/yaml.v2" 16 | ) 17 | 18 | var AllowExts = format.Exts{".yml", ".yaml"} 19 | 20 | type Frgm struct { 21 | ignore []string 22 | } 23 | 24 | // New return new Frgm 25 | func New(ignore []string) *Frgm { 26 | return &Frgm{ 27 | ignore: ignore, 28 | } 29 | } 30 | 31 | func (f *Frgm) Load(src string) (snippet.Snippets, error) { 32 | snippets := snippet.Snippets{} 33 | if _, err := os.Lstat(src); err != nil { 34 | return snippets, err 35 | } 36 | i := gitignore.CompileIgnoreLines(f.ignore...) 37 | err := godirwalk.Walk(src, &godirwalk.Options{ 38 | FollowSymbolicLinks: true, 39 | Callback: func(path string, de *godirwalk.Dirent) error { 40 | b, err := de.IsDirOrSymlinkToDir() 41 | if err != nil { 42 | return err 43 | } 44 | if b { 45 | return nil 46 | } 47 | if i.MatchesPath(path) { 48 | return nil 49 | } 50 | file, err := os.Open(filepath.Clean(path)) 51 | if err != nil { 52 | return err 53 | } 54 | fn := file.Name() 55 | ext := filepath.Ext(fn) 56 | if !AllowExts.Contains(ext) { 57 | return nil 58 | } 59 | group := filepath.Base(fn[:len(fn)-len(ext)]) 60 | set, err := f.LoadSet(file, group) 61 | if err != nil { 62 | return err 63 | } 64 | set.Snippets.AddLoadPath(path) 65 | snippets = append(snippets, set.Snippets...) 66 | return nil 67 | }, 68 | }) 69 | 70 | if err != nil { 71 | return snippets, err 72 | } 73 | 74 | return snippets, nil 75 | } 76 | 77 | func (f *Frgm) LoadSets(src string) ([]*snippet.SnippetSet, error) { 78 | sets := []*snippet.SnippetSet{} 79 | if _, err := os.Lstat(src); err != nil { 80 | return sets, err 81 | } 82 | i := gitignore.CompileIgnoreLines(f.ignore...) 83 | err := godirwalk.Walk(src, &godirwalk.Options{ 84 | FollowSymbolicLinks: true, 85 | Callback: func(path string, de *godirwalk.Dirent) error { 86 | b, err := de.IsDirOrSymlinkToDir() 87 | if err != nil { 88 | return err 89 | } 90 | if b { 91 | return nil 92 | } 93 | if i.MatchesPath(path) { 94 | return nil 95 | } 96 | file, err := os.Open(filepath.Clean(path)) 97 | if err != nil { 98 | return err 99 | } 100 | fn := file.Name() 101 | ext := filepath.Ext(fn) 102 | if !AllowExts.Contains(ext) { 103 | return nil 104 | } 105 | group := filepath.Base(fn[:len(fn)-len(ext)]) 106 | set, err := f.LoadSet(file, group) 107 | if err != nil { 108 | return err 109 | } 110 | if len(set.Snippets) == 0 { 111 | return nil 112 | } 113 | set.LoadPath = path 114 | sets = append(sets, set) 115 | return nil 116 | }, 117 | }) 118 | 119 | if err != nil { 120 | return sets, err 121 | } 122 | 123 | return sets, nil 124 | } 125 | 126 | func (f *Frgm) LoadSet(in io.Reader, defaultGroup string) (*snippet.SnippetSet, error) { 127 | set := &snippet.SnippetSet{} 128 | buf, err := io.ReadAll(in) 129 | if err != nil { 130 | return set, err 131 | } 132 | if !bytes.Contains(buf, []byte("snippets:")) { 133 | return set, nil 134 | } 135 | if err := yaml.Unmarshal(buf, set); err != nil { 136 | return set, err 137 | } 138 | for _, s := range set.Snippets { 139 | if s.Group == "" { 140 | if set.Group != "" { 141 | s.Group = set.Group 142 | } else { 143 | s.Group = defaultGroup 144 | } 145 | } 146 | if s.UID == "" { 147 | uid, err := genUID(s.Group, s.Name, s.Content) 148 | if err != nil { 149 | return set, err 150 | } 151 | s.UID = uid 152 | } 153 | } 154 | return set, nil 155 | } 156 | 157 | func (f *Frgm) Export(snippets snippet.Snippets, dest string) error { 158 | snippets.ClearLoadPath() 159 | sets, err := f.LoadSets(dest) 160 | if err != nil { 161 | return err 162 | } 163 | L: 164 | for _, s := range snippets { 165 | // same UID 166 | for _, set := range sets { 167 | current, err := set.Snippets.FindByUID(s.UID) 168 | if err == nil { 169 | if s.Group != "" { 170 | current.Group = s.Group 171 | } 172 | current.Content = s.Content 173 | if s.Desc != "" { 174 | current.Desc = s.Desc 175 | } 176 | if len(s.Labels) > 0 { 177 | current.Labels = s.Labels 178 | } 179 | continue L 180 | } 181 | } 182 | if s.Group != "" { 183 | // same group ( SnippetSet ) 184 | for _, set := range sets { 185 | if set.Group == s.Group { 186 | set.Snippets = append(set.Snippets, s) 187 | continue L 188 | } 189 | } 190 | // same group ( Snippet ) 191 | for _, set := range sets { 192 | if set.Group != "" { 193 | continue 194 | } 195 | for _, c := range set.Snippets { 196 | if c.Group == s.Group { 197 | set.Snippets = append(set.Snippets, s) 198 | continue L 199 | } 200 | } 201 | } 202 | } 203 | // none 204 | sets[0].Snippets = append(sets[0].Snippets, s) 205 | } 206 | 207 | // set path 208 | paths := map[string]struct{}{} 209 | for _, set := range sets { 210 | if set.LoadPath != "" { 211 | if _, e := paths[set.LoadPath]; e { 212 | return fmt.Errorf("duplicate path: %s", set.LoadPath) 213 | } 214 | continue 215 | } 216 | if set.Group != "" { 217 | path := filepath.Join(dest, fmt.Sprintf("%s.yml", set.Group)) 218 | if _, e := paths[path]; !e { 219 | paths[path] = struct{}{} 220 | set.LoadPath = path 221 | continue 222 | } 223 | } 224 | for { 225 | uid, err := genUID("", set.Snippets[0].Desc, set.Snippets[0].Content) 226 | if err != nil { 227 | return err 228 | } 229 | path := filepath.Join(dest, fmt.Sprintf("%s.yml", uid)) 230 | if _, e := paths[path]; !e { 231 | set.LoadPath = path 232 | break 233 | } 234 | } 235 | } 236 | 237 | // export 238 | for _, set := range sets { 239 | path := set.LoadPath 240 | file, err := os.OpenFile(filepath.Clean(path), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 241 | if err != nil { 242 | return err 243 | } 244 | encoder := yaml.NewEncoder(file) 245 | encoder.SetLineLength(-1) 246 | if err := encoder.Encode(set); err != nil { 247 | return err 248 | } 249 | } 250 | 251 | return nil 252 | } 253 | 254 | func (f *Frgm) Encode(out io.Writer, snippets snippet.Snippets) error { 255 | set := snippet.SnippetSet{ 256 | Group: "", 257 | Snippets: snippets, 258 | } 259 | encoder := yaml.NewEncoder(out) 260 | encoder.SetLineLength(-1) 261 | if err := encoder.Encode(set); err != nil { 262 | return err 263 | } 264 | return nil 265 | } 266 | 267 | func genUID(g, d, c string) (string, error) { 268 | h := sha256.New() 269 | if _, err := io.WriteString(h, fmt.Sprintf("%s-%s-%s", g, d, c)); err != nil { 270 | return "", err 271 | } 272 | s := fmt.Sprintf("%x", h.Sum(nil)) 273 | return fmt.Sprintf("frgm-%s", s[:12]), nil 274 | } 275 | -------------------------------------------------------------------------------- /format/frgm/frgm_test.go: -------------------------------------------------------------------------------- 1 | package frgm 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | "testing" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | "github.com/k1LoW/frgm/snippet" 10 | ) 11 | 12 | func TestLoadSet(t *testing.T) { 13 | tests := []struct { 14 | in string 15 | wantGroup string 16 | wantUIDMatch string 17 | }{ 18 | { 19 | in: `--- 20 | snippets: 21 | - 22 | name: hello world 23 | content: echo hello world 24 | labels: 25 | - test 26 | - echo 27 | `, 28 | wantGroup: "default-group", 29 | wantUIDMatch: "^frgm", 30 | }, 31 | { 32 | in: `--- 33 | snippets: 34 | - 35 | name: hello world 36 | content: echo hello world 37 | group: my-group 38 | labels: 39 | - test 40 | - echo 41 | `, 42 | wantGroup: "my-group", 43 | wantUIDMatch: "^frgm", 44 | }, 45 | { 46 | in: `--- 47 | snippets: 48 | - 49 | name: hello world 50 | content: echo hello world 51 | uid: 1234-5678-90 52 | labels: 53 | - test 54 | - echo 55 | `, 56 | wantGroup: "default-group", 57 | wantUIDMatch: "^1234-5678-90$", 58 | }, 59 | } 60 | 61 | for _, tt := range tests { 62 | in := bytes.NewBufferString(tt.in) 63 | frgm := New([]string{}) 64 | got, err := frgm.LoadSet(in, "default-group") 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | if want := 1; len(got.Snippets) != want { 69 | t.Errorf("got %v\nwant %v", len(got.Snippets), want) 70 | } 71 | if want := tt.wantGroup; got.Snippets[0].Group != want { 72 | t.Errorf("got %v\nwant %v", got.Snippets[0].Group, want) 73 | } 74 | if ok, _ := regexp.MatchString(tt.wantUIDMatch, got.Snippets[0].UID); !ok { 75 | t.Errorf("got %v\nwant match %v", got.Snippets[0].UID, tt.wantUIDMatch) 76 | } 77 | } 78 | } 79 | 80 | func TestFill(t *testing.T) { 81 | tests := []struct { 82 | in string 83 | group string 84 | want string 85 | }{ 86 | { 87 | in: `--- 88 | snippets: 89 | - 90 | name: hello world 91 | content: echo hello world 92 | `, 93 | group: "my-group", 94 | want: `--- 95 | snippets: 96 | - 97 | uid: frgm-de1fbe2f7573 98 | group: my-group 99 | name: hello world 100 | content: echo hello world 101 | `, 102 | }, 103 | { 104 | in: `--- 105 | snippets: 106 | - 107 | name: hello world 108 | content: echo hello world 109 | group: other-group 110 | `, 111 | group: "my-group", 112 | want: `--- 113 | snippets: 114 | - 115 | uid: frgm-526b6b7787e4 116 | group: other-group 117 | name: hello world 118 | content: echo hello world 119 | `, 120 | }, 121 | } 122 | 123 | frgm := New([]string{}) 124 | for _, tt := range tests { 125 | in := bytes.NewBufferString(tt.in) 126 | got, err := frgm.LoadSet(in, tt.group) 127 | if err != nil { 128 | t.Fatal(err) 129 | } 130 | 131 | out := bytes.NewBufferString(tt.want) 132 | want, err := frgm.LoadSet(out, tt.group) 133 | if err != nil { 134 | t.Fatal(err) 135 | } 136 | 137 | if diff := cmp.Diff(got, want, nil); diff != "" { 138 | t.Errorf("got %v\nwant %v", got, want) 139 | } 140 | } 141 | } 142 | 143 | func TestEncode(t *testing.T) { 144 | tests := []struct { 145 | snippets snippet.Snippets 146 | want string 147 | }{ 148 | { 149 | snippets: snippet.Snippets{}, 150 | want: `snippets: [] 151 | `, 152 | }, 153 | { 154 | snippets: snippet.Snippets{ 155 | snippet.New("frgm-de1fbe2f7573", "my-group", "hello world", "echo hello world", "hello world", "show following\nhello world\n", []string{}), 156 | }, 157 | want: `snippets: 158 | - uid: frgm-de1fbe2f7573 159 | group: my-group 160 | name: hello world 161 | desc: | 162 | show following 163 | hello world 164 | content: echo hello world 165 | output: hello world 166 | `, 167 | }, 168 | { 169 | snippets: snippet.Snippets{ 170 | snippet.New("frgm-de1fbe2f7573", "my-group", "count list", `cat list | sort -nk1`, "", "", []string{"a-b", "c"}), 171 | }, 172 | want: `snippets: 173 | - uid: frgm-de1fbe2f7573 174 | group: my-group 175 | name: count list 176 | content: cat list | sort -nk1 177 | labels: 178 | - a-b 179 | - c 180 | `, 181 | }, 182 | { 183 | snippets: snippet.Snippets{ 184 | snippet.New("frgm-de1fbe2f7573", "my-group", "count access.log", `cat /var/log/nginx/access.log | awk '{ if (($1 == "domain:example.com") && ($8 ~ /^status:[2345]/)) { arr[$8]++; x++ } } END { for (i in arr) { printf "%s\t%.2f%\t%s / %s\n", i, arr[i]/x*100, arr[i], x } }' | sort -nk1`, "", "", []string{"a-b", "c"}), 185 | }, 186 | want: `snippets: 187 | - uid: frgm-de1fbe2f7573 188 | group: my-group 189 | name: count access.log 190 | content: cat /var/log/nginx/access.log | awk '{ if (($1 == "domain:example.com") && ($8 ~ /^status:[2345]/)) { arr[$8]++; x++ } } END { for (i in arr) { printf "%s\t%.2f%\t%s / %s\n", i, arr[i]/x*100, arr[i], x } }' | sort -nk1 191 | labels: 192 | - a-b 193 | - c 194 | `, 195 | }, 196 | } 197 | 198 | frgm := New([]string{}) 199 | for _, tt := range tests { 200 | out := new(bytes.Buffer) 201 | if err := frgm.Encode(out, tt.snippets); err != nil { 202 | t.Fatal(err) 203 | } 204 | got := out.String() 205 | if got != tt.want { 206 | t.Errorf("got %v\nwant %v", got, tt.want) 207 | } 208 | set, err := frgm.LoadSet(out, "default") 209 | if err != nil { 210 | t.Fatal(err) 211 | } 212 | if diff := cmp.Diff(set.Snippets, tt.snippets, nil); diff != "" { 213 | t.Errorf("%s", diff) 214 | } 215 | } 216 | } 217 | 218 | func TestGenUID(t *testing.T) { 219 | got, err := genUID("", "", "") 220 | if err != nil { 221 | t.Fatal(err) 222 | } 223 | if want := "frgm-d8156bae0c42"; got != want { 224 | t.Errorf("got %v want %v", got, want) 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /format/history/history.go: -------------------------------------------------------------------------------- 1 | package history 2 | 3 | import ( 4 | "bufio" 5 | "crypto/sha256" 6 | "fmt" 7 | "io" 8 | "regexp" 9 | "strings" 10 | 11 | "github.com/k1LoW/frgm/snippet" 12 | ) 13 | 14 | type History struct{} 15 | 16 | // New return *History 17 | func New() *History { 18 | return &History{} 19 | } 20 | 21 | var trimRe = regexp.MustCompile(`^\s*[0-9*]+\s+`) 22 | 23 | func (h *History) Decode(stdin io.Reader, group string) (snippet.Snippets, error) { 24 | snippets := snippet.Snippets{} 25 | in := bufio.NewReader(stdin) 26 | for { 27 | line, err := in.ReadString('\n') 28 | if err == io.EOF { 29 | break 30 | } else if err != nil { 31 | return snippets, err 32 | } 33 | command := strings.TrimRight(trimRe.ReplaceAllString(line, ""), "\n") 34 | if command == "" { 35 | continue 36 | } 37 | name := command 38 | if strings.Count(command, "#") == 1 { 39 | splited := strings.Split(command, "#") 40 | command = strings.TrimSpace(splited[0]) 41 | name = strings.TrimSpace(splited[1]) 42 | } 43 | uid, err := genUID(line) 44 | if err != nil { 45 | return snippets, err 46 | } 47 | snippets = append(snippets, snippet.New(uid, group, name, command, "", "", []string{})) 48 | } 49 | return snippets, nil 50 | } 51 | 52 | func genUID(line string) (string, error) { 53 | h := sha256.New() 54 | if _, err := io.WriteString(h, line); err != nil { 55 | return "", err 56 | } 57 | s := fmt.Sprintf("%x", h.Sum(nil)) 58 | return fmt.Sprintf("history-%s", s[:12]), nil 59 | } 60 | -------------------------------------------------------------------------------- /format/history/history_test.go: -------------------------------------------------------------------------------- 1 | package history 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/google/go-cmp/cmp" 8 | "github.com/k1LoW/frgm/snippet" 9 | ) 10 | 11 | func TestDecode(t *testing.T) { 12 | tests := []struct { 13 | in string 14 | want snippet.Snippets 15 | }{ 16 | { 17 | in: ``, 18 | want: snippet.Snippets{}, 19 | }, 20 | { 21 | in: ` 22 | `, 23 | want: snippet.Snippets{}, 24 | }, 25 | { 26 | in: ` 8 man history 27 | 9 git checkout -b format-history 28 | 10 emacs format/ 29 | 11* go test ./format/history 30 | 12 ls # exec ls 31 | `, 32 | want: snippet.Snippets{ 33 | &snippet.Snippet{ 34 | UID: "history-bd725c52840f", 35 | Group: "default", 36 | Name: "man history", 37 | Content: "man history", 38 | }, 39 | &snippet.Snippet{ 40 | UID: "history-709a48a0dc97", 41 | Group: "default", 42 | Name: "git checkout -b format-history", 43 | Content: "git checkout -b format-history", 44 | }, 45 | &snippet.Snippet{ 46 | UID: "history-fd42cee8fd75", 47 | Group: "default", 48 | Name: "emacs format/", 49 | Content: "emacs format/", 50 | }, 51 | &snippet.Snippet{ 52 | UID: "history-00b0136f1cf7", 53 | Group: "default", 54 | Name: "go test ./format/history", 55 | Content: "go test ./format/history", 56 | }, 57 | &snippet.Snippet{ 58 | UID: "history-a3a97692f42a", 59 | Group: "default", 60 | Name: "exec ls", 61 | Content: "ls", 62 | }, 63 | }, 64 | }, 65 | } 66 | 67 | for _, tt := range tests { 68 | in := bytes.NewBufferString(tt.in) 69 | history := New() 70 | got, err := history.Decode(in, "default") 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | if diff := cmp.Diff(got, tt.want, nil); diff != "" { 75 | t.Errorf("%v\n", diff) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /format/list/list.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | 8 | "github.com/k1LoW/frgm/snippet" 9 | ) 10 | 11 | const DefaultFormat = ":content # :name [:group :labels]" 12 | 13 | var nlr = strings.NewReplacer("\\\n", "") 14 | 15 | type List struct { 16 | format string 17 | } 18 | 19 | // New return new List 20 | func New(format string) *List { 21 | return &List{ 22 | format: format, 23 | } 24 | } 25 | 26 | func (f *List) Encode(out io.Writer, snippets snippet.Snippets) error { 27 | for _, s := range snippets { 28 | r := strings.NewReplacer(":uid", s.UID, ":group", s.Group, ":name", s.Name, ":content", strings.TrimRight(nlr.Replace(s.Content), "\n"), ":labels", strings.Join(s.Labels, " "), ":desc", s.Desc, ":output", s.Output) 29 | _, err := fmt.Fprintln(out, r.Replace(f.format)) 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /format/list/list_test.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/k1LoW/frgm/snippet" 8 | ) 9 | 10 | func TestEncode(t *testing.T) { 11 | tests := []struct { 12 | format string 13 | snippets snippet.Snippets 14 | want string 15 | }{ 16 | { 17 | format: DefaultFormat, 18 | snippets: snippet.Snippets{}, 19 | want: ``, 20 | }, 21 | { 22 | format: DefaultFormat, 23 | snippets: snippet.Snippets{ 24 | snippet.New("frgm-de1fbe2f7573", "my-group", "hello world", "echo hello world", "hello world", "show following\nhello world\n", []string{"a", "b"}), 25 | }, 26 | want: `echo hello world # hello world [my-group a b] 27 | `, 28 | }, 29 | { 30 | format: ":content", 31 | snippets: snippet.Snippets{ 32 | snippet.New("frgm-de1fbe2f7573", "my-group", "hello world", "echo hello world", "hello world", "show following\nhello world\n", []string{"a", "b"}), 33 | }, 34 | want: `echo hello world 35 | `, 36 | }, 37 | { 38 | format: DefaultFormat, 39 | snippets: snippet.Snippets{ 40 | snippet.New("frgm-de1fbe2f7573", "my-group", "hello world", `echo hello \ 41 | world`, "hello world", "show following\nhello world\n", []string{"a", "b"}), 42 | }, 43 | want: `echo hello world # hello world [my-group a b] 44 | `, 45 | }, 46 | { 47 | format: DefaultFormat, 48 | snippets: snippet.Snippets{ 49 | snippet.New("frgm-de1fbe2f7573", "my-group", "hello world", `echo hello \ 50 | world 51 | 52 | 53 | 54 | `, "hello world", "show following\nhello world\n", []string{"a", "b"}), 55 | }, 56 | want: `echo hello world # hello world [my-group a b] 57 | `, 58 | }, 59 | } 60 | 61 | for _, tt := range tests { 62 | list := New(tt.format) 63 | out := new(bytes.Buffer) 64 | if err := list.Encode(out, tt.snippets); err != nil { 65 | t.Fatal(err) 66 | } 67 | got := out.String() 68 | if got != tt.want { 69 | t.Errorf("got %v\nwant %v", got, tt.want) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /format/md/md.go: -------------------------------------------------------------------------------- 1 | package md 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | "text/template" 10 | 11 | "github.com/k1LoW/frgm/format" 12 | "github.com/k1LoW/frgm/snippet" 13 | ) 14 | 15 | var allowExts = format.Exts{".md"} 16 | 17 | //go:embed templates/* 18 | var tmpl embed.FS 19 | 20 | type Md struct { 21 | ignore []string 22 | tmpl embed.FS 23 | } 24 | 25 | // New return *Md 26 | func New(ignore []string) *Md { 27 | return &Md{ 28 | ignore: ignore, 29 | tmpl: tmpl, 30 | } 31 | } 32 | 33 | func (m *Md) Export(snippets snippet.Snippets, dest string) error { 34 | f, err := os.Stat(dest) 35 | if err == nil && f.IsDir() { 36 | return fmt.Errorf("%s is directory", dest) 37 | } 38 | ext := filepath.Ext(dest) 39 | if !allowExts.Contains(ext) { 40 | return fmt.Errorf("%s is not .md file", dest) 41 | } 42 | snippets.ClearLoadPath() 43 | sets := []*snippet.SnippetSet{} 44 | groups := map[string]*snippet.SnippetSet{} 45 | 46 | for _, s := range snippets { 47 | set, ok := groups[s.Group] 48 | if !ok { 49 | set = &snippet.SnippetSet{ 50 | Group: s.Group, 51 | Snippets: snippet.Snippets{}, 52 | } 53 | groups[s.Group] = set 54 | sets = append(sets, set) 55 | } 56 | set.Snippets = append(set.Snippets, s) 57 | } 58 | 59 | path := dest 60 | file, err := os.OpenFile(filepath.Clean(path), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 61 | if err != nil { 62 | return err 63 | } 64 | if err := m.Write(file, sets); err != nil { 65 | return err 66 | } 67 | 68 | return nil 69 | } 70 | 71 | func (m *Md) Write(wr io.Writer, sets []*snippet.SnippetSet) error { 72 | tb, err := m.tmpl.ReadFile("templates/snippets.md.tmpl") 73 | if err != nil { 74 | return err 75 | } 76 | tmpl := template.Must(template.New("snippets").Funcs(format.Funcs()).Parse(string(tb))) 77 | tmplData := map[string]interface{}{ 78 | "Sets": sets, 79 | } 80 | if err != nil { 81 | return err 82 | } 83 | if err := tmpl.Execute(wr, tmplData); err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /format/md/md_test.go: -------------------------------------------------------------------------------- 1 | package md 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/k1LoW/frgm/snippet" 10 | ) 11 | 12 | func TestWrite(t *testing.T) { 13 | tests := []struct { 14 | sets []*snippet.SnippetSet 15 | goldenPath string 16 | }{ 17 | { 18 | []*snippet.SnippetSet{ 19 | &snippet.SnippetSet{ 20 | Group: "group-a", 21 | Snippets: snippet.Snippets{ 22 | snippet.New("uid-a-1", "group-a", "a-1", "echo a-1", "out a-1", "desc a-1", []string{"label-a", "label:1"}), 23 | snippet.New("uid-a-2", "group-a", "a-2", "echo a-2", "", "", []string{"label-a", "label:2"}), 24 | }, 25 | }, 26 | &snippet.SnippetSet{ 27 | Group: "group-b", 28 | Snippets: snippet.Snippets{ 29 | snippet.New("uid-b-1", "group-b", "b-1", "echo b-1", "", "desc b-1\ndesc b-1", []string{}), 30 | }, 31 | }, 32 | }, 33 | "md_write_1.md.golden", 34 | }, 35 | } 36 | 37 | for _, tt := range tests { 38 | md := New([]string{}) 39 | got := new(bytes.Buffer) 40 | if err := md.Write(got, tt.sets); err != nil { 41 | t.Fatal(err) 42 | } 43 | want, err := os.ReadFile(filepath.Join(testdataDir(), tt.goldenPath)) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | if got.String() != string(want) { 48 | t.Errorf("got\n%s\nwant\n%s", got.Bytes(), want) 49 | } 50 | } 51 | } 52 | 53 | func testdataDir() string { 54 | wd, _ := os.Getwd() 55 | dir, _ := filepath.Abs(filepath.Join(filepath.Dir(filepath.Dir(wd)), "testdata")) 56 | return dir 57 | } 58 | -------------------------------------------------------------------------------- /format/md/templates/snippets.md.tmpl: -------------------------------------------------------------------------------- 1 | ## Snippets 2 | {{ range $set := .Sets }} 3 | - {{ $set.Group -}} 4 | {{ range $s := $set.Snippets }} 5 | - [{{ $s.Name }}](#{{ $s.UID }}){{ end -}} 6 | 7 | {{ end -}} 8 | {{ range $set := .Sets }} 9 | 10 | ### {{ $set.Group -}} 11 | {{ range $s := $set.Snippets }} 12 | 13 | #### {{ $s.Name }} 14 | 15 | ``` 16 | {{ $s.Content }} 17 | ``` 18 | {{- if ne $s.Output "" }} 19 | 20 | **output:** 21 | ``` 22 | {{ $s.Output }} 23 | ```{{ end -}} 24 | {{ if ne $s.Desc "" }} 25 | 26 | {{ $s.Desc | nl2mdnl -}} 27 | {{ end -}} 28 | {{ if ne (len $s.Labels) 0 }} 29 | 30 |

31 | {{ $s.Labels | label_join }} 32 |

{{ end -}} 33 | {{ end -}} 34 | {{ end }} 35 | 36 | --- 37 | 38 | > Generated by [frgm](https://github.com/k1LoW/frgm) 39 | -------------------------------------------------------------------------------- /format/pet/pet.go: -------------------------------------------------------------------------------- 1 | package pet 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/BurntSushi/toml" 11 | "github.com/k1LoW/frgm/format" 12 | "github.com/k1LoW/frgm/snippet" 13 | ) 14 | 15 | var allowExts = format.Exts{".toml"} 16 | 17 | type Snippet struct { 18 | Command string `toml:"command"` 19 | Description string `toml:"description"` 20 | Output string `toml:"output"` 21 | Tag []string `toml:"tag"` 22 | } 23 | 24 | type Snippets struct { 25 | Snippets []Snippet `toml:"snippets"` 26 | } 27 | 28 | type Pet struct{} 29 | 30 | // New return new Pet 31 | func New() *Pet { 32 | return &Pet{} 33 | } 34 | 35 | func (p *Pet) Load(src string) (snippet.Snippets, error) { 36 | snippets := snippet.Snippets{} 37 | if _, err := os.Lstat(src); err != nil { 38 | return snippets, err 39 | } 40 | file, err := os.Open(filepath.Clean(src)) 41 | if err != nil { 42 | return snippets, err 43 | } 44 | fn := file.Name() 45 | ext := filepath.Ext(fn) 46 | if !allowExts.Contains(ext) { 47 | return snippets, fmt.Errorf("pet snippet file should be .toml: %s", src) 48 | } 49 | return p.Decode(file, "default") 50 | } 51 | 52 | func (p *Pet) Decode(in io.Reader, group string) (snippet.Snippets, error) { 53 | snippets := snippet.Snippets{} 54 | var psnips Snippets 55 | if _, err := toml.DecodeReader(in, &psnips); err != nil { 56 | return snippets, err 57 | } 58 | for _, s := range psnips.Snippets { 59 | uid, err := genUID(s.Command) 60 | if err != nil { 61 | return snippets, err 62 | } 63 | snippets = append(snippets, snippet.New(uid, group, s.Description, s.Command, s.Output, "", s.Tag)) 64 | } 65 | return snippets, nil 66 | } 67 | 68 | func (p *Pet) loadFile(in io.Reader) (Snippets, error) { 69 | var psnips Snippets 70 | if _, err := toml.DecodeReader(in, &psnips); err != nil { 71 | return psnips, err 72 | } 73 | return psnips, nil 74 | } 75 | 76 | func genUID(c string) (string, error) { 77 | h := sha256.New() 78 | if _, err := io.WriteString(h, c); err != nil { 79 | return "", err 80 | } 81 | s := fmt.Sprintf("%x", h.Sum(nil)) 82 | return fmt.Sprintf("pet-%s", s[:12]), nil 83 | } 84 | 85 | func (p *Pet) Export(snippets snippet.Snippets, dest string) error { 86 | psnips := Snippets{ 87 | Snippets: []Snippet{}, 88 | } 89 | current, err := p.Load(dest) 90 | if err != nil { 91 | return err 92 | } 93 | for _, s := range snippets { 94 | cs, err := current.FindByUID(s.UID) 95 | if err != nil { 96 | current = append(current, s) 97 | continue 98 | } 99 | cs.Name = s.Name 100 | cs.Desc = s.Desc 101 | cs.Output = s.Output 102 | cs.Labels = s.Labels 103 | } 104 | for _, s := range current { 105 | psnips.Snippets = append(psnips.Snippets, Snippet{ 106 | Command: s.Content, 107 | Description: s.Name, 108 | Output: s.Output, 109 | Tag: s.Labels, 110 | }) 111 | } 112 | file, err := os.OpenFile(filepath.Clean(dest), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 113 | if err != nil { 114 | return err 115 | } 116 | encoder := toml.NewEncoder(file) 117 | if err := encoder.Encode(psnips); err != nil { 118 | return err 119 | } 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /format/pet/pet_test.go: -------------------------------------------------------------------------------- 1 | package pet 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/google/go-cmp/cmp" 10 | "github.com/k1LoW/frgm/snippet" 11 | ) 12 | 13 | func TestDecode(t *testing.T) { 14 | tests := []struct { 15 | in string 16 | want snippet.Snippets 17 | }{ 18 | { 19 | in: ` 20 | `, 21 | want: snippet.Snippets{}, 22 | }, 23 | { 24 | in: `[[snippets]] 25 | description = "ping" 26 | command = "ping 8.8.8.8" 27 | tag = ["network", "google"] 28 | output = "" 29 | 30 | [[snippets]] 31 | description = "hello" 32 | command = "echo hello" 33 | tag = ["sample"] 34 | output = "hello" 35 | `, 36 | want: snippet.Snippets{ 37 | &snippet.Snippet{ 38 | UID: "pet-df7bb29f9681", 39 | Group: "default", 40 | Content: "ping 8.8.8.8", 41 | Name: "ping", 42 | Output: "", 43 | Labels: []string{"network", "google"}, 44 | }, 45 | &snippet.Snippet{ 46 | UID: "pet-584a331fd6b0", 47 | Group: "default", 48 | Content: "echo hello", 49 | Name: "hello", 50 | Output: "hello", 51 | Labels: []string{"sample"}, 52 | }, 53 | }, 54 | }, 55 | } 56 | for _, tt := range tests { 57 | in := bytes.NewBufferString(tt.in) 58 | pet := New() 59 | got, err := pet.Decode(in, "default") 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | if diff := cmp.Diff(got, tt.want, nil); diff != "" { 64 | t.Errorf("got %v\nwant %v", got, tt.want) 65 | } 66 | } 67 | } 68 | 69 | func TestExport(t *testing.T) { 70 | uid, err := genUID("ping 8.8.8.8") 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | tests := []struct { 75 | current string 76 | in snippet.Snippets 77 | want string 78 | }{ 79 | { 80 | current: `[[snippets]] 81 | description = "ping" 82 | command = "ping 8.8.8.8" 83 | tag = ["network", "google"] 84 | output = "" 85 | `, 86 | in: snippet.Snippets{ 87 | snippet.New("uid", "group", "name", "content", "output", "description", []string{"a", "b"}), 88 | }, 89 | want: `[[snippets]] 90 | command = "ping 8.8.8.8" 91 | description = "ping" 92 | output = "" 93 | tag = ["network", "google"] 94 | 95 | [[snippets]] 96 | command = "content" 97 | description = "name" 98 | output = "output" 99 | tag = ["a", "b"] 100 | `, 101 | }, 102 | { 103 | current: `[[snippets]] 104 | description = "ping" 105 | command = "ping 8.8.8.8" 106 | tag = ["network", "google"] 107 | output = "" 108 | `, 109 | in: snippet.Snippets{ 110 | snippet.New(uid, "group", "name", "content", "output", "description", []string{"a", "b"}), 111 | }, 112 | want: `[[snippets]] 113 | command = "ping 8.8.8.8" 114 | description = "name" 115 | output = "output" 116 | tag = ["a", "b"] 117 | `, 118 | }, 119 | } 120 | 121 | tmp, err := os.MkdirTemp("", "frgm") 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | defer os.RemoveAll(tmp) 126 | 127 | for _, tt := range tests { 128 | pet := New() 129 | dest := filepath.Join(tmp, "pet.toml") 130 | if err := os.WriteFile(dest, []byte(tt.current), 0644); err != nil { //nolint 131 | t.Fatal(err) 132 | } 133 | if err := pet.Export(tt.in, dest); err != nil { 134 | t.Fatal(err) 135 | } 136 | d, err := os.ReadFile(dest) 137 | if err != nil { 138 | t.Fatal(err) 139 | } 140 | got := string(d) 141 | if got != tt.want { 142 | t.Errorf("got %v\nwant %v", got, tt.want) 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/k1LoW/frgm 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/BurntSushi/toml v1.2.1 7 | github.com/Songmu/prompter v0.5.1 8 | github.com/google/go-cmp v0.5.9 9 | github.com/karrick/godirwalk v1.17.0 10 | github.com/labstack/gommon v0.4.0 11 | github.com/mattn/go-isatty v0.0.18 12 | github.com/mattn/go-tty v0.0.4 13 | github.com/pkg/errors v0.9.1 14 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 15 | github.com/spf13/cobra v1.6.1 16 | github.com/spf13/viper v1.15.0 17 | github.com/whilp/git-urls v1.0.0 18 | github.com/x-motemen/ghq v1.4.1 19 | gopkg.in/jmhodges/yaml.v2 v2.2.5 20 | ) 21 | 22 | require ( 23 | github.com/daviddengcn/go-colortext v1.0.0 // indirect 24 | github.com/fsnotify/fsnotify v1.6.0 // indirect 25 | github.com/hashicorp/hcl v1.0.0 // indirect 26 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 27 | github.com/magiconair/properties v1.8.7 // indirect 28 | github.com/mattn/go-colorable v0.1.13 // indirect 29 | github.com/mitchellh/mapstructure v1.5.0 // indirect 30 | github.com/motemen/go-colorine v0.0.0-20180816141035-45d19169413a // indirect 31 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect 32 | github.com/rogpeppe/go-internal v1.8.0 // indirect 33 | github.com/spf13/afero v1.9.3 // indirect 34 | github.com/spf13/cast v1.5.0 // indirect 35 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 36 | github.com/spf13/pflag v1.0.5 // indirect 37 | github.com/subosito/gotenv v1.4.2 // indirect 38 | golang.org/x/sys v0.6.0 // indirect 39 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect 40 | golang.org/x/text v0.5.0 // indirect 41 | gopkg.in/ini.v1 v1.67.0 // indirect 42 | gopkg.in/yaml.v3 v3.0.1 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 41 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 42 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 43 | github.com/Songmu/prompter v0.5.1 h1:IAsttKsOZWSDw7bV1mtGn9TAmLFAjXbp9I/eYmUUogo= 44 | github.com/Songmu/prompter v0.5.1/go.mod h1:CS3jEPD6h9IaLaG6afrl1orTgII9+uDWuw95dr6xHSw= 45 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 46 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 47 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 48 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 49 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 50 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 51 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 52 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 53 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 54 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 56 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 57 | github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE= 58 | github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= 59 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 60 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 61 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 62 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 63 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 64 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 65 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 66 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 67 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 68 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 69 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 70 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 71 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 72 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 73 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 74 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 75 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 76 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 77 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 78 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 79 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 80 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 81 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 82 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 83 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 84 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 85 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 86 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 87 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 88 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 89 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 90 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 91 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 92 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 93 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 94 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 95 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 96 | github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= 97 | github.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A= 98 | github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE= 99 | github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrSEUQ= 100 | github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA= 101 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 102 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 103 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 104 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 105 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 106 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 107 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 108 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 109 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 110 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 111 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 113 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 114 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 115 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 116 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 117 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 118 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 119 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 120 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 121 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 122 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 123 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 124 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 125 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 126 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 127 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 128 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 129 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 130 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 131 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 132 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 133 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 134 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 135 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 136 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 137 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 138 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 139 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 140 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 141 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 142 | github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= 143 | github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= 144 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 145 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 146 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 147 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 148 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 149 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 150 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 151 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= 152 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 153 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 154 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 155 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 156 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 157 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 158 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 159 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 160 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 161 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 162 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 163 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= 164 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 165 | github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 166 | github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= 167 | github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= 168 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 169 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 170 | github.com/motemen/go-colorine v0.0.0-20180816141035-45d19169413a h1:CONqI/36EjYzkAzrMD0UWuL/lRDr7UdoID4fDGke+Yc= 171 | github.com/motemen/go-colorine v0.0.0-20180816141035-45d19169413a/go.mod h1:PU2urRC7j30rrabSyp1MGGhyoiWSninPD8ckjzBSgkU= 172 | github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= 173 | github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 174 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 175 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 176 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 177 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 178 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 179 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 180 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 181 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 182 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 183 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 184 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 185 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= 186 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= 187 | github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= 188 | github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= 189 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 190 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 191 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 192 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 193 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 194 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 195 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 196 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 197 | github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= 198 | github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= 199 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 200 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 201 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 202 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 203 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 204 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 205 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 206 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 207 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 208 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 209 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 210 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 211 | github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= 212 | github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 213 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 214 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 215 | github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= 216 | github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= 217 | github.com/x-motemen/ghq v1.4.1 h1:DIdx/ywV0aemhq2baadhqntyPM4C3RBClm+E0s2Bv/Y= 218 | github.com/x-motemen/ghq v1.4.1/go.mod h1:v+2i9NI4qwVZF/NF6VgCO+hYFv4H+LAcHYPn2zwwN3A= 219 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 220 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 221 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 222 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 223 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 224 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 225 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 226 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 227 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 228 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 229 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 230 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 231 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 232 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 233 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 234 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 235 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 236 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 237 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 238 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 239 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 240 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 241 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 242 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 243 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 244 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 245 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 246 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 247 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 248 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 249 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 250 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 251 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 252 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 253 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 254 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 255 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 256 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 257 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 258 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 259 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 260 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 261 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 262 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 263 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 264 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 265 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 266 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 267 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 268 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 269 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 270 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 271 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 272 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 273 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 274 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 275 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 276 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 277 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 278 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 279 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 280 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 281 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 282 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 283 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 284 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 285 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 286 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 287 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 288 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 289 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 290 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 291 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 292 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 293 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 294 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 295 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 296 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 297 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 298 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 299 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 300 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 301 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 302 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 303 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 304 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 305 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 306 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 307 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 308 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 309 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 310 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 311 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 312 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 313 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 314 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 315 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 316 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 317 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 318 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 319 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 320 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 321 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 322 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 326 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 327 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 328 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 329 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 330 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 331 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 332 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 334 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 338 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 349 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 351 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 353 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 356 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 357 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 358 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 359 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 360 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 361 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 362 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 363 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 364 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= 365 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 366 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 367 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 368 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 369 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 370 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 371 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 372 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 373 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 374 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 375 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 376 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 377 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 378 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 379 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 380 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 381 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 382 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 383 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 384 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 385 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 386 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 387 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 388 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 389 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 390 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 391 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 392 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 393 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 394 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 395 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 396 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 397 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 398 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 399 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 400 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 401 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 402 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 403 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 404 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 405 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 406 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 407 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 408 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 409 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 410 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 411 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 412 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 413 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 414 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 415 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 416 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 417 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 418 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 419 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 420 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 421 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 422 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 423 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 424 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 425 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 426 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 427 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 428 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 429 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 430 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 431 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 432 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 433 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 434 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 435 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 436 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 437 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 438 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 439 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 440 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 441 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 442 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 443 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 444 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 445 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 446 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 447 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 448 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 449 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 450 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 451 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 452 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 453 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 454 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 455 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 456 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 457 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 458 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 459 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 460 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 461 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 462 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 463 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 464 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 465 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 466 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 467 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 468 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 469 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 470 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 471 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 472 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 473 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 474 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 475 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 476 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 477 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 478 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 479 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 480 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 481 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 482 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 483 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 484 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 485 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 486 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 487 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 488 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 489 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 490 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 491 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 492 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 493 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 494 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 495 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 496 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 497 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 498 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 499 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 500 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 501 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 502 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 503 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 504 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 505 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 506 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 507 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 508 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 509 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 510 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 511 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 512 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 513 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 514 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 515 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 516 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 517 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 518 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 519 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 520 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 521 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 522 | gopkg.in/jmhodges/yaml.v2 v2.2.5 h1:5fchNBz/LM+T+YHTc6HY7HcuxRUk9AxJdY6xZ07VIkg= 523 | gopkg.in/jmhodges/yaml.v2 v2.2.5/go.mod h1:m5cKkfn71Anarl8lVLAXtU8SyVmqBQx9UIS3JhoI5UI= 524 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 525 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 526 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 527 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 528 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 529 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 530 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 531 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 532 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 533 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 534 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 535 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 536 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 537 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 538 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 539 | -------------------------------------------------------------------------------- /img/frgm.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k1LoW/frgm/b2e9751efe4f16d2f035b7aab79408e2759fe477/img/frgm.afdesign -------------------------------------------------------------------------------- /img/frgm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k1LoW/frgm/b2e9751efe4f16d2f035b7aab79408e2759fe477/img/frgm.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Ken'ichiro Oyama 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | package main 23 | 24 | import "github.com/k1LoW/frgm/cmd" 25 | 26 | func main() { 27 | cmd.Execute() 28 | } 29 | -------------------------------------------------------------------------------- /snippet/snippet.go: -------------------------------------------------------------------------------- 1 | package snippet 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Snippet struct { 9 | UID string `json:"uid,omitempty" yaml:"uid,omitempty"` 10 | Group string `json:"group,omitempty" yaml:"group,omitempty"` 11 | Name string `json:"name" yaml:"name"` 12 | Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` 13 | Content string `json:"content" yaml:"content"` 14 | Output string `json:"output,omitempty" yaml:"output,omitempty"` 15 | Labels []string `json:"labels,omitempty" yaml:"labels,omitempty"` 16 | LoadPath string `json:"-" yaml:"-"` 17 | } 18 | 19 | func (s Snippet) String() string { 20 | j, _ := json.Marshal(s) 21 | return string(j) 22 | } 23 | 24 | // New return new Snippet 25 | func New(u, g, n, c, o, d string, l []string) *Snippet { 26 | s := Snippet{ 27 | UID: u, 28 | Group: g, 29 | Name: n, 30 | Content: c, 31 | Output: o, 32 | Desc: d, 33 | } 34 | if len(l) > 0 { 35 | s.Labels = l 36 | } 37 | return &s 38 | } 39 | 40 | type Snippets []*Snippet 41 | 42 | func (snips Snippets) AddLoadPath(path string) { 43 | for i := range snips { 44 | snips[i].LoadPath = path 45 | } 46 | } 47 | 48 | func (snips Snippets) ClearLoadPath() { 49 | for i := range snips { 50 | snips[i].LoadPath = "" 51 | } 52 | } 53 | 54 | func (snips Snippets) FindByUID(uid string) (*Snippet, error) { 55 | for _, s := range snips { 56 | if uid == s.UID { 57 | return s, nil 58 | } 59 | } 60 | return nil, fmt.Errorf("can not find snippet UID:%s", uid) 61 | } 62 | 63 | func (snips Snippets) Validate() error { 64 | uids := map[string]*Snippet{} 65 | 66 | // check for duplicate 67 | for _, s := range snips { 68 | if _, ok := uids[s.UID]; ok { 69 | return fmt.Errorf("duplicate UID\n%s\n---\n%s", uids[s.UID], s) 70 | } 71 | uids[s.UID] = s 72 | } 73 | 74 | return nil 75 | } 76 | 77 | type SnippetSet struct { 78 | Group string `yaml:"group,omitempty"` 79 | Snippets Snippets `yaml:"snippets"` 80 | LoadPath string `json:"-" yaml:"-"` 81 | } 82 | -------------------------------------------------------------------------------- /snippet/snippet_test.go: -------------------------------------------------------------------------------- 1 | package snippet 2 | 3 | import "testing" 4 | 5 | func TestValidate(t *testing.T) { 6 | snips := Snippets{ 7 | New("uid", "group", "name", "content", "output", "desc", []string{}), 8 | New("uid", "group2", "name2", "content2", "output2", "desc2", []string{}), 9 | } 10 | if err := snips.Validate(); err == nil { 11 | t.Error("got nil want error") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /testdata/md_write_1.md.golden: -------------------------------------------------------------------------------- 1 | ## Snippets 2 | 3 | - group-a 4 | - [a-1](#uid-a-1) 5 | - [a-2](#uid-a-2) 6 | - group-b 7 | - [b-1](#uid-b-1) 8 | 9 | ### group-a 10 | 11 | #### a-1 12 | 13 | ``` 14 | echo a-1 15 | ``` 16 | 17 | **output:** 18 | ``` 19 | out a-1 20 | ``` 21 | 22 | desc a-1 23 | 24 |

25 | label-a label:1 26 |

27 | 28 | #### a-2 29 | 30 | ``` 31 | echo a-2 32 | ``` 33 | 34 |

35 | label-a label:2 36 |

37 | 38 | ### group-b 39 | 40 | #### b-1 41 | 42 | ``` 43 | echo b-1 44 | ``` 45 | 46 | desc b-1 47 | desc b-1 48 | 49 | --- 50 | 51 | > Generated by [frgm](https://github.com/k1LoW/frgm) 52 | -------------------------------------------------------------------------------- /testdata/pet.toml: -------------------------------------------------------------------------------- 1 | [[snippets]] 2 | description = "ping" 3 | command = "ping 8.8.8.8" 4 | tag = ["network", "google"] 5 | output = "" 6 | 7 | [[snippets]] 8 | description = "hello" 9 | command = "echo hello" 10 | tag = ["sample"] 11 | output = "hello" 12 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Name for this 4 | const Name string = "frgm" 5 | 6 | // Version for this 7 | var Version = "0.11.0" 8 | --------------------------------------------------------------------------------