├── .gitignore ├── tool ├── setup └── setup.nim ├── .github ├── pr-labeler.yml ├── workflows │ ├── pr-labeler.yml │ ├── test.yml │ └── release.yml └── release-drafter.yml ├── tests ├── config.nims └── test1.nim ├── nmi.nimble ├── .chglog ├── config.yml └── CHANGELOG.tpl.md ├── LICENSE ├── README.rst └── src └── nmi.nim /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | tests/test1 3 | -------------------------------------------------------------------------------- /tool/setup: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiro4989/nmi/HEAD/tool/setup -------------------------------------------------------------------------------- /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | feature: feature/* 2 | bug: hotfix/* 3 | chore: chore/* 4 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") 2 | switch("hints", "off") 3 | switch("d", "modeTest") 4 | -------------------------------------------------------------------------------- /tests/test1.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | include nmi 4 | 5 | suite "suite": 6 | test "test": 7 | check true 8 | -------------------------------------------------------------------------------- /nmi.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "1.1.0" 4 | author = "jiro4989" 5 | description = "nmi display animations aimed to correct users who accidentally enter nmi instead of nim." 6 | license = "MIT" 7 | srcDir = "src" 8 | bin = @["nmi"] 9 | binDir = "bin" 10 | 11 | 12 | # Dependencies 13 | 14 | requires "nim >= 1.0.6" 15 | -------------------------------------------------------------------------------- /.github/workflows/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | name: labeler 2 | 3 | on: 4 | pull_request: 5 | types: [opened] 6 | 7 | jobs: 8 | labeler: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: TimonVS/pr-labeler-action@v3.1.0 12 | with: 13 | configuration-path: .github/pr-labeler.yml 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | categories: 4 | - title: '🚀 Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: '🐛 Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: '🧰 Maintenance' 14 | label: 'chore' 15 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 16 | template: | 17 | ## Changes 18 | 19 | $CHANGES 20 | -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/YOUR_NAME/REPOSITORY 6 | options: 7 | commits: 8 | filters: 9 | Type: 10 | - feat 11 | - fix 12 | - perf 13 | - refactor 14 | commit_groups: 15 | # title_maps: 16 | # feat: Features 17 | # fix: Bug Fixes 18 | # perf: Performance Improvements 19 | # refactor: Code Refactoring 20 | header: 21 | pattern: "^(\\w*)\\:\\s(.*)$" 22 | pattern_maps: 23 | - Type 24 | - Subject 25 | notes: 26 | keywords: 27 | - BREAKING CHANGE 28 | -------------------------------------------------------------------------------- /.chglog/CHANGELOG.tpl.md: -------------------------------------------------------------------------------- 1 | {{ range .Versions }} 2 | ## Changes 3 | 4 | {{ range .CommitGroups -}} 5 | ### {{ .Title }} 6 | 7 | {{ range .Commits -}} 8 | * {{ .Subject }} 9 | {{ end }} 10 | {{ end -}} 11 | 12 | {{- if .RevertCommits -}} 13 | ### Reverts 14 | 15 | {{ range .RevertCommits -}} 16 | * {{ .Revert.Header }} 17 | {{ end }} 18 | {{ end -}} 19 | 20 | {{- if .MergeCommits -}} 21 | ### Pull Requests 22 | 23 | {{ range .MergeCommits -}} 24 | * {{ .Header }} 25 | {{ end }} 26 | {{ end -}} 27 | 28 | {{- if .NoteGroups -}} 29 | {{ range .NoteGroups -}} 30 | ### {{ .Title }} 31 | 32 | {{ range .Notes }} 33 | {{ .Body }} 34 | {{ end }} 35 | {{ end -}} 36 | {{ end -}} 37 | {{ end -}} 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | NIM_VERSION: '1.2.4' 9 | 10 | jobs: 11 | skip: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - run: echo "Skip job" 15 | 16 | before: 17 | runs-on: ubuntu-latest 18 | if: "! contains(github.event.head_commit.message, '[skip ci]')" 19 | steps: 20 | - run: echo "not contains '[skip ci]'" 21 | 22 | build: 23 | runs-on: ${{ matrix.os }} 24 | strategy: 25 | matrix: 26 | os: 27 | - ubuntu-latest 28 | - windows-latest 29 | - macOS-latest 30 | needs: before 31 | steps: 32 | - uses: actions/checkout@v1 33 | - uses: jiro4989/setup-nim-action@v1 34 | with: 35 | nim-version: ${{ env.NIM_VERSION }} 36 | - run: nimble build -Y 37 | - run: nimble test -Y 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jiro4989 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==== 2 | nmi 3 | ==== 4 | 5 | |nimble-version| |nimble-install| |gh-actions| 6 | 7 | nmi display animations aimed to correct users who accidentally enter nmi instead of nim. 8 | nmi is heavily inspired by ``sl`` . 9 | 10 | |demo_1| 11 | 12 | |demo_2| 13 | 14 | .. contents:: Table of contents 15 | 16 | Usage 17 | ===== 18 | 19 | .. code-block:: shell 20 | 21 | $ nmi 22 | $ nmi -c 23 | 24 | Installation 25 | ============ 26 | 27 | .. code-block:: shell 28 | 29 | $ nimble install -Y nmi 30 | 31 | or 32 | 33 | Download from `Releases `_ 34 | 35 | LICENSE 36 | ======= 37 | 38 | MIT 39 | 40 | .. |gh-actions| image:: https://github.com/jiro4989/nmi/workflows/build/badge.svg 41 | :target: https://github.com/jiro4989/nmi/actions 42 | .. |nimble-version| image:: https://nimble.directory/ci/badges/nmi/version.svg 43 | :target: https://nimble.directory/ci/badges/nmi/nimdevel/output.html 44 | .. |nimble-install| image:: https://nimble.directory/ci/badges/nmi/nimdevel/status.svg 45 | :target: https://nimble.directory/ci/badges/nmi/nimdevel/output.html 46 | .. |demo_1| image:: https://user-images.githubusercontent.com/13825004/83327472-1e57b200-a2b7-11ea-80f3-025d5eba2102.gif 47 | .. |demo_2| image:: https://user-images.githubusercontent.com/13825004/83327473-20ba0c00-a2b7-11ea-8fe9-a8f4e6862d01.gif 48 | -------------------------------------------------------------------------------- /tool/setup.nim: -------------------------------------------------------------------------------- 1 | import strutils, os, strformat, times, parseopt 2 | 3 | const 4 | doc = """ 5 | setup setups this template repository. 6 | 7 | Usage: 8 | setup [options] 9 | setup (-h | --help) 10 | setup --author: --appname: 11 | 12 | Options: 13 | -h, --help Print this help 14 | --author: Set author name 15 | --appname: Set application name 16 | """ 17 | 18 | proc changeFile(beforeFile, afterFile, appName, author, dt: string) = 19 | let body = 20 | readFile(beforeFile) 21 | .replace("APPNAME", appName) 22 | .replace("", author) 23 | .replace("", dt) 24 | writeFile(beforeFile, body) 25 | echo &"{beforeFile} was replaced." 26 | 27 | if afterFile == "": return 28 | moveFile(beforeFile, afterFile) 29 | echo &"{beforeFile} was renamed {afterFile}." 30 | 31 | var 32 | optParser = initOptParser(commandLineParams()) 33 | author = "" 34 | appName = "" 35 | 36 | for kind, key, val in optParser.getopt(): 37 | case kind 38 | of cmdLongOption, cmdShortOption: 39 | case key 40 | of "help", "h": 41 | echo doc 42 | quit 0 43 | of "author": 44 | author = val 45 | of "appname": 46 | appName = val 47 | of cmdEnd: 48 | assert false # cannot happen 49 | else: discard 50 | 51 | if author == "" or appName == "": 52 | stderr.writeLine "'author' and 'appname' must be set." 53 | stderr.writeLine "see 'setup -h'." 54 | quit 1 55 | 56 | let 57 | now = now().format("yyyy") 58 | 59 | changeFile("APPNAME.nimble", &"{appName}.nimble", appName, author, now) 60 | changeFile("README.rst", "", appName, author, now) 61 | changeFile("src"/"APPNAME.nim", "src" / &"{appName}.nim", appName, author, now) 62 | changeFile("tests"/"test1.nim", "", appName, author, now) 63 | changeFile("LICENSE", "", appName, author, now) 64 | changeFile(".github" / "workflows" / "main.yml", "", appName, author, now) 65 | 66 | echo "" 67 | echo "success." 68 | -------------------------------------------------------------------------------- /src/nmi.nim: -------------------------------------------------------------------------------- 1 | import strformat, strutils, parseopt, os, terminal 2 | 3 | type 4 | Options = ref object 5 | args: seq[string] 6 | useHelp, useVersion, color: bool 7 | 8 | const 9 | nimAscii = """ 10 | 11 | ::: 12 | : :::::: :: 13 | :::: ::::::::::::::::::::. :::: 14 | :::::::::::::::::::::::::::::::::::::::::: 15 | :::::::::::::::::::::::::::::::::::::::::::: 16 | ::::::::::::::::::::::::::::::::::::::::::::::::: 17 | ::. :::::::::::::::::::: ::::::::::::::::::::: :: 18 | ::::::::::::::::: ::::::::::::::::: 19 | ::::::::::::: ::::::::::::: 20 | ::::::::::: :::::::::: 21 | :::::::: :::::::: 22 | :::::: :::::: 23 | :::: ::::. 24 | *::. ::: 25 | %@@ @%% 26 | @@@ @@ #@@@ 27 | @@@@ #@@@@@@@ @@@@ 28 | @@@@# =@@@@@@@@@@@@@ @@@@@ 29 | @@@@@@@% @@@@@@@@@@@@@@@@@@@ @@@@@@@ 30 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 31 | *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 32 | *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 33 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 34 | +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 35 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 36 | +@@@@@@@@@@@@@@@@@@@@@@@@@@@ 37 | +@@@@@@@@@@@@@@@@@@@ 38 | +@@@ 39 | """ 40 | appName = "nmi" 41 | version = &"""{appName} command version 1.1.0 42 | Copyright (c) 2020 jiro4989 43 | Released under the MIT License. 44 | https://github.com/jiro4989/nmi""" 45 | doc = &""" 46 | {appName} display animations aimed to correct users who accidentally enter nmi instead of nim. 47 | 48 | Usage: 49 | {appName} [options] 50 | {appName} (-h | --help) 51 | {appName} (-v | --version) 52 | 53 | Options: 54 | -h, --help Print this help 55 | -v, --version Print version 56 | -c, --color Colorful print 57 | """ 58 | 59 | proc getCmdOpts(params: seq[string]): Options = 60 | ## コマンドライン引数を解析して返す。 61 | ## helpとversionが見つかったらテキストを標準出力して早期リターンする。 62 | var optParser = initOptParser(params) 63 | new result 64 | 65 | for kind, key, val in optParser.getopt(): 66 | case kind 67 | of cmdArgument: 68 | result.args.add(key) 69 | of cmdLongOption, cmdShortOption: 70 | case key 71 | of "help", "h": 72 | echo doc 73 | result.useHelp = true 74 | return 75 | of "version", "v": 76 | echo version 77 | result.useVersion = true 78 | return 79 | of "color", "c": 80 | result.color = true 81 | of cmdEnd: 82 | assert false # cannot happen 83 | 84 | proc colorful(text: string): string = 85 | let yellow = ":." 86 | let black = "@%#*+=" 87 | result = text 88 | result = result.replace(" ", &"\x1b[47m \x1b[0m") 89 | for c in yellow: 90 | result = result.replace($c, &"\x1b[43m \x1b[0m") 91 | for c in black: 92 | result = result.replace($c, &"\x1b[40m \x1b[0m") 93 | 94 | proc traverse(text: string, color: bool) = 95 | let width = terminalWidth() - 5 96 | let asciiHeight = text.split("\n").len 97 | let lines = text.split("\n") 98 | var i: int 99 | for leftPadWidth in countdown(width, 0): 100 | var buf: seq[string] 101 | for line in lines: 102 | if line.len < 1: continue 103 | let leftPad = " ".repeat(leftPadWidth) 104 | if line.len <= i: i = line.len - 1 105 | let logoPart = line[0..i] 106 | buf.add(leftPad & logoPart) 107 | 108 | var t = buf.join("\n") 109 | if color: 110 | t = t.colorful 111 | echo t 112 | 113 | cursorUp(asciiHeight - 1) 114 | sleep 50 115 | inc i 116 | 117 | for x in 0..lines[0].len: 118 | var buf: seq[string] 119 | for line in lines: 120 | if line.len < 1: continue 121 | buf.add(line[x..^1]) 122 | 123 | var t = buf.join("\n") 124 | if color: 125 | t = t.colorful 126 | echo t 127 | 128 | cursorUp(asciiHeight - 1) 129 | sleep 50 130 | 131 | when isMainModule and not defined modeTest: 132 | when not defined windows: 133 | from posix import onSignal, SIGTERM, SIGINT 134 | onSignal(SIGTERM, SIGINT): 135 | discard 136 | 137 | let opts = commandLineParams().getCmdOpts() 138 | if opts.useHelp or opts.useVersion: 139 | quit 0 140 | 141 | hideCursor() 142 | eraseScreen() 143 | setCursorPos(0, 0) 144 | traverse(nimAscii, opts.color) 145 | 146 | proc closeFunc() {.noconv.} = 147 | showCursor() 148 | eraseScreen() 149 | quit 1 150 | 151 | setControlCHook(closeFunc) 152 | 153 | showCursor() 154 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | env: 9 | APP_NAME: 'nmi' 10 | NIM_VERSION: '1.2.4' 11 | MAINTAINER: 'jiro4989' 12 | DESC: 'nmi display animations aimed to correct users who accidentally enter nmi instead of nim.' 13 | 14 | jobs: 15 | build-artifact: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - windows-latest 22 | - macOS-latest 23 | steps: 24 | - uses: actions/checkout@v1 25 | - uses: jiro4989/setup-nim-action@v1 26 | with: 27 | nim-version: ${{ env.NIM_VERSION }} 28 | - run: nimble build -Y -d:release 29 | - name: Create artifact 30 | run: | 31 | os="${{ runner.os }}" 32 | assets="${{ env.APP_NAME }}_$(echo "${{ runner.os }}" | tr '[:upper:]' '[:lower:]')" 33 | echo "$assets" 34 | mkdir -p "dist/$assets" 35 | cp -r bin LICENSE README.* "dist/$assets/" 36 | ( 37 | cd dist 38 | if [[ "${{ runner.os }}" == Windows ]]; then 39 | 7z a "$assets.zip" "$assets" 40 | else 41 | tar czf "$assets.tar.gz" "$assets" 42 | fi 43 | ls -lah *.* 44 | ) 45 | shell: bash 46 | - uses: actions/upload-artifact@v2 47 | with: 48 | name: artifact-${{ matrix.os }} 49 | path: | 50 | dist/*.tar.gz 51 | dist/*.zip 52 | 53 | build-linux-packages: 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v1 57 | - uses: jiro4989/setup-nim-action@v1 58 | with: 59 | nim-version: ${{ env.NIM_VERSION }} 60 | - run: nimble build -Y -d:release 61 | 62 | - name: create sample script 63 | run: | 64 | mkdir -p .debpkg/usr/bin 65 | mkdir -p .rpmpkg/usr/bin 66 | cp -p bin/* .debpkg/usr/bin/ 67 | cp -p bin/* .rpmpkg/usr/bin/ 68 | - uses: jiro4989/build-deb-action@v2 69 | with: 70 | package: ${{ env.APP_NAME }} 71 | package_root: .debpkg 72 | maintainer: ${{ env.MAINTAINER }} 73 | version: ${{ github.ref }} 74 | arch: 'amd64' 75 | desc: '${{ env.DESC }}' 76 | 77 | - uses: jiro4989/build-rpm-action@v2 78 | with: 79 | summary: '${{ env.DESC }}' 80 | package: ${{ env.APP_NAME }} 81 | package_root: .rpmpkg 82 | maintainer: ${{ env.MAINTAINER }} 83 | version: ${{ github.ref }} 84 | arch: 'x86_64' 85 | desc: '${{ env.DESC }}' 86 | 87 | - uses: actions/upload-artifact@v2 88 | with: 89 | name: artifact-deb 90 | path: | 91 | ./*.deb 92 | 93 | - uses: actions/upload-artifact@v2 94 | with: 95 | name: artifact-rpm 96 | path: | 97 | ./*.rpm 98 | !./*-debuginfo-*.rpm 99 | 100 | create-release: 101 | runs-on: ubuntu-latest 102 | needs: 103 | - build-artifact 104 | - build-linux-packages 105 | steps: 106 | - uses: actions/checkout@v1 107 | - name: Generate changelog 108 | run: | 109 | wget https://github.com/git-chglog/git-chglog/releases/download/0.9.1/git-chglog_linux_amd64 110 | chmod +x git-chglog_linux_amd64 111 | mv git-chglog_linux_amd64 git-chglog 112 | ./git-chglog --output ./changelog $(git describe --tags $(git rev-list --tags --max-count=1)) 113 | 114 | - name: Create Release 115 | id: create-release 116 | uses: actions/create-release@v1 117 | env: 118 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 119 | with: 120 | tag_name: ${{ github.ref }} 121 | release_name: ${{ github.ref }} 122 | body_path: ./changelog 123 | draft: false 124 | prerelease: false 125 | 126 | - name: Write upload_url to file 127 | run: echo '${{ steps.create-release.outputs.upload_url }}' > upload_url.txt 128 | 129 | - uses: actions/upload-artifact@v2 130 | with: 131 | name: create-release 132 | path: upload_url.txt 133 | 134 | upload-release: 135 | runs-on: ubuntu-latest 136 | needs: create-release 137 | strategy: 138 | matrix: 139 | include: 140 | - os: ubuntu-latest 141 | asset_name_suffix: linux.tar.gz 142 | asset_content_type: application/gzip 143 | - os: windows-latest 144 | asset_name_suffix: windows.zip 145 | asset_content_type: application/zip 146 | - os: macOS-latest 147 | asset_name_suffix: macos.tar.gz 148 | asset_content_type: application/gzip 149 | steps: 150 | - uses: actions/download-artifact@v2 151 | with: 152 | name: artifact-${{ matrix.os }} 153 | 154 | - uses: actions/download-artifact@v2 155 | with: 156 | name: create-release 157 | 158 | - id: vars 159 | run: | 160 | echo "::set-output name=upload_url::$(cat upload_url.txt)" 161 | 162 | - name: Upload Release Asset 163 | id: upload-release-asset 164 | uses: actions/upload-release-asset@v1 165 | env: 166 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 167 | with: 168 | upload_url: ${{ steps.vars.outputs.upload_url }} 169 | asset_path: ${{ env.APP_NAME }}_${{ matrix.asset_name_suffix }} 170 | asset_name: ${{ env.APP_NAME }}_${{ matrix.asset_name_suffix }} 171 | asset_content_type: ${{ matrix.asset_content_type }} 172 | 173 | upload-linux-packages: 174 | runs-on: ubuntu-latest 175 | needs: create-release 176 | strategy: 177 | matrix: 178 | include: 179 | - pkg: deb 180 | asset_content_type: application/vnd.debian.binary-package 181 | - pkg: rpm 182 | asset_content_type: application/x-rpm 183 | steps: 184 | - uses: actions/download-artifact@v2 185 | with: 186 | name: artifact-${{ matrix.pkg }} 187 | 188 | - uses: actions/download-artifact@v2 189 | with: 190 | name: create-release 191 | 192 | - id: vars 193 | run: | 194 | echo "::set-output name=upload_url::$(cat upload_url.txt)" 195 | echo "::set-output name=asset_name::$(ls *.${{ matrix.pkg }} | head -n 1)" 196 | 197 | - name: Upload Release Asset 198 | id: upload-release-asset 199 | uses: actions/upload-release-asset@v1 200 | env: 201 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 202 | with: 203 | upload_url: ${{ steps.vars.outputs.upload_url }} 204 | asset_path: ${{ steps.vars.outputs.asset_name }} 205 | asset_name: ${{ steps.vars.outputs.asset_name }} 206 | asset_content_type: ${{ matrix.asset_content_type }} 207 | --------------------------------------------------------------------------------