├── .eslintrc.json ├── .github └── workflows │ └── patch.yaml ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── BASE.md ├── CHANGELOG.md ├── COMING_SOON.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SETTINGS.md ├── assets ├── demo.gif ├── demo_old.gif ├── icon_black.png ├── icon_black.svg ├── icon_light.png ├── icon_light.svg └── logo.png ├── package.json ├── sandbox ├── .eslintrc.json ├── .gitignore ├── README.md ├── components │ ├── CanvasTitle.tsx │ ├── CodeTitle.tsx │ ├── DefaultVariablesValuesInput.tsx │ ├── DefaultVariablesValuesRenderer.tsx │ ├── InsertionModeInput.tsx │ ├── InsertionModeRenderer.tsx │ ├── TemplateInput.tsx │ ├── TemplateInput.utils.tsx │ ├── TemplateRenderer.tsx │ ├── VariableAdd.tsx │ ├── VariablesDisplayTitlesInput.tsx │ ├── VariablesDisplayTitlesRenderer.tsx │ ├── VariablesInput.tsx │ ├── VariablesInputElement.tsx │ ├── VariablesInputElementCustom.tsx │ ├── VariablesRenderer.tsx │ ├── core │ │ ├── DefaultVariableValueInput.tsx │ │ ├── Menu.tsx │ │ ├── Pre.tsx │ │ ├── SelectString.tsx │ │ ├── VariableArrayInput.tsx │ │ ├── VariableBranchInput.tsx │ │ ├── VariableDisplayTitleInput.tsx │ │ ├── VariableFileInput.tsx │ │ ├── VariableInput.tsx │ │ ├── VariableMergeInput.tsx │ │ └── VariablePredefinedInput.tsx │ └── md │ │ ├── code.tsx │ │ ├── em.tsx │ │ ├── index.ts │ │ └── vs.ts ├── generated │ └── doc.md.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages │ ├── _app.tsx │ ├── api │ │ └── hello.ts │ ├── configurator.tsx │ └── index.tsx ├── public │ ├── assets │ │ ├── demo.gif │ │ ├── demo_old.gif │ │ ├── icon_black.png │ │ ├── icon_black.svg │ │ ├── icon_light.png │ │ ├── icon_light.svg │ │ └── logo.png │ ├── favicon.ico │ ├── logo.png │ └── vercel.svg ├── styles │ ├── CanvasTitle.module.css │ ├── CodeTitle.module.css │ ├── Configurator.module.css │ ├── DefaultVariablesValuesInput.module.css │ ├── Home.module.css │ ├── Input.module.css │ ├── Pre.module.css │ ├── SelectString.module.css │ ├── StringArrayInput.module.css │ ├── StringInput.module.css │ ├── VariableAdd.module.css │ ├── VariableInput.module.css │ └── globals.css ├── tsconfig.json ├── typings │ ├── Editor.d.ts │ ├── Renderer.d.ts │ └── Store.d.ts ├── utils │ ├── store.tsx │ ├── template.ts │ └── variables.ts └── yarn.lock ├── scripts ├── mdConcatenate.js ├── mdCopySandbox.js ├── semver.js └── version.js ├── share ├── README.md ├── caiomizerkowski.md ├── default.json ├── default.md ├── diogorossi.md └── wilsonmar.md ├── src ├── config │ ├── const.ts │ ├── constants.ts │ ├── dynamics.ts │ └── presets.ts ├── index.ts ├── scripts │ └── run.ts ├── typings │ ├── action.d.ts │ ├── git.d.ts │ ├── quickPick.d.ts │ └── settings.d.ts └── utils │ ├── actions.ts │ ├── feedback.ts │ ├── files.ts │ ├── git.ts │ ├── migrate.ts │ ├── settings.ts │ ├── storage.ts │ ├── strings.ts │ ├── template.ts │ └── variables.ts ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/semi": "warn", 11 | "curly": "warn", 12 | "eqeqeq": "warn", 13 | "no-throw-literal": "warn", 14 | "semi": "off", 15 | "quotes": ["warn", "single"], 16 | "indent": ["off", 2] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/patch.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | paths-ignore: # dont run when changes made to these folders 6 | - '.vscode/**' 7 | - 'sandbox/**' 8 | - 'share/**' 9 | - 'scripts/**' 10 | 11 | jobs: 12 | extension: 13 | name: extension 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Fetch 20 | run: git fetch --prune --unshallow 21 | 22 | - name: Setup node 16.x 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: '16.x' 26 | 27 | - name: Setup yarn 28 | run: npm i -g yarn 29 | 30 | - name: Setup dependencies 31 | run: yarn 32 | 33 | - name: Combine markdowns 34 | run: yarn md:concatenate 35 | 36 | - name: Export version 37 | id: version 38 | run: echo "::set-output name=new::1.0.0" 39 | 40 | - name: Use version 41 | run: echo "${{steps.version.new}}" 42 | 43 | # Update version number 44 | 45 | - name: Build Sources 46 | run: yarn extension:build 47 | 48 | # - name: Compile VSIX 49 | # run: yarn extension:pack 50 | 51 | # - name: Upload artifact 52 | # uses: actions/upload-artifact@v1 53 | # with: 54 | # name: vscode-git-commit-3.1.1.vsix 55 | # path: ${{github.workspace}}/vscode-git-commit-3.1.1.vsix 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | package-lock.json 5 | assets/logo.pdn 6 | assets/logoold.png 7 | /vscode-git-commit-*.vsix 8 | /yarn-error.log 9 | assets/Preview/ 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": ["dbaeumer.vscode-eslint", "rioukkevin.vscode-git-commit"] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Debug", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 15 | "preLaunchTask": "${defaultBuildTask}" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off", 11 | 12 | // "vscodeGitCommit.messageTemplate": ["{prefix} ({scope}): {message}"], 13 | 14 | // "vscodeGitCommit.insertMode": "Concatenate", 15 | 16 | // "vscodeGitCommit.predefinedPrefix": "Alpha8", 17 | 18 | // "vscodeGitCommit.customAlias": [ 19 | // { 20 | // "name": "🚀 Space", 21 | // "description": "Choose when pushing in canary at SpaceX" 22 | // }, 23 | // { 24 | // "name": "😝 Easter-Egg", 25 | // "description": "Use when comitting easter-eggs for example" 26 | // } 27 | // ] 28 | "vscodeGitCommit.insertMode": "Concatenate", 29 | "vscodeGitCommit.template": ["{feat} ({scope}): {message}"], 30 | "vscodeGitCommit.variables": { 31 | "feat": "alpha8" 32 | }, 33 | "vscodeGitCommit.defaultVariablesValues": { 34 | "scope": "😉" 35 | }, 36 | "vscodeGitCommit.variablesDisplayTitles": { 37 | "prefix": "Here is a title in config for testing purpose", 38 | "scope": "The scope represent a global one word link to changes" 39 | }, 40 | "workbench.colorCustomizations": { 41 | "activityBar.activeBackground": "#e36b61", 42 | "activityBar.activeBorder": "#136c1a", 43 | "activityBar.background": "#e36b61", 44 | "activityBar.foreground": "#15202b", 45 | "activityBar.inactiveForeground": "#15202b99", 46 | "activityBarBadge.background": "#136c1a", 47 | "activityBarBadge.foreground": "#e7e7e7", 48 | "sash.hoverBorder": "#e36b61", 49 | "commandCenter.border": "#e7e7e799", 50 | "statusBar.background": "#db4236", 51 | "statusBar.foreground": "#e7e7e7", 52 | "statusBarItem.hoverBackground": "#e36b61", 53 | "statusBarItem.remoteBackground": "#db4236", 54 | "statusBarItem.remoteForeground": "#e7e7e7", 55 | "titleBar.activeBackground": "#db4236", 56 | "titleBar.activeForeground": "#e7e7e7", 57 | "titleBar.inactiveBackground": "#db423699", 58 | "titleBar.inactiveForeground": "#e7e7e799" 59 | }, 60 | "peacock.color": "#DB4236" 61 | } 62 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | sandbox/** 12 | -------------------------------------------------------------------------------- /BASE.md: -------------------------------------------------------------------------------- 1 | ![demo](./assets/demo.gif) 2 | 3 | # What is it ? 4 | 5 | This extension is an extension made to formalize git commit messages. By using a template and variables, you can define how the commit message needs to looks and which data you want in this message. 6 | 7 | By example: When you want to specify a **scope** in your commits, you can create a variable named scope in th e template like this `{scope}` 8 | 9 | If you want to specify which data you want in this scope variable, you can specify it with the variable settings 10 | 11 | ```json 12 | { 13 | "scope": [ 14 | { 15 | "label": "🟢 App" 16 | }, 17 | { 18 | "label": "🟠 Api" 19 | }, 20 | { 21 | "label": "🔵 Scripts" 22 | } 23 | ] 24 | } 25 | ``` 26 | 27 | I show you more on the #Settings part 28 | 29 | toto 30 | 31 | ## How to use it ? 32 | 33 | Use _ctrl_+_alt_+_enter_ (_cmd_+_shift_+_enter_ on Mac) or `click` on the icon in SouceControl 34 | ![bl](./assets/icon_black.png) 35 | ![li](./assets/icon_light.png) 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | #### **3.2.0** (_01-30-2024_) 4 | 5 | - ✨feature: Trim messages when finished 6 | - ✨feature: Ability to define titles on prompts 7 | - ✨feature: Ability to define default values on prompts 8 | 9 | #### **3.1.1** (_09-30-2022_) 10 | 11 | - ✨feature: Trim messages when finished 12 | - ✨feature: Ability to define titles on prompts 13 | - ✨feature: Ability to define default values on prompts 14 | 15 | #### **3.1.0** (_08-05-2022_) 16 | 17 | - 🐞fix: update for vscode api change in 1.70 about repositories 18 | 19 | #### **3.0.2** (_07-16-2022_) 20 | 21 | - ✨feature: ability to define default value for free text inputs 22 | - ✨feature: add repo current branch in variables 23 | 24 | #### **3.0.1** (_02-24-2022_) 25 | 26 | - 🐞fix: settings migration script fix from V3.0.0 27 | 28 | > third release with an UI O_o and a lot of new features (btw: we reached the 5k installs THX U ♥️) 29 | 30 | #### **3.0.0** (_02-23-2022_) 31 | 32 | - ✨feature: use of information or error message as feedback 33 | - ✨feature: update contributes.configuration $schema 34 | - ✨feature: add an UI to generate settings (cf: this [UI](https://gcm-config.netlify.app/configurator)) 35 | - ✨feature: add PR template 36 | - ✨feature: add a folder for settings sharing between users 37 | - ✨feature: add ability to search for files using predefined variable names 38 | - 📄docs (docs): update docs globaly and make a web version 39 | 40 | #### **2.1.0** (_05-06-2021_) 41 | 42 | - ✨feature: auto focus scm commit input on finish (cf: issues [#10](https://github.com/rioukkevin/vscode-git-commit/issues/10)) 43 | - ✨feature: add abilities to concatenate multiple variables in settings (cf: issues [#11](https://github.com/rioukkevin/vscode-git-commit/issues/11) & [#12](https://github.com/rioukkevin/vscode-git-commit/issues/12)) 44 | 45 | > second release OH YEAHHH !!!! 46 | 47 | #### **2.0.0** (_04-09-2021_) 48 | 49 | - ✨feature: add ability to use text input or select input for any variables 50 | 51 | #### **1.1.5** (_12-18-2020_) 52 | 53 | - 📄docs (docs): update docs with animated gif 54 | 55 | #### **1.1.4** (_11-06-2020_) 56 | 57 | - ✨feature: Handle multi repo case 58 | 59 | #### **1.1.3** (_11-06-2020_) 60 | 61 | - 🐞fix: Update icon (cf: issues [#4](https://github.com/rioukkevin/vscode-git-commit/issues/4)) 62 | - 🐞fix: Handle cancel action when typing variables (cf: issues [#5](https://github.com/rioukkevin/vscode-git-commit/issues/5)) 63 | ⚙️refactor: Refacto on extension command name 64 | 65 | #### **1.1.1** (_10-20-2020_) 66 | 67 | - 🐞fix: Update icon (cf: issues [#4](https://github.com/rioukkevin/vscode-git-commit/issues/4)) 68 | 69 | #### **1.1.0** (_10-12-2020_) 70 | 71 | - ✨feat(workflow): Add abilities to create custom message format using simple brackets in settings, see doc 72 | - ⚙️refactor(global): Refacto on major part of the code 73 | - 🌈style(typescript/prettier): Pass into prettier + update ts rules 74 | 75 | #### **1.0.1** (_09-30-2020_) 76 | 77 | - ✨feature: On demand, add based commitizen prefix for alpha8 78 | - 🔵other: Change default mode to concatenate settings 79 | 80 | > First Release, I've done it !! 81 | 82 | #### **1.0.0** (_09-21-2020_) 83 | 84 | - ✨feature: Replace icon with outlined 85 | 86 | #### **0.0.4** (_09-15-2020_) 87 | 88 | - ✨feature: add a mode to concatenate message with existing or replace existing 89 | - 🐞fix: focus on quickPick not on scm input box when triggering extension 90 | 91 | #### **0.0.3** (_08-25-2020_) 92 | 93 | - ✨feature: V0.0.3 add prefix sets 94 | 95 | #### **0.0.2** (_08-23-2020_) 96 | 97 | - ✨feature: Add custom prefix setting 98 | - 🐞fix: Open SCM view when prefix selector is opened, not at the end of process 99 | 100 | #### **0.0.1** (_08-18-2020_) 101 | 102 | - Initial release 103 | -------------------------------------------------------------------------------- /COMING_SOON.md: -------------------------------------------------------------------------------- 1 | ## Migration 2 | 3 | - Update removing key 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the extension 2 | 3 | #### Share configs 4 | 5 | You can share the config you made for you by creating a pull request, in the folder 'share' you can find a Readme as a template and an example made by [jycouet](https://github.com/jycouet) 6 | 7 | #### Contributing to code base 8 | 9 | You just have to create a pull-request 😉 with what you want and a clear description of the goal of your feature 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 RIOU Kevin 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.md: -------------------------------------------------------------------------------- 1 | ![demo](./assets/demo.gif) 2 | 3 | # What is it ? 4 | 5 | This extension is an extension made to formalize git commit messages. By using a template and variables, you can define how the commit message needs to looks and which data you want in this message. 6 | 7 | By example: When you want to specify a **scope** in your commits, you can create a variable named scope in th e template like this `{scope}` 8 | 9 | If you want to specify which data you want in this scope variable, you can specify it with the variable settings 10 | 11 | ```json 12 | { 13 | "scope": [ 14 | { 15 | "label": "🟢 App" 16 | }, 17 | { 18 | "label": "🟠 Api" 19 | }, 20 | { 21 | "label": "🔵 Scripts" 22 | } 23 | ] 24 | } 25 | ``` 26 | 27 | I show you more on the #Settings part 28 | 29 | toto 30 | 31 | ## How to use it ? 32 | 33 | Use _ctrl_+_alt_+_enter_ (_cmd_+_shift_+_enter_ on Mac) or `click` on the icon in SouceControl 34 | ![bl](./assets/icon_black.png) 35 | ![li](./assets/icon_light.png) 36 | 37 | # Settings 38 | 39 | ### UI 40 | 41 | An UI is available [HERE](https://gcm-config.netlify.app/configurator), it's not the best UI but it can help you make your first configuration for the extension 42 | 43 | **Hosted on Netlify** 44 | 45 | ### **Erase previous commit on new one** (`vscodeGitCommit.insertMode`) 46 | 47 | ```json 48 | { 49 | "vscodeGitCommit.insertMode": "Concatenate" 50 | } 51 | ``` 52 | 53 | 'Concatenate' give the ability to use multiple message in the same commit where 'Replace' not 54 | 55 | ### **Create my own template message** (`vscodeGitCommit.template`) 56 | 57 | ```json 58 | { 59 | "vscodeGitCommit.template": [ 60 | "{feat}({scope}): {message}" 61 | "by {author}" 62 | ] 63 | } 64 | ``` 65 | 66 | By defining one string, you create a line, with two strings like in the example, you define two line for the template. 67 | 68 | You can define dynamic content by using _{_ & _}_ in doc, I called it a variable. 69 | 70 | When triggering this extension, a value for each variable is prompt. 71 | 72 | ### **Use select or text for each variables** (`vscodeGitCommit.variables`) 73 | 74 | ```json 75 | { 76 | "vscodeGitCommit.variables": { 77 | "author": [ 78 | { 79 | "label": "Devs", 80 | "detail": "Use when a change is made by Developers" 81 | }, 82 | { 83 | "label": "Ops" 84 | } 85 | ], 86 | "feat": "keke" 87 | } 88 | } 89 | ``` 90 | 91 | For each variables defined in the template above, you can define the content: 92 | 93 | - If not set -> Free string input 94 | - If it's an Array -> A select of choices is displayed 95 | - If it's a string three possibilities 96 | - the value is oneOf 'keke' | 'angular' | 'semantic' | 'alpha8' -> A predefined Array is associated to the variable 97 | - the value startWith 'files' -> An array of files with defined status is displayed as choices 98 | - the value looks like '<>...<>' -> a merge of array between the variable 'something' and 'something_else' is created as choices 99 | 100 | #### If you write your own Array 101 | 102 | - The 'detail' property is optionnal 103 | - The 'label' property is used as the content include in template 104 | - (For old users) The 'id' property is now automatically generated and not used anymore by the extension 105 | 106 | #### If you want predefined choices 107 | 108 | - 'keke' is the prefix I use personnally 109 | - 'angular' is prefix specific to angular repos (HELP: if someone has a full config for angular commits, can you share it with me by creating a PR or Issue ?) 110 | - 'alpha8' is prefix we used in the enterprise where I work 111 | - 'semantic' is an other normalization of prefix but I lost the link associated :/ 112 | 113 | #### If you want to list files many possibilities are yours 114 | 115 | - 'files': All staged and changed files 116 | - 'files.deleted': All deleted staged and changed files 117 | - 'files.modified': All modified staged and changed files 118 | - 'files.added': All added staged and changed files 119 | - 'files.staged': All staged files 120 | - 'files.staged.deleted': All deleted staged files 121 | - 'files.staged.modified': All modified staged files 122 | - 'files.staged.added': All added staged files 123 | - 'files.changed': All changed files 124 | - 'files.changed.deleted': All deleted changed files 125 | - 'files.changed.modified': All modified changed files 126 | - 'files.changed.added': All added changed files 127 | 128 | #### If you want to add branch name in commit, you can use this 129 | 130 | 'branch': Show you a select with the choice between short and long name for current branch 131 | 132 | ### **Define some default values for free input variables** (`vscodeGitCommit.defaultVariablesValues`) 133 | 134 | ```json 135 | { 136 | "vscodeGitCommit.defaultVariablesValues": { 137 | "author": "@RIOU Kevin" 138 | } 139 | } 140 | ``` 141 | 142 | For each variables defined in the template not used in `variables` setting, you can define a default value pretyped in the input 143 | 144 | In the upside example, for the variable `author`, the input is prefill with `@RIOU Kevin`. 145 | 146 | ### **Define titles display on the input** (`vscodeGitCommit.variablesDisplayTitles`) 147 | 148 | ```json 149 | { 150 | "vscodeGitCommit.variablesDisplayTitles": { 151 | "author": "Fill the pseudo of the person commiting with an @" 152 | } 153 | } 154 | ``` 155 | 156 | For each variables prompt, you can define a default value pretyped in the input 157 | 158 | In the upside example, for the variable `author`, the input will show the title `Fill the pseudo of the person commiting with an @`. 159 | 160 | # Contributing to the extension 161 | 162 | #### Share configs 163 | 164 | You can share the config you made for you by creating a pull request, in the folder 'share' you can find a Readme as a template and an example made by [jycouet](https://github.com/jycouet) 165 | 166 | #### Contributing to code base 167 | 168 | You just have to create a pull-request 😉 with what you want and a clear description of the goal of your feature 169 | 170 | # Changelog 171 | 172 | #### **3.2.0** (_01-30-2024_) 173 | 174 | - ✨feature: Trim messages when finished 175 | - ✨feature: Ability to define titles on prompts 176 | - ✨feature: Ability to define default values on prompts 177 | 178 | #### **3.1.1** (_09-30-2022_) 179 | 180 | - ✨feature: Trim messages when finished 181 | - ✨feature: Ability to define titles on prompts 182 | - ✨feature: Ability to define default values on prompts 183 | 184 | #### **3.1.0** (_08-05-2022_) 185 | 186 | - 🐞fix: update for vscode api change in 1.70 about repositories 187 | 188 | #### **3.0.2** (_07-16-2022_) 189 | 190 | - ✨feature: ability to define default value for free text inputs 191 | - ✨feature: add repo current branch in variables 192 | 193 | #### **3.0.1** (_02-24-2022_) 194 | 195 | - 🐞fix: settings migration script fix from V3.0.0 196 | 197 | > third release with an UI O_o and a lot of new features (btw: we reached the 5k installs THX U ♥️) 198 | 199 | #### **3.0.0** (_02-23-2022_) 200 | 201 | - ✨feature: use of information or error message as feedback 202 | - ✨feature: update contributes.configuration $schema 203 | - ✨feature: add an UI to generate settings (cf: this [UI](https://gcm-config.netlify.app/configurator)) 204 | - ✨feature: add PR template 205 | - ✨feature: add a folder for settings sharing between users 206 | - ✨feature: add ability to search for files using predefined variable names 207 | - 📄docs (docs): update docs globaly and make a web version 208 | 209 | #### **2.1.0** (_05-06-2021_) 210 | 211 | - ✨feature: auto focus scm commit input on finish (cf: issues [#10](https://github.com/rioukkevin/vscode-git-commit/issues/10)) 212 | - ✨feature: add abilities to concatenate multiple variables in settings (cf: issues [#11](https://github.com/rioukkevin/vscode-git-commit/issues/11) & [#12](https://github.com/rioukkevin/vscode-git-commit/issues/12)) 213 | 214 | > second release OH YEAHHH !!!! 215 | 216 | #### **2.0.0** (_04-09-2021_) 217 | 218 | - ✨feature: add ability to use text input or select input for any variables 219 | 220 | #### **1.1.5** (_12-18-2020_) 221 | 222 | - 📄docs (docs): update docs with animated gif 223 | 224 | #### **1.1.4** (_11-06-2020_) 225 | 226 | - ✨feature: Handle multi repo case 227 | 228 | #### **1.1.3** (_11-06-2020_) 229 | 230 | - 🐞fix: Update icon (cf: issues [#4](https://github.com/rioukkevin/vscode-git-commit/issues/4)) 231 | - 🐞fix: Handle cancel action when typing variables (cf: issues [#5](https://github.com/rioukkevin/vscode-git-commit/issues/5)) 232 | ⚙️refactor: Refacto on extension command name 233 | 234 | #### **1.1.1** (_10-20-2020_) 235 | 236 | - 🐞fix: Update icon (cf: issues [#4](https://github.com/rioukkevin/vscode-git-commit/issues/4)) 237 | 238 | #### **1.1.0** (_10-12-2020_) 239 | 240 | - ✨feat(workflow): Add abilities to create custom message format using simple brackets in settings, see doc 241 | - ⚙️refactor(global): Refacto on major part of the code 242 | - 🌈style(typescript/prettier): Pass into prettier + update ts rules 243 | 244 | #### **1.0.1** (_09-30-2020_) 245 | 246 | - ✨feature: On demand, add based commitizen prefix for alpha8 247 | - 🔵other: Change default mode to concatenate settings 248 | 249 | > First Release, I've done it !! 250 | 251 | #### **1.0.0** (_09-21-2020_) 252 | 253 | - ✨feature: Replace icon with outlined 254 | 255 | #### **0.0.4** (_09-15-2020_) 256 | 257 | - ✨feature: add a mode to concatenate message with existing or replace existing 258 | - 🐞fix: focus on quickPick not on scm input box when triggering extension 259 | 260 | #### **0.0.3** (_08-25-2020_) 261 | 262 | - ✨feature: V0.0.3 add prefix sets 263 | 264 | #### **0.0.2** (_08-23-2020_) 265 | 266 | - ✨feature: Add custom prefix setting 267 | - 🐞fix: Open SCM view when prefix selector is opened, not at the end of process 268 | 269 | #### **0.0.1** (_08-18-2020_) 270 | 271 | - Initial release 272 | -------------------------------------------------------------------------------- /SETTINGS.md: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | ### UI 4 | 5 | An UI is available [HERE](https://gcm-config.netlify.app/configurator), it's not the best UI but it can help you make your first configuration for the extension 6 | 7 | **Hosted on Netlify** 8 | 9 | ### **Erase previous commit on new one** (`vscodeGitCommit.insertMode`) 10 | 11 | ```json 12 | { 13 | "vscodeGitCommit.insertMode": "Concatenate" 14 | } 15 | ``` 16 | 17 | 'Concatenate' give the ability to use multiple message in the same commit where 'Replace' not 18 | 19 | ### **Create my own template message** (`vscodeGitCommit.template`) 20 | 21 | ```json 22 | { 23 | "vscodeGitCommit.template": [ 24 | "{feat}({scope}): {message}" 25 | "by {author}" 26 | ] 27 | } 28 | ``` 29 | 30 | By defining one string, you create a line, with two strings like in the example, you define two line for the template. 31 | 32 | You can define dynamic content by using _{_ & _}_ in doc, I called it a variable. 33 | 34 | When triggering this extension, a value for each variable is prompt. 35 | 36 | ### **Use select or text for each variables** (`vscodeGitCommit.variables`) 37 | 38 | ```json 39 | { 40 | "vscodeGitCommit.variables": { 41 | "author": [ 42 | { 43 | "label": "Devs", 44 | "detail": "Use when a change is made by Developers" 45 | }, 46 | { 47 | "label": "Ops" 48 | } 49 | ], 50 | "feat": "keke" 51 | } 52 | } 53 | ``` 54 | 55 | For each variables defined in the template above, you can define the content: 56 | 57 | - If not set -> Free string input 58 | - If it's an Array -> A select of choices is displayed 59 | - If it's a string three possibilities 60 | - the value is oneOf 'keke' | 'angular' | 'semantic' | 'alpha8' -> A predefined Array is associated to the variable 61 | - the value startWith 'files' -> An array of files with defined status is displayed as choices 62 | - the value looks like '<>...<>' -> a merge of array between the variable 'something' and 'something_else' is created as choices 63 | 64 | #### If you write your own Array 65 | 66 | - The 'detail' property is optionnal 67 | - The 'label' property is used as the content include in template 68 | - (For old users) The 'id' property is now automatically generated and not used anymore by the extension 69 | 70 | #### If you want predefined choices 71 | 72 | - 'keke' is the prefix I use personnally 73 | - 'angular' is prefix specific to angular repos (HELP: if someone has a full config for angular commits, can you share it with me by creating a PR or Issue ?) 74 | - 'alpha8' is prefix we used in the enterprise where I work 75 | - 'semantic' is an other normalization of prefix but I lost the link associated :/ 76 | 77 | #### If you want to list files many possibilities are yours 78 | 79 | - 'files': All staged and changed files 80 | - 'files.deleted': All deleted staged and changed files 81 | - 'files.modified': All modified staged and changed files 82 | - 'files.added': All added staged and changed files 83 | - 'files.staged': All staged files 84 | - 'files.staged.deleted': All deleted staged files 85 | - 'files.staged.modified': All modified staged files 86 | - 'files.staged.added': All added staged files 87 | - 'files.changed': All changed files 88 | - 'files.changed.deleted': All deleted changed files 89 | - 'files.changed.modified': All modified changed files 90 | - 'files.changed.added': All added changed files 91 | 92 | #### If you want to add branch name in commit, you can use this 93 | 94 | 'branch': Show you a select with the choice between short and long name for current branch 95 | 96 | ### **Define some default values for free input variables** (`vscodeGitCommit.defaultVariablesValues`) 97 | 98 | ```json 99 | { 100 | "vscodeGitCommit.defaultVariablesValues": { 101 | "author": "@RIOU Kevin" 102 | } 103 | } 104 | ``` 105 | 106 | For each variables defined in the template not used in `variables` setting, you can define a default value pretyped in the input 107 | 108 | In the upside example, for the variable `author`, the input is prefill with `@RIOU Kevin`. 109 | 110 | ### **Define titles display on the input** (`vscodeGitCommit.variablesDisplayTitles`) 111 | 112 | ```json 113 | { 114 | "vscodeGitCommit.variablesDisplayTitles": { 115 | "author": "Fill the pseudo of the person commiting with an @" 116 | } 117 | } 118 | ``` 119 | 120 | For each variables prompt, you can define a default value pretyped in the input 121 | 122 | In the upside example, for the variable `author`, the input will show the title `Fill the pseudo of the person commiting with an @`. 123 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/assets/demo.gif -------------------------------------------------------------------------------- /assets/demo_old.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/assets/demo_old.gif -------------------------------------------------------------------------------- /assets/icon_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/assets/icon_black.png -------------------------------------------------------------------------------- /assets/icon_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/assets/icon_light.png -------------------------------------------------------------------------------- /assets/icon_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/assets/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-git-commit", 3 | "displayName": "VSCode Git Commit Message", 4 | "description": "Harmonize your git commit message with your colleagues", 5 | "version": "3.2.0", 6 | "engines": { 7 | "vscode": "^1.60.0" 8 | }, 9 | "publisher": "rioukkevin", 10 | "license": "MIT", 11 | "homepage": "https://gcm-config.netlify.app", 12 | "bugs": { 13 | "url": "https://github.com/rioukkevin/vscode-git-commit/issues" 14 | }, 15 | "icon": "assets/logo.png", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/rioukkevin/vscode-git-commit" 19 | }, 20 | "keywords": [ 21 | "git", 22 | "commit", 23 | "vscode", 24 | "emoji", 25 | "prefix", 26 | "message", 27 | "template", 28 | "scm", 29 | "github", 30 | "gitlab", 31 | "harmonization", 32 | "customization", 33 | "format", 34 | "groot", 35 | "commitizen", 36 | "custom" 37 | ], 38 | "categories": [ 39 | "SCM Providers", 40 | "Other", 41 | "Formatters" 42 | ], 43 | "main": "./out/index.js", 44 | "activationEvents": [ 45 | "onCommand:vscodeGitCommit.setMessage", 46 | "onStartupFinished" 47 | ], 48 | "contributes": { 49 | "configuration": { 50 | "title": "VSCode Git Commit", 51 | "properties": { 52 | "vscodeGitCommit.insertMode": { 53 | "type": "string", 54 | "default": "Concatenate", 55 | "enum": [ 56 | "Replace", 57 | "Concatenate" 58 | ], 59 | "enumDescriptions": [ 60 | "Replace existing commit message on new", 61 | "Concatenate to existing commit message on new" 62 | ], 63 | "markdownDescription": "Select new commit message mode" 64 | }, 65 | "vscodeGitCommit.template": { 66 | "type": "array", 67 | "items": { 68 | "type": "string", 69 | "title": "Template" 70 | }, 71 | "default": [ 72 | "{prefix}: {message}" 73 | ], 74 | "markdownDescription": "Add abilities to create custom message format, each variable is between mustaches and a prompt is automatically added to the workflow" 75 | }, 76 | "vscodeGitCommit.variables": { 77 | "type": "object", 78 | "default": { 79 | "prefix": "keke" 80 | }, 81 | "markdownDescription": "Define variables that use QuickPick, **if a variable is not defined, a Text input will be displayed**", 82 | "additionalProperties": { 83 | "anyOf": [ 84 | { 85 | "type": "string", 86 | "minLength": 1 87 | }, 88 | { 89 | "items": { 90 | "additionalProperties": false, 91 | "properties": { 92 | "detail": { 93 | "type": "string" 94 | }, 95 | "label": { 96 | "type": "string" 97 | } 98 | }, 99 | "required": [ 100 | "label" 101 | ], 102 | "type": "object" 103 | }, 104 | "type": "array" 105 | } 106 | ] 107 | } 108 | }, 109 | "vscodeGitCommit.defaultVariablesValues": { 110 | "type": "object", 111 | "markdownDescription": "Define default values for free input text variables", 112 | "additionalProperties": { 113 | "anyOf": [ 114 | { 115 | "type": "string", 116 | "minLength": 1 117 | } 118 | ] 119 | } 120 | }, 121 | "vscodeGitCommit.variablesDisplayTitles": { 122 | "type": "object", 123 | "markdownDescription": "Define titles displayed optionnaly on variables inputs", 124 | "additionalProperties": { 125 | "anyOf": [ 126 | { 127 | "type": "string", 128 | "minLength": 1 129 | } 130 | ] 131 | } 132 | } 133 | } 134 | }, 135 | "commands": [ 136 | { 137 | "command": "vscodeGitCommit.setMessage", 138 | "title": "Git prefix emote", 139 | "icon": { 140 | "dark": "assets/icon_black.svg", 141 | "light": "assets/icon_light.svg" 142 | } 143 | } 144 | ], 145 | "menus": { 146 | "scm/title": [ 147 | { 148 | "command": "vscodeGitCommit.setMessage", 149 | "group": "navigation", 150 | "when": "scmProvider == git" 151 | } 152 | ] 153 | }, 154 | "keybindings": [ 155 | { 156 | "command": "vscodeGitCommit.setMessage", 157 | "key": "ctrl+alt+enter", 158 | "mac": "shift+cmd+enter" 159 | } 160 | ] 161 | }, 162 | "scripts": { 163 | "compile": "tsc -p ./", 164 | "watch": "tsc -watch -p ./", 165 | "md:concatenate": "node ./scripts/mdConcatenate.js", 166 | "md:copy": "node ./scripts/mdCopySandbox.js && rm -rf ./sandbox/public/assets || true && cp -R ./assets ./sandbox/public/assets", 167 | "md": "yarn md:concatenate && yarn md:copy", 168 | "semver": "node ./scripts/semver", 169 | "semver:patch": "yarn semver", 170 | "semver:minor": "yarn semver -t minor", 171 | "semver:major": "yarn semver -t major", 172 | "version": "node ./scripts/version", 173 | "extension:rm": "rm -f ./vscode-git-commit-3.1.0.vsix", 174 | "extension:build": "yarn md:concatenate && tsc -p ./", 175 | "extension:pack": "vsce package", 176 | "extension:install": "code --install-extension vscode-git-commit-3.1.1.vsix", 177 | "extension:local": "yarn extension:rm && yarn extension:build && yarn extension:pack && yarn extension:install", 178 | "extension:lint": "eslint src --ext ts --fix" 179 | }, 180 | "devDependencies": { 181 | "@types/cli-color": "^2.0.2", 182 | "@types/commander": "^2.12.2", 183 | "@types/node": "^12.12.0", 184 | "@types/semver": "^7.3.9", 185 | "@types/vscode": "^1.34.0", 186 | "@typescript-eslint/eslint-plugin": "^4.16.0", 187 | "@typescript-eslint/parser": "^4.16.0", 188 | "args-parser": "^1.3.0", 189 | "cli-color": "^2.0.3", 190 | "commander": "^9.4.1", 191 | "eslint": "^7.21.0", 192 | "package-json-editor": "^1.0.1", 193 | "typescript": "^4.2.2" 194 | }, 195 | "dependencies": { 196 | "semver": "^7.3.5" 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /sandbox/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /sandbox/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | .vscode -------------------------------------------------------------------------------- /sandbox/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /sandbox/components/CanvasTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Heading } from '@chakra-ui/layout'; 2 | import React, { FC } from 'react'; 3 | import styles from '../styles/CanvasTitle.module.css'; 4 | 5 | interface IProps { 6 | title: string; 7 | } 8 | 9 | const CanvasTitle: FC = (props) => { 10 | const { title } = props; 11 | return {title}; 12 | }; 13 | 14 | export default CanvasTitle; 15 | -------------------------------------------------------------------------------- /sandbox/components/CodeTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Heading } from '@chakra-ui/layout'; 2 | import React, { FC } from 'react'; 3 | import styles from '../styles/CodeTitle.module.css'; 4 | 5 | interface IProps { 6 | title: string; 7 | } 8 | 9 | const CodeTitle: FC = (props) => { 10 | const { title } = props; 11 | return {title}; 12 | }; 13 | 14 | export default CodeTitle; 15 | -------------------------------------------------------------------------------- /sandbox/components/DefaultVariablesValuesInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { LineElement } from '../typings/Editor'; 3 | import { IStoreDefaultVariableValue } from '../typings/Store'; 4 | import { Store } from '../utils/store'; 5 | import { parseVariableFromTemplate } from '../utils/template'; 6 | import DefaultVariableValueInput from './core/DefaultVariableValueInput'; 7 | 8 | interface IProps {} 9 | 10 | const DefaultVariablesValuesInput: FC = (props) => { 11 | const { 12 | defaultVariablesValues, 13 | setDefaultVariableValue, 14 | template, 15 | variables, 16 | } = useContext(Store); 17 | 18 | const templateVariables = 19 | parseVariableFromTemplate(template as LineElement[]) ?? []; 20 | 21 | const availableVariables = [...templateVariables]; 22 | 23 | const disabledVariables = Object.keys(variables); 24 | 25 | return ( 26 |
27 | {availableVariables.map((av, i) => ( 28 | 34 | setDefaultVariableValue(av, value) 35 | } 36 | onDelete={() => setDefaultVariableValue(av, undefined)} 37 | /> 38 | ))} 39 |
40 | ); 41 | }; 42 | 43 | export default DefaultVariablesValuesInput; 44 | -------------------------------------------------------------------------------- /sandbox/components/DefaultVariablesValuesRenderer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { Store } from '../utils/store'; 3 | import Pre from './core/Pre'; 4 | 5 | interface IProps {} 6 | 7 | const DefaultVariablesValuesRenderer: FC = (props) => { 8 | const { defaultVariablesValues } = useContext(Store); 9 | return ( 10 |
11 |       {`"vscodeGitCommit.defaultVariablesValues": ${JSON.stringify(
12 |         { ...defaultVariablesValues },
13 |         null,
14 |         2
15 |       )}`}
16 |     
17 | ); 18 | }; 19 | 20 | export default DefaultVariablesValuesRenderer; 21 | -------------------------------------------------------------------------------- /sandbox/components/InsertionModeInput.tsx: -------------------------------------------------------------------------------- 1 | import { Switch } from '@chakra-ui/react'; 2 | import React, { FC, useContext } from 'react'; 3 | import { Store } from '../utils/store'; 4 | 5 | interface IProps {} 6 | 7 | const InsertionModeInput: FC = (props) => { 8 | const { insertionMode, setInsertionMode } = useContext(Store); 9 | 10 | return ( 11 |
12 | Replace{' '} 13 | setInsertionMode(e.target.checked)} 16 | colorScheme="red" 17 | size="md" 18 | />{' '} 19 | Concatenate 20 |
21 | ); 22 | }; 23 | 24 | export default InsertionModeInput; 25 | -------------------------------------------------------------------------------- /sandbox/components/InsertionModeRenderer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { Store } from '../utils/store'; 3 | import Pre from './core/Pre'; 4 | 5 | interface IProps {} 6 | 7 | const InsertionModeRenderer: FC = (props) => { 8 | const { insertionMode } = useContext(Store); 9 | return ( 10 |
11 |       {insertionMode
12 |         ? '"vscodeGitCommit.insertMode": "Concatenate"'
13 |         : '"vscodeGitCommit.insertMode": "Replace"'}
14 |     
15 | ); 16 | }; 17 | 18 | export default InsertionModeRenderer; 19 | -------------------------------------------------------------------------------- /sandbox/components/TemplateInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | useMemo, 3 | FC, 4 | useCallback, 5 | useContext, 6 | useState, 7 | useEffect, 8 | } from 'react'; 9 | import { createEditor, Descendant } from 'slate'; 10 | import { Slate, Editable, withReact, RenderLeafProps } from 'slate-react'; 11 | import styles from '../styles/Input.module.css'; 12 | import { DEFAULT_VALUE, Store } from '../utils/store'; 13 | import { buildOnKeyDown, Leaf } from './TemplateInput.utils'; 14 | 15 | interface IProps {} 16 | 17 | const TemplateInput: FC = (props) => { 18 | const [value, setValue] = useState(DEFAULT_VALUE['template']); 19 | 20 | const { template, setTemplate } = useContext(Store); 21 | 22 | const editor = useMemo(() => withReact(createEditor()), []); 23 | 24 | const handleChange = (newValue: Descendant[]) => { 25 | setValue(newValue); 26 | setTemplate(newValue); 27 | }; 28 | 29 | const renderLeaf = useCallback((props: RenderLeafProps) => { 30 | return ; 31 | }, []); 32 | 33 | useEffect(() => { 34 | setValue(template); 35 | }, [template]); 36 | 37 | return ( 38 | 39 | 45 | 46 | ); 47 | }; 48 | 49 | export default TemplateInput; 50 | -------------------------------------------------------------------------------- /sandbox/components/TemplateInput.utils.tsx: -------------------------------------------------------------------------------- 1 | import { Badge } from '@chakra-ui/layout'; 2 | import { Editor, Node } from 'slate'; 3 | import { RenderLeafProps } from 'slate-react'; 4 | 5 | export const buildOnKeyDown = 6 | (editor: Editor): React.KeyboardEventHandler => 7 | (event) => { 8 | if (event.key === '{') { 9 | event.preventDefault(); 10 | editor.addMark('variable', true); 11 | } 12 | if (event.key === '}') { 13 | event.preventDefault(); 14 | editor.removeMark('variable'); 15 | editor.insertText(' '); 16 | } 17 | }; 18 | 19 | interface LeafProps extends RenderLeafProps {} 20 | 21 | export const Leaf = (props: LeafProps) => { 22 | return props.leaf.variable ? ( 23 | props.onClick!(props.children)} 25 | {...props.attributes} 26 | backgroundColor="#DB4437" 27 | textColor="white" 28 | style={{ padding: '0 5px', margin: '-2px 2px 0 2px' }} 29 | > 30 | {props.children} 31 | 32 | ) : ( 33 | {props.children} 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /sandbox/components/TemplateRenderer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { Descendant } from 'slate'; 3 | import { Store } from '../utils/store'; 4 | import { templateSerializer } from '../utils/template'; 5 | import Pre from './core/Pre'; 6 | 7 | interface IProps {} 8 | 9 | const TemplateRenderer: FC = (props) => { 10 | const { template } = useContext(Store); 11 | return ( 12 |
{`"vscodeGitCommit.template": ${templateSerializer(template)}`}
13 | ); 14 | }; 15 | 16 | export default TemplateRenderer; 17 | -------------------------------------------------------------------------------- /sandbox/components/VariableAdd.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@chakra-ui/button'; 2 | import { Input } from '@chakra-ui/input'; 3 | import React, { FC, useState } from 'react'; 4 | import styles from '../styles/VariableAdd.module.css'; 5 | 6 | interface IProps { 7 | onAdd: (name: string) => void; 8 | } 9 | 10 | const VariableAdd: FC = (props) => { 11 | const { onAdd } = props; 12 | 13 | const [value, setValue] = useState(''); 14 | const [isError, setIsError] = useState(false); 15 | 16 | const handleClick = () => { 17 | if (value.length > 0) { 18 | onAdd(value.toLowerCase()); 19 | setValue(''); 20 | } else { 21 | setIsError(true); 22 | setTimeout(() => { 23 | setIsError(false); 24 | }, 1500); 25 | } 26 | }; 27 | 28 | return ( 29 |
30 | setValue(e.target.value.toUpperCase())} 33 | variant="filled" 34 | size="sm" 35 | placeholder="name..." 36 | /> 37 | 49 |
50 | ); 51 | }; 52 | 53 | export default VariableAdd; 54 | -------------------------------------------------------------------------------- /sandbox/components/VariablesDisplayTitlesInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { LineElement } from '../typings/Editor'; 3 | import { IStoreDefaultVariableValue } from '../typings/Store'; 4 | import { Store } from '../utils/store'; 5 | import { parseVariableFromTemplate } from '../utils/template'; 6 | import VariableDisplayTitleInput from './core/VariableDisplayTitleInput'; 7 | 8 | interface IProps {} 9 | 10 | const VariablesDisplayTitlesInput: FC = (props) => { 11 | const { variablesDisplayTitles, setVariableDisplayTitle, template } = 12 | useContext(Store); 13 | 14 | const templateVariables = 15 | parseVariableFromTemplate(template as LineElement[]) ?? []; 16 | 17 | const availableVariables = [...templateVariables]; 18 | 19 | return ( 20 |
21 | {availableVariables.map((av, i) => ( 22 | 27 | setVariableDisplayTitle(av, value) 28 | } 29 | onDelete={() => setVariableDisplayTitle(av, undefined)} 30 | /> 31 | ))} 32 |
33 | ); 34 | }; 35 | 36 | export default VariablesDisplayTitlesInput; 37 | -------------------------------------------------------------------------------- /sandbox/components/VariablesDisplayTitlesRenderer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { Store } from '../utils/store'; 3 | import Pre from './core/Pre'; 4 | 5 | interface IProps {} 6 | 7 | const VariablesDisplayTitlesRenderer: FC = (props) => { 8 | const { variablesDisplayTitles } = useContext(Store); 9 | return ( 10 |
11 |       {`"vscodeGitCommit.variablesDisplayTitles": ${JSON.stringify(
12 |         { ...variablesDisplayTitles },
13 |         null,
14 |         2
15 |       )}`}
16 |     
17 | ); 18 | }; 19 | 20 | export default VariablesDisplayTitlesRenderer; 21 | -------------------------------------------------------------------------------- /sandbox/components/VariablesInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { LineElement } from '../typings/Editor'; 3 | import styles from '../styles/VariableInput.module.css'; 4 | import VariableInputElement from './VariablesInputElement'; 5 | import { Divider, Input } from '@chakra-ui/react'; 6 | import VariableAdd from './VariableAdd'; 7 | import VariableInputElementCustom from './VariablesInputElementCustom'; 8 | import { Store } from '../utils/store'; 9 | import { parseVariableFromTemplate } from '../utils/template'; 10 | import { PREDEFINED_PREFIX } from '../utils/variables'; 11 | 12 | interface IProps {} 13 | 14 | const VariableInput: FC = (props) => { 15 | const { variables, template, setVariable } = useContext(Store); 16 | 17 | const templateVariables = 18 | parseVariableFromTemplate(template as LineElement[]) ?? []; 19 | const customVariables = 20 | Object.keys(variables).filter( 21 | (v) => !templateVariables?.includes(v) && variables[v] !== undefined 22 | ) ?? []; 23 | 24 | const availableForMerge = [...templateVariables, ...customVariables].filter( 25 | (n) => !!variables[n] 26 | ); 27 | 28 | const handleAdd = (name: string) => { 29 | // Verif no double 30 | if ( 31 | templateVariables 32 | .map((a) => a.toLowerCase()) 33 | .includes(name.toLowerCase()) || 34 | customVariables 35 | .map((a) => a.toLowerCase()) 36 | .includes(name.toLowerCase()) || 37 | PREDEFINED_PREFIX.map((a) => a.toLowerCase()).includes(name.toLowerCase()) 38 | ) { 39 | return; 40 | } 41 | setVariable(name, []); 42 | }; 43 | 44 | return ( 45 |
46 | {templateVariables.map((v) => ( 47 | 52 | ))} 53 | 54 | {customVariables.map((v) => ( 55 | 60 | ))} 61 | {customVariables.length > 0 && } 62 | 63 |
64 | ); 65 | }; 66 | 67 | export default VariableInput; 68 | -------------------------------------------------------------------------------- /sandbox/components/VariablesInputElement.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext, useEffect, useState } from 'react'; 2 | import styles from '../styles/VariableInput.module.css'; 3 | import { Select } from '@chakra-ui/select'; 4 | import VariableArrayInput from './core/VariableArrayInput'; 5 | import VariableMergeInput from './core/VariableMergeInput'; 6 | import VariablePredefinedInput from './core/VariablePredefinedInput'; 7 | import { Store } from '../utils/store'; 8 | import VariableFileInput from './core/VariableFileInput'; 9 | import VariableBranchInput from './core/VariableBranchInput'; 10 | 11 | interface IProps { 12 | name: string; 13 | mergeItems: string[]; 14 | } 15 | 16 | const VariableInputElement: FC = (props) => { 17 | const { name, mergeItems } = props; 18 | 19 | const { variables, setVariable } = useContext(Store); 20 | const value = variables[name]; 21 | 22 | const [type, setType] = useState< 23 | 'string' | 'array' | 'merge' | 'predefined' | 'files' | 'branch' 24 | >('string'); 25 | 26 | const mergeItemsWithoutSelf = mergeItems.filter((a) => a !== name); 27 | 28 | // Update from bottom 29 | const handleChangeType = (e: any) => { 30 | const newVal = e.target.value as 31 | | 'string' 32 | | 'array' 33 | | 'merge' 34 | | 'predefined' 35 | | 'files' 36 | | 'branch'; 37 | setType(newVal); 38 | if (newVal === 'string' || newVal === 'predefined' || newVal === 'files') { 39 | setVariable(name, undefined); 40 | } else { 41 | setVariable(name, []); 42 | } 43 | }; 44 | 45 | useEffect(() => { 46 | if (typeof value === 'string') { 47 | if (value === 'branch') { 48 | setType('branch'); 49 | } else if (value.startsWith('files')) { 50 | setType('files'); 51 | } else { 52 | setType('predefined'); 53 | } 54 | } else if (typeof value === 'object' && value.length > 0) { 55 | if (typeof value[0] === 'string') { 56 | setType('merge'); 57 | } else { 58 | setType('array'); 59 | } 60 | } 61 | }, [value]); 62 | 63 | return ( 64 | <> 65 |

66 | {name} 67 |

68 |
69 |
70 | 83 |
84 |
85 | {type === 'array' && } 86 | {type === 'merge' && ( 87 | 91 | )} 92 | {type === 'predefined' && } 93 | {type === 'files' && } 94 | {type === 'branch' && } 95 |
96 |
97 | 98 | ); 99 | }; 100 | 101 | export default VariableInputElement; 102 | -------------------------------------------------------------------------------- /sandbox/components/VariablesInputElementCustom.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext, useState } from 'react'; 2 | import styles from '../styles/VariableInput.module.css'; 3 | import { Select } from '@chakra-ui/select'; 4 | import VariableArrayInput from './core/VariableArrayInput'; 5 | import { IconButton } from '@chakra-ui/react'; 6 | import { DeleteIcon } from '@chakra-ui/icons'; 7 | import VariableMergeInput from './core/VariableMergeInput'; 8 | import { Store } from '../utils/store'; 9 | import VariableBranchInput from './core/VariableBranchInput'; 10 | 11 | interface IProps { 12 | name: string; 13 | mergeItems: string[]; 14 | } 15 | 16 | const VariableInputElementCustom: FC = (props) => { 17 | const { name, mergeItems } = props; 18 | 19 | const { variables, setVariable } = useContext(Store); 20 | 21 | const [type, setType] = useState<'array' | 'merge' | 'branch'>('array'); 22 | 23 | const mergeItemsWithoutSelf = mergeItems.filter((a) => a !== name); 24 | 25 | // Update from bottom 26 | const handleChangeType = (e: any) => { 27 | const newVal = e.target.value as 'array' | 'merge' | 'branch'; 28 | setType(newVal); 29 | setVariable(name, []); 30 | }; 31 | 32 | const handleDelete = () => { 33 | setVariable(name, undefined); 34 | }; 35 | 36 | return ( 37 | <> 38 |

39 | {name} 40 |

41 |
42 |
43 | 53 | } 57 | color="#000000" 58 | backgroundColor="#EEF2F6" 59 | _hover={{ backgroundColor: '#db4437', color: '#FBFFFF' }} 60 | size="sm" 61 | onClick={handleDelete} 62 | style={{ marginLeft: '5px' }} 63 | /> 64 |
65 |
66 | {type === 'array' && } 67 | {type === 'branch' && } 68 | {type === 'merge' && ( 69 | 73 | )} 74 |
75 |
76 | 77 | ); 78 | }; 79 | 80 | export default VariableInputElementCustom; 81 | -------------------------------------------------------------------------------- /sandbox/components/VariablesRenderer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { Store } from '../utils/store'; 3 | import { variableSerializer } from '../utils/variables'; 4 | import Pre from './core/Pre'; 5 | 6 | interface IProps {} 7 | 8 | const VariablesRenderer: FC = (props) => { 9 | const { variables } = useContext(Store); 10 | return ( 11 |
{`"vscodeGitCommit.variables": ${variableSerializer(variables)}`}
12 | ); 13 | }; 14 | 15 | export default VariablesRenderer; 16 | -------------------------------------------------------------------------------- /sandbox/components/core/DefaultVariableValueInput.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton, Input } from '@chakra-ui/react'; 2 | import React, { FC, useEffect, useState } from 'react'; 3 | import styles from '../../styles/DefaultVariablesValuesInput.module.css'; 4 | import { DeleteIcon } from '@chakra-ui/icons'; 5 | import { IStoreDefaultVariableValue } from '../../typings/Store'; 6 | 7 | interface IProps { 8 | name: string; 9 | value: IStoreDefaultVariableValue; 10 | isDisabled?: boolean; 11 | onChange: (value: IStoreDefaultVariableValue) => IStoreDefaultVariableValue; 12 | onDelete: () => void; 13 | } 14 | 15 | const DefaultVariableValueInput: FC = (props) => { 16 | const { name, value, onChange, onDelete, isDisabled } = props; 17 | 18 | const [internalValue, setInternalValue] = 19 | useState(value); 20 | 21 | const handleChangeValue = (e: any) => { 22 | const newValue = e.target.value.length === 0 ? undefined : e.target.value; 23 | onChange(newValue); 24 | setInternalValue(newValue); 25 | }; 26 | 27 | useEffect(() => { 28 | setInternalValue(value); 29 | }, [value]); 30 | 31 | return ( 32 | <> 33 |

34 | {name}{' '} 35 | {isDisabled && ( 36 | 37 | Not handle because a variable config is defined for 38 | 39 | )} 40 |

41 |
42 |
43 | 51 |
52 | } 57 | color="#000000" 58 | backgroundColor="#EEF2F6" 59 | _hover={{ backgroundColor: '#db4437', color: '#FBFFFF' }} 60 | size="sm" 61 | onClick={onDelete} 62 | /> 63 |
64 | 65 | ); 66 | }; 67 | 68 | export default DefaultVariableValueInput; 69 | -------------------------------------------------------------------------------- /sandbox/components/core/Menu.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from '@chakra-ui/react'; 2 | import { FC } from 'react'; 3 | 4 | export const Menu: FC = () => { 5 | return ( 6 |
7 | 8 | Documentation 9 | 10 | 11 | Configurator 12 | 13 |
14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /sandbox/components/core/Pre.tsx: -------------------------------------------------------------------------------- 1 | import { CheckIcon, CopyIcon, IconProps } from '@chakra-ui/icons'; 2 | import { ComponentWithAs, IconButton } from '@chakra-ui/react'; 3 | import React, { FC, useState } from 'react'; 4 | import styles from '../../styles/Pre.module.css'; 5 | 6 | interface IProps { 7 | children: string; 8 | } 9 | 10 | const Pre: FC = (props) => { 11 | const [Icon, setIcon] = useState>(CopyIcon); 12 | const [color, setColor] = useState('#FDFFFF'); 13 | 14 | const handleClick = () => { 15 | navigator.clipboard.writeText(props.children); 16 | setColor('#AFDDC9'); 17 | setIcon(CheckIcon); 18 | setTimeout(() => { 19 | setColor('#FDFFFF'); 20 | setIcon(CopyIcon); 21 | }, 1000); 22 | }; 23 | 24 | return ( 25 |
26 |
{props.children}
27 | } 31 | color="#292A2B" 32 | backgroundColor={color} 33 | size="sm" 34 | onClick={handleClick} 35 | /> 36 |
37 | ); 38 | }; 39 | 40 | export default Pre; 41 | -------------------------------------------------------------------------------- /sandbox/components/core/SelectString.tsx: -------------------------------------------------------------------------------- 1 | import { DeleteIcon } from '@chakra-ui/icons'; 2 | import { IconButton, Select } from '@chakra-ui/react'; 3 | import React, { FC, useEffect, useState } from 'react'; 4 | import styles from '../../styles/SelectString.module.css'; 5 | 6 | interface IProps { 7 | value: string | undefined; 8 | mergeItems: string[]; 9 | onChange: (mergedItem: string | undefined) => void; 10 | } 11 | 12 | const SelectString: FC = (props) => { 13 | const { mergeItems, onChange, value } = props; 14 | 15 | const [selected, setSelected] = useState(''); 16 | 17 | const handleChange = (e: any) => { 18 | setSelected(e.target.value); 19 | onChange(e.target.value === '' ? undefined : e.target.value); 20 | }; 21 | 22 | const handleDelete = () => { 23 | onChange(undefined); 24 | }; 25 | 26 | useEffect(() => { 27 | setSelected(value ?? ''); 28 | }, [value]); 29 | 30 | return ( 31 |
32 | 46 | } 51 | color="#000000" 52 | backgroundColor="#EEF2F6" 53 | _hover={{ backgroundColor: '#db4437', color: '#FBFFFF' }} 54 | size="sm" 55 | onClick={handleDelete} 56 | /> 57 |
58 | ); 59 | }; 60 | 61 | export default SelectString; 62 | -------------------------------------------------------------------------------- /sandbox/components/core/VariableArrayInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext, useState } from 'react'; 2 | import VariableInput from './VariableInput'; 3 | import styles from '../../styles/StringArrayInput.module.css'; 4 | import { Button } from '@chakra-ui/button'; 5 | import { Store } from '../../utils/store'; 6 | import { IStoreVariableCustom } from '../../typings/Store'; 7 | 8 | interface IProps { 9 | name: string; 10 | } 11 | 12 | type TVariable = IStoreVariableCustom; 13 | 14 | const VariableArrayInput: FC = (props) => { 15 | const { name } = props; 16 | 17 | const { variables, setVariable } = useContext(Store); 18 | const value = variables[name] as TVariable; 19 | 20 | const handleInputChange = ( 21 | newLabel: string, 22 | newDetail: string, 23 | index: number 24 | ) => { 25 | const temp = value; 26 | temp[index] = { 27 | label: newLabel, 28 | detail: newDetail.length > 0 ? newDetail : undefined, 29 | }; 30 | setVariable(name, temp); 31 | }; 32 | 33 | const handleAdd = () => { 34 | const temp = [ 35 | ...value, 36 | { 37 | label: '', 38 | }, 39 | ]; 40 | setVariable(name, temp); 41 | }; 42 | 43 | const handleDelete = (index: number) => { 44 | const temp = value.filter((_, i) => i !== index); 45 | setVariable(name, temp); 46 | }; 47 | 48 | return ( 49 | <> 50 | {value.map((v, i) => { 51 | return ( 52 | handleInputChange(label, detail, i)} 55 | key={i} 56 | onDelete={() => handleDelete(i)} 57 | /> 58 | ); 59 | })} 60 | 63 | 64 | ); 65 | }; 66 | 67 | export default VariableArrayInput; 68 | -------------------------------------------------------------------------------- /sandbox/components/core/VariableBranchInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext, useEffect } from 'react'; 2 | import { Store } from '../../utils/store'; 3 | 4 | interface IProps { 5 | name: string; 6 | } 7 | 8 | const KEY_VALUE = 'branch'; 9 | 10 | const VariableBranchInput: FC = (props) => { 11 | const { name } = props; 12 | 13 | const { variables, setVariable } = useContext(Store); 14 | 15 | useEffect(() => { 16 | setVariable(name, KEY_VALUE); 17 | 18 | return () => { 19 | if (variables[name] === KEY_VALUE) { 20 | setVariable(name, undefined); 21 | } 22 | }; 23 | }, []); 24 | 25 | return <>; 26 | }; 27 | 28 | export default VariableBranchInput; 29 | -------------------------------------------------------------------------------- /sandbox/components/core/VariableDisplayTitleInput.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton, Input } from '@chakra-ui/react'; 2 | import React, { FC, useEffect, useState } from 'react'; 3 | import styles from '../../styles/DefaultVariablesValuesInput.module.css'; 4 | import { DeleteIcon } from '@chakra-ui/icons'; 5 | import { IStoreVariableDisplayTitle } from '../../typings/Store'; 6 | 7 | interface IProps { 8 | name: string; 9 | value: IStoreVariableDisplayTitle; 10 | onChange: (value: IStoreVariableDisplayTitle) => IStoreVariableDisplayTitle; 11 | onDelete: () => void; 12 | } 13 | 14 | const VariableDisplayTitleInput: FC = (props) => { 15 | const { name, value, onChange, onDelete } = props; 16 | 17 | const [internalValue, setInternalValue] = 18 | useState(value); 19 | 20 | const handleChangeValue = (e: any) => { 21 | const newValue = e.target.value.length === 0 ? undefined : e.target.value; 22 | onChange(newValue); 23 | setInternalValue(newValue); 24 | }; 25 | 26 | useEffect(() => { 27 | setInternalValue(value); 28 | }, [value]); 29 | 30 | return ( 31 | <> 32 |

33 | {name} 34 |

35 |
36 |
37 | 45 |
46 | } 51 | color="#000000" 52 | backgroundColor="#EEF2F6" 53 | _hover={{ backgroundColor: '#db4437', color: '#FBFFFF' }} 54 | size="sm" 55 | onClick={onDelete} 56 | /> 57 |
58 | 59 | ); 60 | }; 61 | 62 | export default VariableDisplayTitleInput; 63 | -------------------------------------------------------------------------------- /sandbox/components/core/VariableFileInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext, useEffect, useState } from 'react'; 2 | import { Store } from '../../utils/store'; 3 | import SelectString from './SelectString'; 4 | 5 | interface IProps { 6 | name: string; 7 | } 8 | 9 | type TVariable = string | undefined; 10 | 11 | const FILE_STATUS = ['staged', 'changed']; 12 | const FILE_ACTIONS = ['deleted', 'updated', 'added']; 13 | 14 | const VariableFileInput: FC = (props) => { 15 | const { name } = props; 16 | 17 | const [filesStatus, setFilesStatus] = useState(undefined); 18 | const [filesActions, setFilesActions] = useState( 19 | undefined 20 | ); 21 | 22 | const { variables, setVariable } = useContext(Store); 23 | const value = variables[name] as TVariable; 24 | 25 | useEffect(() => { 26 | if (typeof value === 'string') { 27 | const parts = value?.split('.'); 28 | if (parts[2]) { 29 | setFilesStatus(parts[1]); 30 | setFilesActions(parts[2]); 31 | } else { 32 | if (FILE_STATUS.includes(parts[1])) { 33 | setFilesStatus(parts[1]); 34 | } else if (FILE_ACTIONS.includes(parts[1])) { 35 | setFilesActions(parts[1]); 36 | } 37 | } 38 | } 39 | }, [value]); 40 | 41 | useEffect(() => { 42 | handleChange(undefined, undefined); 43 | }, []); 44 | 45 | // update from bottom 46 | const handleChange = (status: TVariable, actions: TVariable) => { 47 | const base = 'files'; 48 | 49 | console.log('CHANGE', status, actions); 50 | 51 | const stringified = `${base}${status ? `.${status}` : ''}${ 52 | actions ? `.${actions}` : '' 53 | }`; 54 | setVariable(name, stringified); 55 | }; 56 | 57 | const handleChangeStatus = (v: TVariable) => { 58 | setFilesStatus(v); 59 | handleChange(v, filesActions); 60 | }; 61 | 62 | const handleChangeActions = (v: TVariable) => { 63 | setFilesActions(v); 64 | handleChange(filesStatus, v); 65 | }; 66 | 67 | return ( 68 | <> 69 | 74 | 79 | 80 | ); 81 | }; 82 | 83 | export default VariableFileInput; 84 | -------------------------------------------------------------------------------- /sandbox/components/core/VariableInput.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton, Input } from '@chakra-ui/react'; 2 | import React, { FC, useEffect, useState } from 'react'; 3 | import styles from '../../styles/StringInput.module.css'; 4 | import { DeleteIcon } from '@chakra-ui/icons'; 5 | import { IStoreVariable, IStoreVariableCustom } from '../../typings/Store'; 6 | 7 | interface IProps { 8 | value: IStoreVariable; 9 | onChange: (label: string, detail: string) => void; 10 | onDelete: () => void; 11 | } 12 | 13 | const VariableInput: FC = (props) => { 14 | const { value, onChange, onDelete } = props; 15 | 16 | const [label, setLabel] = useState(value.label); 17 | const [detail, setDetail] = useState(value.detail ?? ''); 18 | 19 | const handleChangeLabel = (e: any) => { 20 | onChange(e.target.value, detail); 21 | setLabel(e.target.value); 22 | }; 23 | 24 | const handleChangeDetail = (e: any) => { 25 | onChange(label, e.target.value); 26 | setDetail(e.target.value); 27 | }; 28 | 29 | useEffect(() => { 30 | setLabel(value.label); 31 | setDetail(value.detail ?? ''); 32 | }, [value]); 33 | 34 | return ( 35 |
36 |
37 | 45 | 52 |
53 | } 57 | color="#000000" 58 | backgroundColor="#EEF2F6" 59 | _hover={{ backgroundColor: '#db4437', color: '#FBFFFF' }} 60 | size="sm" 61 | onClick={onDelete} 62 | /> 63 |
64 | ); 65 | }; 66 | 67 | export default VariableInput; 68 | -------------------------------------------------------------------------------- /sandbox/components/core/VariableMergeInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext, useEffect, useState } from 'react'; 2 | import { Store } from '../../utils/store'; 3 | import SelectString from './SelectString'; 4 | 5 | interface IProps { 6 | mergeItems: string[]; 7 | name: string; 8 | } 9 | 10 | type TVariable = (string | undefined)[]; 11 | 12 | const VariableMergeInput: FC = (props) => { 13 | const { mergeItems, name } = props; 14 | 15 | const { variables, setVariable } = useContext(Store); 16 | const value = (variables[name] ?? []) as TVariable; 17 | 18 | // update from bottom 19 | const handleChange = (index: number, newValue: string | undefined) => { 20 | const newVal = [...value]; 21 | newVal[index] = newValue; 22 | 23 | setVariable(name, newVal.filter((v) => !!v) as string[]); 24 | }; 25 | 26 | useEffect(() => { 27 | value.map((v, i) => { 28 | if (!mergeItems.includes(v!)) { 29 | handleChange(i, undefined); 30 | } 31 | }); 32 | // eslint-disable-next-line react-hooks/exhaustive-deps 33 | }, [mergeItems]); 34 | 35 | return ( 36 | <> 37 | {value.map((v, i) => ( 38 | handleChange(i, value)} 42 | key={i} 43 | /> 44 | ))} 45 | handleChange(value?.length ?? 1, value)} 49 | key={value.length + 1} 50 | /> 51 | 52 | ); 53 | }; 54 | 55 | export default VariableMergeInput; 56 | -------------------------------------------------------------------------------- /sandbox/components/core/VariablePredefinedInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react'; 2 | import { Store } from '../../utils/store'; 3 | import { PREDEFINED_PREFIX } from '../../utils/variables'; 4 | import SelectString from './SelectString'; 5 | 6 | interface IProps { 7 | name: string; 8 | } 9 | 10 | type TVariable = string | undefined; 11 | 12 | const VariablePredefinedInput: FC = (props) => { 13 | const { name } = props; 14 | 15 | const { variables, setVariable } = useContext(Store); 16 | const value = variables[name] as TVariable; 17 | 18 | // update from bottom 19 | const handleChange = (newValue: TVariable) => { 20 | setVariable(name, newValue); 21 | }; 22 | 23 | return ( 24 | <> 25 | 30 | 31 | ); 32 | }; 33 | 34 | export default VariablePredefinedInput; 35 | -------------------------------------------------------------------------------- /sandbox/components/md/code.tsx: -------------------------------------------------------------------------------- 1 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; 2 | import { vs } from './vs'; 3 | 4 | export const code = ({ node, inline, className, children, ...props }: any) => { 5 | const match = /language-(\w+)/.exec(className || ''); 6 | return !inline && match ? ( 7 | 16 | ) : ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /sandbox/components/md/em.tsx: -------------------------------------------------------------------------------- 1 | import { Kbd } from '@chakra-ui/react'; 2 | 3 | export const em = Kbd; 4 | -------------------------------------------------------------------------------- /sandbox/components/md/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code'; 2 | export * from './em'; 3 | -------------------------------------------------------------------------------- /sandbox/components/md/vs.ts: -------------------------------------------------------------------------------- 1 | export const vs = { 2 | 'code[class*="language-"]': { 3 | color: 'white', 4 | background: 'none', 5 | fontFamily: "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace", 6 | fontSize: '1em', 7 | textAlign: 'left', 8 | textShadow: '0 -.1em .2em black', 9 | whiteSpace: 'pre', 10 | wordSpacing: 'normal', 11 | wordBreak: 'normal', 12 | wordWrap: 'normal', 13 | lineHeight: '1.5', 14 | MozTabSize: '4', 15 | OTabSize: '4', 16 | tabSize: '4', 17 | WebkitHyphens: 'none', 18 | MozHyphens: 'none', 19 | msHyphens: 'none', 20 | hyphens: 'none', 21 | }, 22 | 'pre[class*="language-"]': { 23 | color: 'white', 24 | background: 'hsl(0, 0%, 8%)', 25 | fontFamily: "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace", 26 | fontSize: '1em', 27 | textAlign: 'left', 28 | textShadow: '0 -.1em .2em black', 29 | whiteSpace: 'pre', 30 | wordSpacing: 'normal', 31 | wordBreak: 'normal', 32 | wordWrap: 'normal', 33 | lineHeight: '1.5', 34 | MozTabSize: '4', 35 | OTabSize: '4', 36 | tabSize: '4', 37 | WebkitHyphens: 'none', 38 | MozHyphens: 'none', 39 | msHyphens: 'none', 40 | hyphens: 'none', 41 | margin: '.5em 0', 42 | overflow: 'auto', 43 | padding: '1em', 44 | borderRadius: 10, 45 | }, 46 | ':not(pre) > code[class*="language-"]': { 47 | background: 'hsl(0, 0%, 8%)', 48 | borderRadius: '.3em', 49 | boxShadow: '1px 1px .3em -.1em black inset', 50 | padding: '.15em .2em .05em', 51 | whiteSpace: 'normal', 52 | }, 53 | 'pre[class*="language-"]::-moz-selection': { 54 | background: 'hsla(0, 0%, 93%, 0.15)', 55 | textShadow: 'none', 56 | }, 57 | 'pre[class*="language-"]::selection': { 58 | background: 'hsla(0, 0%, 93%, 0.15)', 59 | textShadow: 'none', 60 | }, 61 | 'pre[class*="language-"] ::-moz-selection': { 62 | textShadow: 'none', 63 | background: 'hsla(0, 0%, 93%, 0.15)', 64 | }, 65 | 'code[class*="language-"]::-moz-selection': { 66 | textShadow: 'none', 67 | background: 'hsla(0, 0%, 93%, 0.15)', 68 | }, 69 | 'code[class*="language-"] ::-moz-selection': { 70 | textShadow: 'none', 71 | background: 'hsla(0, 0%, 93%, 0.15)', 72 | }, 73 | 'pre[class*="language-"] ::selection': { 74 | textShadow: 'none', 75 | background: 'hsla(0, 0%, 93%, 0.15)', 76 | }, 77 | 'code[class*="language-"]::selection': { 78 | textShadow: 'none', 79 | background: 'hsla(0, 0%, 93%, 0.15)', 80 | }, 81 | 'code[class*="language-"] ::selection': { 82 | textShadow: 'none', 83 | background: 'hsla(0, 0%, 93%, 0.15)', 84 | }, 85 | comment: { 86 | color: 'hsl(0, 0%, 47%)', 87 | }, 88 | prolog: { 89 | color: 'hsl(0, 0%, 47%)', 90 | }, 91 | doctype: { 92 | color: 'hsl(0, 0%, 47%)', 93 | }, 94 | cdata: { 95 | color: 'hsl(0, 0%, 47%)', 96 | }, 97 | punctuation: { 98 | Opacity: '.7', 99 | }, 100 | namespace: { 101 | Opacity: '.7', 102 | }, 103 | tag: { 104 | color: 'hsl(14, 58%, 55%)', 105 | }, 106 | boolean: { 107 | color: 'hsl(14, 58%, 55%)', 108 | }, 109 | number: { 110 | color: 'hsl(14, 58%, 55%)', 111 | }, 112 | deleted: { 113 | color: 'hsl(14, 58%, 55%)', 114 | }, 115 | keyword: { 116 | color: 'hsl(53, 89%, 79%)', 117 | }, 118 | property: { 119 | color: 'hsl(53, 89%, 79%)', 120 | }, 121 | selector: { 122 | color: 'hsl(53, 89%, 79%)', 123 | }, 124 | constant: { 125 | color: 'hsl(53, 89%, 79%)', 126 | }, 127 | symbol: { 128 | color: 'hsl(53, 89%, 79%)', 129 | }, 130 | builtin: { 131 | color: 'hsl(53, 89%, 79%)', 132 | }, 133 | 'attr-name': { 134 | color: 'hsl(76, 21%, 52%)', 135 | }, 136 | 'attr-value': { 137 | color: 'hsl(76, 21%, 52%)', 138 | }, 139 | string: { 140 | color: 'hsl(76, 21%, 52%)', 141 | }, 142 | char: { 143 | color: 'hsl(76, 21%, 52%)', 144 | }, 145 | operator: { 146 | color: 'hsl(76, 21%, 52%)', 147 | }, 148 | entity: { 149 | color: 'hsl(76, 21%, 52%)', 150 | cursor: 'help', 151 | }, 152 | url: { 153 | color: 'hsl(76, 21%, 52%)', 154 | }, 155 | '.language-css .token.string': { 156 | color: 'hsl(76, 21%, 52%)', 157 | }, 158 | '.style .token.string': { 159 | color: 'hsl(76, 21%, 52%)', 160 | }, 161 | variable: { 162 | color: 'hsl(76, 21%, 52%)', 163 | }, 164 | inserted: { 165 | color: 'hsl(76, 21%, 52%)', 166 | }, 167 | atrule: { 168 | color: 'hsl(218, 22%, 55%)', 169 | }, 170 | regex: { 171 | color: 'hsl(42, 75%, 65%)', 172 | }, 173 | important: { 174 | color: 'hsl(42, 75%, 65%)', 175 | fontWeight: 'bold', 176 | }, 177 | bold: { 178 | fontWeight: 'bold', 179 | }, 180 | italic: { 181 | fontStyle: 'italic', 182 | }, 183 | '.language-markup .token.tag': { 184 | color: 'hsl(33, 33%, 52%)', 185 | }, 186 | '.language-markup .token.attr-name': { 187 | color: 'hsl(33, 33%, 52%)', 188 | }, 189 | '.language-markup .token.punctuation': { 190 | color: 'hsl(33, 33%, 52%)', 191 | }, 192 | '': { 193 | position: 'relative', 194 | zIndex: '1', 195 | }, 196 | '.line-highlight.line-highlight': { 197 | background: 198 | 'linear-gradient(to right, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0))', 199 | borderBottom: '1px dashed hsl(0, 0%, 33%)', 200 | borderTop: '1px dashed hsl(0, 0%, 33%)', 201 | marginTop: '0.75em', 202 | zIndex: '0', 203 | }, 204 | '.line-highlight.line-highlight:before': { 205 | backgroundColor: 'hsl(215, 15%, 59%)', 206 | color: 'hsl(24, 20%, 95%)', 207 | }, 208 | '.line-highlight.line-highlight[data-end]:after': { 209 | backgroundColor: 'hsl(215, 15%, 59%)', 210 | color: 'hsl(24, 20%, 95%)', 211 | }, 212 | }; 213 | -------------------------------------------------------------------------------- /sandbox/generated/doc.md.ts: -------------------------------------------------------------------------------- 1 | 2 | export const doc = `![demo](./assets/demo.gif) 3 | 4 | ![version](https://vsmarketplacebadge.apphb.com/version-short/rioukkevin.vscode-git-commit.svg?style=for-the-badge&color=dd4739) 5 | ![install](https://vsmarketplacebadge.apphb.com/installs/rioukkevin.vscode-git-commit.svg?style=for-the-badge&color=dd4739) 6 | ![rating](https://vsmarketplacebadge.apphb.com/rating-star/rioukkevin.vscode-git-commit.svg?style=for-the-badge&color=dd4739) 7 | 8 | # What is it ? 9 | 10 | This extension is an extension made to formalize git commit messages. By using a template and variables, you can define how the commit message needs to looks and which data you want in this message. 11 | 12 | By example: When you want to specify a **scope** in your commits, you can create a variable named scope in th e template like this \`{scope}\` 13 | 14 | If you want to specify which data you want in this scope variable, you can specify it with the variable settings 15 | 16 | \`\`\`json 17 | { 18 | "scope": [ 19 | { 20 | "label": "🟢 App" 21 | }, 22 | { 23 | "label": "🟠 Api" 24 | }, 25 | { 26 | "label": "🔵 Scripts" 27 | } 28 | ] 29 | } 30 | \`\`\` 31 | 32 | I show you more on the #Settings part 33 | 34 | toto 35 | 36 | ## How to use it ? 37 | 38 | Use _ctrl_+_alt_+_enter_ (_cmd_+_shift_+_enter_ on Mac) or \`click\` on the icon in SouceControl 39 | ![bl](./assets/icon_black.png) 40 | ![li](./assets/icon_light.png) 41 | 42 | # Settings 43 | 44 | ### UI 45 | 46 | An UI is available [HERE](https://gcm-config.netlify.app/configurator), it's not the best UI but it can help you make your first configuration for the extension 47 | 48 | **Hosted on Netlify** 49 | 50 | ### **Erase previous commit on new one** (\`vscodeGitCommit.insertMode\`) 51 | 52 | \`\`\`json 53 | { 54 | "vscodeGitCommit.insertMode": "Concatenate" 55 | } 56 | \`\`\` 57 | 58 | 'Concatenate' give the ability to use multiple message in the same commit where 'Replace' not 59 | 60 | ### **Create my own template message** (\`vscodeGitCommit.template\`) 61 | 62 | \`\`\`json 63 | { 64 | "vscodeGitCommit.template": [ 65 | "{feat}({scope}): {message}" 66 | "by {author}" 67 | ] 68 | } 69 | \`\`\` 70 | 71 | By defining one string, you create a line, with two strings like in the example, you define two line for the template. 72 | 73 | You can define dynamic content by using _{_ & _}_ in doc, I called it a variable. 74 | 75 | When triggering this extension, a value for each variable is prompt. 76 | 77 | ### **Use select or text for each variables** (\`vscodeGitCommit.variables\`) 78 | 79 | \`\`\`json 80 | { 81 | "vscodeGitCommit.variables": { 82 | "author": [ 83 | { 84 | "label": "Devs", 85 | "detail": "Use when a change is made by Developers" 86 | }, 87 | { 88 | "label": "Ops" 89 | } 90 | ], 91 | "feat": "keke" 92 | } 93 | } 94 | \`\`\` 95 | 96 | For each variables defined in the template above, you can define the content: 97 | 98 | - If not set -> Free string input 99 | - If it's an Array -> A select of choices is displayed 100 | - If it's a string three possibilities 101 | - the value is oneOf 'keke' | 'angular' | 'semantic' | 'alpha8' -> A predefined Array is associated to the variable 102 | - the value startWith 'files' -> An array of files with defined status is displayed as choices 103 | - the value looks like '<>...<>' -> a merge of array between the variable 'something' and 'something_else' is created as choices 104 | 105 | #### If you write your own Array 106 | 107 | - The 'detail' property is optionnal 108 | - The 'label' property is used as the content include in template 109 | - (For old users) The 'id' property is now automatically generated and not used anymore by the extension 110 | 111 | #### If you want predefined choices 112 | 113 | - 'keke' is the prefix I use personnally 114 | - 'angular' is prefix specific to angular repos (HELP: if someone has a full config for angular commits, can you share it with me by creating a PR or Issue ?) 115 | - 'alpha8' is prefix we used in the enterprise where I work 116 | - 'semantic' is an other normalization of prefix but I lost the link associated :/ 117 | 118 | #### If you want to list files many possibilities are yours 119 | 120 | - 'files': All staged and changed files 121 | - 'files.deleted': All deleted staged and changed files 122 | - 'files.modified': All modified staged and changed files 123 | - 'files.added': All added staged and changed files 124 | - 'files.staged': All staged files 125 | - 'files.staged.deleted': All deleted staged files 126 | - 'files.staged.modified': All modified staged files 127 | - 'files.staged.added': All added staged files 128 | - 'files.changed': All changed files 129 | - 'files.changed.deleted': All deleted changed files 130 | - 'files.changed.modified': All modified changed files 131 | - 'files.changed.added': All added changed files 132 | 133 | #### If you want to add branch name in commit, you can use this 134 | 135 | 'branch': Show you a select with the choice between short and long name for current branch 136 | 137 | ### **Define some default values for free input variables** (\`vscodeGitCommit.defaultVariablesValues\`) 138 | 139 | \`\`\`json 140 | { 141 | "vscodeGitCommit.defaultVariablesValues": { 142 | "author": "@RIOU Kevin" 143 | } 144 | } 145 | \`\`\` 146 | 147 | For each variables defined in the template not used in \`variables\` setting, you can define a default value pretyped in the input 148 | 149 | In the upside example, for the variable \`author\`, the input is prefill with \`@RIOU Kevin\`. 150 | 151 | ### **Define titles display on the input** (\`vscodeGitCommit.variablesDisplayTitles\`) 152 | 153 | \`\`\`json 154 | { 155 | "vscodeGitCommit.variablesDisplayTitles": { 156 | "author": "Fill the pseudo of the person commiting with an @" 157 | } 158 | } 159 | \`\`\` 160 | 161 | For each variables prompt, you can define a default value pretyped in the input 162 | 163 | In the upside example, for the variable \`author\`, the input will show the title \`Fill the pseudo of the person commiting with an @\`. 164 | 165 | # Contributing to the extension 166 | 167 | #### Share configs 168 | 169 | You can share the config you made for you by creating a pull request, in the folder 'share' you can find a Readme as a template and an example made by [jycouet](https://github.com/jycouet) 170 | 171 | #### Contributing to code base 172 | 173 | You just have to create a pull-request 😉 with what you want and a clear description of the goal of your feature 174 | 175 | # Changelog 176 | 177 | #### **3.1.1** (_09-30-2022_) 178 | 179 | - ✨feature: Trim messages when finished 180 | - ✨feature: Ability to define titles on prompts 181 | - ✨feature: Ability to define default values on prompts 182 | 183 | #### **3.1.0** (_08-05-2022_) 184 | 185 | - 🐞fix: update for vscode api change in 1.70 about repositories 186 | 187 | #### **3.0.2** (_07-16-2022_) 188 | 189 | - ✨feature: ability to define default value for free text inputs 190 | - ✨feature: add repo current branch in variables 191 | 192 | #### **3.0.1** (_02-24-2022_) 193 | 194 | - 🐞fix: settings migration script fix from V3.0.0 195 | 196 | > third release with an UI O_o and a lot of new features (btw: we reached the 5k installs THX U ♥️) 197 | 198 | #### **3.0.0** (_02-23-2022_) 199 | 200 | - ✨feature: use of information or error message as feedback 201 | - ✨feature: update contributes.configuration $schema 202 | - ✨feature: add an UI to generate settings (cf: this [UI](https://gcm-config.netlify.app/configurator)) 203 | - ✨feature: add PR template 204 | - ✨feature: add a folder for settings sharing between users 205 | - ✨feature: add ability to search for files using predefined variable names 206 | - 📄docs (docs): update docs globaly and make a web version 207 | 208 | #### **2.1.0** (_05-06-2021_) 209 | 210 | - ✨feature: auto focus scm commit input on finish (cf: issues [#10](https://github.com/rioukkevin/vscode-git-commit/issues/10)) 211 | - ✨feature: add abilities to concatenate multiple variables in settings (cf: issues [#11](https://github.com/rioukkevin/vscode-git-commit/issues/11) & [#12](https://github.com/rioukkevin/vscode-git-commit/issues/12)) 212 | 213 | > second release OH YEAHHH !!!! 214 | 215 | #### **2.0.0** (_04-09-2021_) 216 | 217 | - ✨feature: add ability to use text input or select input for any variables 218 | 219 | #### **1.1.5** (_12-18-2020_) 220 | 221 | - 📄docs (docs): update docs with animated gif 222 | 223 | #### **1.1.4** (_11-06-2020_) 224 | 225 | - ✨feature: Handle multi repo case 226 | 227 | #### **1.1.3** (_11-06-2020_) 228 | 229 | - 🐞fix: Update icon (cf: issues [#4](https://github.com/rioukkevin/vscode-git-commit/issues/4)) 230 | - 🐞fix: Handle cancel action when typing variables (cf: issues [#5](https://github.com/rioukkevin/vscode-git-commit/issues/5)) 231 | ⚙️refactor: Refacto on extension command name 232 | 233 | #### **1.1.1** (_10-20-2020_) 234 | 235 | - 🐞fix: Update icon (cf: issues [#4](https://github.com/rioukkevin/vscode-git-commit/issues/4)) 236 | 237 | #### **1.1.0** (_10-12-2020_) 238 | 239 | - ✨feat(workflow): Add abilities to create custom message format using simple brackets in settings, see doc 240 | - ⚙️refactor(global): Refacto on major part of the code 241 | - 🌈style(typescript/prettier): Pass into prettier + update ts rules 242 | 243 | #### **1.0.1** (_09-30-2020_) 244 | 245 | - ✨feature: On demand, add based commitizen prefix for alpha8 246 | - 🔵other: Change default mode to concatenate settings 247 | 248 | > First Release, I've done it !! 249 | 250 | #### **1.0.0** (_09-21-2020_) 251 | 252 | - ✨feature: Replace icon with outlined 253 | 254 | #### **0.0.4** (_09-15-2020_) 255 | 256 | - ✨feature: add a mode to concatenate message with existing or replace existing 257 | - 🐞fix: focus on quickPick not on scm input box when triggering extension 258 | 259 | #### **0.0.3** (_08-25-2020_) 260 | 261 | - ✨feature: V0.0.3 add prefix sets 262 | 263 | #### **0.0.2** (_08-23-2020_) 264 | 265 | - ✨feature: Add custom prefix setting 266 | - 🐞fix: Open SCM view when prefix selector is opened, not at the end of process 267 | 268 | #### **0.0.1** (_08-18-2020_) 269 | 270 | - Initial release 271 | ` 272 | -------------------------------------------------------------------------------- /sandbox/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /sandbox/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | } 5 | -------------------------------------------------------------------------------- /sandbox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sandbox", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "@chakra-ui/icons": "^1.0.17", 12 | "@chakra-ui/react": "^1.8.5", 13 | "@emotion/react": "^11.8.1", 14 | "@emotion/styled": "^11.8.1", 15 | "chakra-ui-markdown-renderer": "^4.0.0", 16 | "framer-motion": "^6.2.7", 17 | "next": "12.0.2", 18 | "react": "17.0.2", 19 | "react-dom": "17.0.2", 20 | "react-markdown": "^7.0.0", 21 | "react-syntax-highlighter": "^15.4.5", 22 | "slate": "^0.66.5", 23 | "slate-history": "^0.66.0", 24 | "slate-react": "^0.69.0" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "16.11.6", 28 | "@types/react": "17.0.34", 29 | "@types/react-syntax-highlighter": "^13.5.2", 30 | "eslint": "7", 31 | "eslint-config-next": "12.0.2", 32 | "typescript": "4.4.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sandbox/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | import type { AppProps } from 'next/app'; 3 | import { ChakraProvider } from '@chakra-ui/react'; 4 | import '../typings/Editor.d'; 5 | import { StoreProvider } from '../utils/store'; 6 | 7 | function MyApp({ Component, pageProps }: AppProps) { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default MyApp; 18 | -------------------------------------------------------------------------------- /sandbox/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /sandbox/pages/configurator.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @next/next/no-img-element */ 2 | 3 | import { Heading, Link } from '@chakra-ui/react'; 4 | import type { NextPage } from 'next'; 5 | import Head from 'next/head'; 6 | import React from 'react'; 7 | import CanvasTitle from '../components/CanvasTitle'; 8 | import CodeTitle from '../components/CodeTitle'; 9 | import { Menu } from '../components/core/Menu'; 10 | import DefaultVariablesValuesInput from '../components/DefaultVariablesValuesInput'; 11 | import DefaultVariablesValuesRenderer from '../components/DefaultVariablesValuesRenderer'; 12 | import InsertionModeInput from '../components/InsertionModeInput'; 13 | import InsertionModeRenderer from '../components/InsertionModeRenderer'; 14 | import TemplateInput from '../components/TemplateInput'; 15 | import TemplateRenderer from '../components/TemplateRenderer'; 16 | import VariablesDisplayTitlesInput from '../components/VariablesDisplayTitlesInput'; 17 | import VariablesDisplayTitlesRenderer from '../components/VariablesDisplayTitlesRenderer'; 18 | import VariableInput from '../components/VariablesInput'; 19 | import VariablesRenderer from '../components/VariablesRenderer'; 20 | import styles from '../styles/Configurator.module.css'; 21 | 22 | const TITLE = 'Configurator - VSCode Git commit message'; 23 | const DESCRIPTION = 24 | 'A settings generator for the extension Git commit message of VSCode'; 25 | 26 | const Configurator: NextPage = () => { 27 | return ( 28 |
29 | 30 | 31 | {TITLE} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 |
59 | 60 | logo Git 61 | commit message configurator 62 | 63 | 64 |

65 | This website as goal to generate settings for{' '} 66 | 67 | VSCode Git Commit Message 68 | {' '} 69 | extension, this website is a simple GUI for it. 70 |

71 |
72 | version 76 | install 80 | rating 84 |
85 |
86 |
87 |
88 |
89 |
90 | 91 |

DEFAULT: Concatenate

92 | 93 |
94 |
95 | 96 | 97 |
98 |
99 |
100 |
101 | 102 |

103 | DEFAULT: {'{feat}({scope}):{message}'} 104 |

105 | 106 |
107 |
108 | 109 | 110 |
111 |
112 |
113 |
114 | 115 |

DEFAULT: {'{}'}

116 | 117 |
118 |
119 | 120 | 121 |
122 |
123 |
124 |
125 | 126 |

DEFAULT: {'{}'}

127 | 128 |
129 |
130 | 131 | 132 |
133 |
134 |
135 |
136 | 137 |

DEFAULT: {'{}'}

138 | 139 |
140 |
141 | 142 | 143 |
144 |
145 |
146 |
147 | ); 148 | }; 149 | 150 | export default Configurator; 151 | -------------------------------------------------------------------------------- /sandbox/pages/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @next/next/no-img-element */ 2 | import { Heading, Link } from '@chakra-ui/react'; 3 | import type { NextPage } from 'next'; 4 | import Head from 'next/head'; 5 | import React from 'react'; 6 | import ChakraUIRenderer from 'chakra-ui-markdown-renderer'; 7 | import styles from '../styles/Home.module.css'; 8 | 9 | import ReactMarkdown from 'react-markdown'; 10 | 11 | import { doc } from '../generated/doc.md'; 12 | import * as customs from '../components/md'; 13 | import { Menu } from '../components/core/Menu'; 14 | 15 | const TITLE = 'Home - VSCode Git commit message'; 16 | const DESCRIPTION = 17 | 'A settings generator for the extension Git commit message of VSCode'; 18 | 19 | const Home: NextPage = () => { 20 | return ( 21 |
22 | 23 | 24 | {TITLE} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | logo Git commit 49 | message 50 | 51 | 52 | 58 | {doc} 59 | 60 |
61 |
62 | ); 63 | }; 64 | 65 | export default Home; 66 | -------------------------------------------------------------------------------- /sandbox/public/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/assets/demo.gif -------------------------------------------------------------------------------- /sandbox/public/assets/demo_old.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/assets/demo_old.gif -------------------------------------------------------------------------------- /sandbox/public/assets/icon_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/assets/icon_black.png -------------------------------------------------------------------------------- /sandbox/public/assets/icon_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sandbox/public/assets/icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/assets/icon_light.png -------------------------------------------------------------------------------- /sandbox/public/assets/icon_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sandbox/public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/assets/logo.png -------------------------------------------------------------------------------- /sandbox/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/favicon.ico -------------------------------------------------------------------------------- /sandbox/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rioukkevin/vscode-git-commit/f31a611c043a61436b88a28a27e395cd4d946bb1/sandbox/public/logo.png -------------------------------------------------------------------------------- /sandbox/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /sandbox/styles/CanvasTitle.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | position: relative; 3 | margin-bottom: 20px; 4 | width: 100%; 5 | } 6 | 7 | .title::before { 8 | content: ''; 9 | display: block; 10 | width: 5px; 11 | height: 100%; 12 | background-color: #db4437; 13 | position: absolute; 14 | top: 0; 15 | right: -40px; 16 | } 17 | -------------------------------------------------------------------------------- /sandbox/styles/CodeTitle.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | position: relative; 3 | margin-bottom: 20px; 4 | } 5 | 6 | .title::before { 7 | content: ''; 8 | display: block; 9 | width: 5px; 10 | height: 100%; 11 | background-color: #db4437; 12 | position: absolute; 13 | top: 0; 14 | left: -40px; 15 | } 16 | -------------------------------------------------------------------------------- /sandbox/styles/Configurator.module.css: -------------------------------------------------------------------------------- 1 | /* Black: #292A2B */ 2 | /* White: #FDFFFF */ 3 | /* Red: #DB4437 */ 4 | 5 | .container { 6 | padding: 0; 7 | } 8 | 9 | .main { 10 | min-height: 100vh; 11 | height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | flex-wrap: nowrap; 15 | } 16 | 17 | .logo { 18 | margin-top: -7px; 19 | height: 50px; 20 | display: inline; 21 | } 22 | 23 | .presentation { 24 | margin: 40px 0px 20px 0px; 25 | } 26 | 27 | .presentation > a { 28 | color: #db4437; 29 | font-weight: bold; 30 | } 31 | 32 | .presentation > a:hover { 33 | text-decoration-line: underline; 34 | text-decoration-thickness: 2px; 35 | } 36 | 37 | .stats { 38 | position: relative; 39 | display: flex; 40 | margin-bottom: -40px; 41 | } 42 | 43 | .stats > img { 44 | margin-right: 5px; 45 | } 46 | 47 | .default { 48 | color: #808080; 49 | font-size: 0.9rem; 50 | margin-bottom: 10px; 51 | } 52 | 53 | .line { 54 | display: flex; 55 | flex-direction: row; 56 | flex-wrap: nowrap; 57 | } 58 | 59 | .left, 60 | .right { 61 | flex-grow: 1; 62 | display: flex; 63 | flex-direction: column; 64 | padding: 40px; 65 | } 66 | 67 | .left { 68 | background-color: #fdffff; 69 | max-width: 60%; 70 | min-width: 600px; 71 | align-items: flex-start; 72 | } 73 | 74 | .right { 75 | background-color: #292a2b; 76 | max-width: 40%; 77 | min-width: 400px; 78 | align-items: flex-start; 79 | color: #fdffff; 80 | } 81 | 82 | .footer { 83 | display: flex; 84 | flex: 1; 85 | padding: 2rem 0; 86 | border-top: 1px solid #eaeaea; 87 | justify-content: center; 88 | align-items: center; 89 | } 90 | 91 | .footer a { 92 | display: flex; 93 | justify-content: center; 94 | align-items: center; 95 | flex-grow: 1; 96 | } 97 | -------------------------------------------------------------------------------- /sandbox/styles/DefaultVariablesValuesInput.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | border-radius: 3px; 3 | margin: 10px 0px; 4 | display: flex; 5 | justify-content: stretch; 6 | align-items: flex-start; 7 | width: 100%; 8 | } 9 | 10 | .container:first-child { 11 | margin-top: 0; 12 | } 13 | 14 | .input { 15 | width: 100%; 16 | } 17 | 18 | .name { 19 | font-weight: bold; 20 | margin-bottom: 5px; 21 | text-transform: uppercase; 22 | } 23 | 24 | .disabled { 25 | font-weight: 400; 26 | text-transform: none; 27 | font-size: small; 28 | color: #db4437; 29 | } 30 | 31 | .deleteIcon { 32 | margin-left: 5px; 33 | } 34 | -------------------------------------------------------------------------------- /sandbox/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | /* Black: #292A2B */ 2 | /* White: #FDFFFF */ 3 | /* Red: #DB4437 */ 4 | 5 | .container { 6 | padding: 0; 7 | } 8 | 9 | .main { 10 | min-height: 100vh; 11 | height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | flex-wrap: nowrap; 15 | padding: 30px; 16 | width: 60vw; 17 | overflow-x: hidden; 18 | box-shadow: 40vw 0 0 1px #292a2b; 19 | } 20 | 21 | .logo { 22 | margin-top: -7px; 23 | height: 50px; 24 | display: inline; 25 | } 26 | 27 | /* Paragraph */ 28 | .main p { 29 | display: inline; 30 | } 31 | 32 | /* Images */ 33 | .main img { 34 | max-width: calc(60vw - 60px); 35 | border-radius: 10px; 36 | margin-top: 10px; 37 | } 38 | -------------------------------------------------------------------------------- /sandbox/styles/Input.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: fit-content; 3 | } 4 | 5 | .editor { 6 | min-width: 400px; 7 | width: 100%; 8 | background-color: #edf2f7; 9 | padding: 10px; 10 | border-radius: 4px; 11 | } 12 | -------------------------------------------------------------------------------- /sandbox/styles/Pre.module.css: -------------------------------------------------------------------------------- 1 | .preContainer { 2 | width: 100%; 3 | position: relative; 4 | } 5 | 6 | .preContainer > pre { 7 | overflow-x: auto; 8 | } 9 | 10 | .preContainer > pre::selection { 11 | background-color: #db44377f; 12 | } 13 | 14 | .copyIcon { 15 | position: absolute !important; 16 | right: 0; 17 | top: -40px; 18 | } 19 | -------------------------------------------------------------------------------- /sandbox/styles/SelectString.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | width: 100%; 4 | flex-wrap: nowrap; 5 | } 6 | 7 | .select { 8 | margin-bottom: 5px; 9 | } 10 | 11 | .deleteIcon { 12 | margin-left: 5px; 13 | } 14 | -------------------------------------------------------------------------------- /sandbox/styles/StringArrayInput.module.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sandbox/styles/StringInput.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | border-radius: 3px; 3 | margin: 10px 0px; 4 | display: flex; 5 | justify-content: stretch; 6 | align-items: flex-start; 7 | width: 100%; 8 | } 9 | 10 | .container:first-child { 11 | margin-top: 0; 12 | } 13 | 14 | .input { 15 | width: 100%; 16 | } 17 | 18 | .label { 19 | margin-bottom: 5px; 20 | } 21 | 22 | .deleteIcon { 23 | margin-left: 5px; 24 | } 25 | -------------------------------------------------------------------------------- /sandbox/styles/VariableAdd.module.css: -------------------------------------------------------------------------------- 1 | .input { 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: nowrap; 5 | width: 100%; 6 | } 7 | 8 | .button { 9 | width: 300px !important; 10 | margin-left: 5px; 11 | } 12 | -------------------------------------------------------------------------------- /sandbox/styles/VariableInput.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: flex-start; 5 | width: 100%; 6 | } 7 | 8 | .name { 9 | font-weight: bold; 10 | margin-bottom: 5px; 11 | text-transform: uppercase; 12 | } 13 | 14 | .content { 15 | display: flex; 16 | flex-direction: row; 17 | width: 100%; 18 | } 19 | 20 | .contentLeft { 21 | flex-shrink: 1; 22 | min-width: 150px; 23 | padding-bottom: 20px; 24 | } 25 | 26 | .contentLeftRow { 27 | display: flex; 28 | flex-wrap: nowrap; 29 | } 30 | 31 | .contentRight { 32 | display: flex; 33 | flex-direction: column; 34 | align-items: flex-start; 35 | padding-left: 20px; 36 | width: 100%; 37 | } 38 | 39 | .divider { 40 | margin: 20px 0; 41 | } 42 | -------------------------------------------------------------------------------- /sandbox/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /sandbox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /sandbox/typings/Editor.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEditor } from 'slate'; 2 | import { ReactEditor } from 'slate-react'; 3 | import { HistoryEditor } from 'slate-history'; 4 | 5 | type CustomText = { text: string; variable?: boolean }; 6 | 7 | export type LineElement = { 8 | type: 'line'; 9 | children: CustomText[]; 10 | }; 11 | 12 | export type CustomElement = LineElement; 13 | 14 | declare module 'slate' { 15 | interface CustomTypes { 16 | Editor: BaseEditor & ReactEditor & HistoryEditor; 17 | Element: CustomElement; 18 | Text: CustomText; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sandbox/typings/Renderer.d.ts: -------------------------------------------------------------------------------- 1 | export interface IRendererVariable { 2 | label: string; 3 | detail?: string; 4 | key: string; 5 | } 6 | 7 | export type IRendererTemplate = string; 8 | 9 | export interface IRenderer { 10 | template: IRendererTemplate; 11 | variables: IRendererVariable[]; 12 | } 13 | -------------------------------------------------------------------------------- /sandbox/typings/Store.d.ts: -------------------------------------------------------------------------------- 1 | import { Descendant } from 'slate'; 2 | 3 | export interface IStoreVariable { 4 | label: string; 5 | detail?: string; 6 | } 7 | 8 | export type IStoreVariableCustom = IStoreVariable[]; 9 | export type IStoreVariableMerge = string[]; 10 | export type IStoreVariablePredefined = string; 11 | export type IStoreDefaultVariableValue = string | undefined; 12 | export type IStoreVariableDisplayTitle = string | undefined; 13 | 14 | type IStoreVariableValue = 15 | | IStoreVariableCustom 16 | | IStoreVariableMerge 17 | | IStoreVariablePredefined 18 | | undefined; 19 | 20 | export interface IStoreVariables { 21 | [key: string]: IStoreVariableValue; 22 | } 23 | 24 | export interface IStoreDefaultVariablesValues { 25 | [key: string]: IStoreDefaultVariableValue; 26 | } 27 | 28 | export interface IStoreVariablesDisplayTitles { 29 | [key: string]: IStoreVariableDisplayTitle; 30 | } 31 | 32 | export type IStoreTemplate = Descendant[]; 33 | 34 | export interface IStoreData { 35 | template: IStoreTemplate; 36 | variables: IStoreVariables; 37 | defaultVariablesValues: IStoreDefaultVariablesValues; 38 | variablesDisplayTitles: IStoreVariablesDisplayTitles; 39 | insertionMode: boolean; 40 | } 41 | 42 | export interface IStore extends IStoreData { 43 | setVariable: ( 44 | name: string, 45 | value: IStoreVariableValue 46 | ) => IStoreVariableValue; 47 | setTemplate: (value: IStoreTemplate) => IStoreTemplate; 48 | setDefaultVariableValue: ( 49 | name: string, 50 | value: IStoreDefaultVariableValue 51 | ) => IStoreDefaultVariableValue; 52 | setVariableDisplayTitle: ( 53 | name: string, 54 | value: IStoreVariableDisplayTitle 55 | ) => IStoreVariableDisplayTitle; 56 | setInsertionMode: (value: boolean) => boolean; 57 | } 58 | -------------------------------------------------------------------------------- /sandbox/utils/store.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, FC, useState } from 'react'; 2 | import { CustomElement } from '../typings/Editor'; 3 | import { IStore, IStoreTemplate } from '../typings/Store'; 4 | import { parseVariableFromTemplate } from './template'; 5 | 6 | const DEFAULT_TEMPLATE: IStoreTemplate = [ 7 | { 8 | type: 'line', 9 | children: [ 10 | { text: 'feat', variable: true }, 11 | { text: '(' }, 12 | { text: 'scope', variable: true }, 13 | { text: '): ' }, 14 | { text: 'message', variable: true }, 15 | { text: ' ' }, 16 | ], 17 | }, 18 | ]; 19 | 20 | export const DEFAULT_VALUE: IStore = { 21 | template: DEFAULT_TEMPLATE, 22 | variables: {}, 23 | defaultVariablesValues: {}, 24 | variablesDisplayTitles: {}, 25 | insertionMode: false, 26 | setVariable: () => [], 27 | setTemplate: () => [], 28 | setDefaultVariableValue: () => '', 29 | setVariableDisplayTitle: () => '', 30 | setInsertionMode: () => false, 31 | }; 32 | 33 | export const Store = createContext(DEFAULT_VALUE); 34 | 35 | // Provider 36 | export const StoreProvider: FC = (props) => { 37 | const [template, setTemplateState] = 38 | useState(DEFAULT_TEMPLATE); 39 | const [variables, setVariablesState] = useState({}); 40 | const [insertionMode, setInsertionModeState] = useState(true); 41 | const [defaultVariablesValues, setDefaultVariablesValuesState] = useState< 42 | IStore['defaultVariablesValues'] 43 | >({}); 44 | const [variablesDisplayTitles, setVariablesDisplayTitlesState] = useState< 45 | IStore['variablesDisplayTitles'] 46 | >({}); 47 | 48 | const setTemplate: IStore['setTemplate'] = (value) => { 49 | updateVariablesNames(value); 50 | setTemplateState(value); 51 | return value; 52 | }; 53 | const setVariable: IStore['setVariable'] = (name, value) => { 54 | setVariablesState((oldValue) => { 55 | return { ...oldValue, [name]: value }; 56 | }); 57 | return value; 58 | }; 59 | 60 | const setInsertionMode: IStore['setInsertionMode'] = (value) => { 61 | setInsertionModeState(value); 62 | return value; 63 | }; 64 | 65 | const setDefaultVariableValue: IStore['setDefaultVariableValue'] = ( 66 | name, 67 | value 68 | ) => { 69 | setDefaultVariablesValuesState((oldValue) => ({ 70 | ...oldValue, 71 | [name]: value, 72 | })); 73 | return value; 74 | }; 75 | 76 | const setVariableDisplayTitle: IStore['setVariableDisplayTitle'] = ( 77 | name, 78 | value 79 | ) => { 80 | setVariablesDisplayTitlesState((oldValue) => ({ 81 | ...oldValue, 82 | [name]: value, 83 | })); 84 | return value; 85 | }; 86 | 87 | // Utils 88 | const updateVariableName = (oldName?: string, newName?: string) => { 89 | if (oldName && newName) { 90 | setVariablesState({ 91 | ...variables, 92 | [oldName]: undefined, 93 | [newName]: variables[oldName], 94 | }); 95 | } else if (oldName && !newName) { 96 | setVariablesState({ 97 | ...variables, 98 | [oldName]: undefined, 99 | }); 100 | } 101 | }; 102 | 103 | const updateVariablesNames = (newTemplate: IStoreTemplate) => { 104 | const oldVariablesNames = 105 | parseVariableFromTemplate(template as CustomElement[]) ?? []; 106 | const newVariablesNames = 107 | parseVariableFromTemplate(newTemplate as CustomElement[]) ?? []; 108 | 109 | const oldChangedName = oldVariablesNames.filter( 110 | (n) => !newVariablesNames.includes(n) 111 | )[0]; 112 | const newChangedName = newVariablesNames.filter( 113 | (n) => !oldVariablesNames.includes(n) 114 | )[0]; 115 | 116 | updateVariableName(oldChangedName, newChangedName); 117 | }; 118 | 119 | return ( 120 | 134 | {props.children} 135 | 136 | ); 137 | }; 138 | 139 | // Consumer 140 | interface IStoreConsumerProps { 141 | children: any; 142 | } 143 | 144 | export const StoreConsumer: FC = (props) => { 145 | const { children: Children } = props; 146 | return {(value) => }; 147 | }; 148 | -------------------------------------------------------------------------------- /sandbox/utils/template.ts: -------------------------------------------------------------------------------- 1 | import { Descendant } from 'slate'; 2 | import { CustomElement } from '../typings/Editor'; 3 | 4 | export const templateSerializer = (descendants: Descendant[]) => { 5 | const totalLines = descendants.length; 6 | const lines = descendants.map((line: any, i) => { 7 | const l = line.children 8 | .map((t: any) => (t.variable ? `{${t.text.toLowerCase()}}` : t.text)) 9 | .reduce((a: any, b: any) => `${a}${b}`); 10 | 11 | return i < totalLines ? `\n "${l}"` : ''; 12 | }); 13 | 14 | return lines.length > 1 15 | ? `[${lines.reduce((a: any, b: any) => `${a}${b}`)}\n]` 16 | : `[${lines[0]}\n]`; 17 | }; 18 | export const parseVariableFromTemplate = (template: CustomElement[]) => { 19 | if (template.length > 1) { 20 | return template 21 | .map((line) => { 22 | return line.children 23 | .filter((t: any) => t.variable) 24 | .map((el) => el.text.toLowerCase()); 25 | }) 26 | .reduce((a: string[], b: string[]) => [...a, ...b]); 27 | } else { 28 | return template 29 | .find((l) => !!l.children) 30 | ?.children.filter((t) => !!t.variable) 31 | .map((el) => el.text.toLowerCase()); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /sandbox/utils/variables.ts: -------------------------------------------------------------------------------- 1 | import { IStoreVariables } from '../typings/Store'; 2 | 3 | export const PREDEFINED_PREFIX: string[] = [ 4 | 'keke', 5 | 'semantic', 6 | 'angular', 7 | 'alpha8', 8 | ]; 9 | 10 | export const variableSerializer = (variables: IStoreVariables) => { 11 | const toDisplay: { [key: string]: any } = {}; 12 | Object.keys(variables).map((key) => { 13 | if ( 14 | variables && 15 | variables[key] !== undefined && 16 | variables[key]![0] && 17 | Array.isArray(variables[key]) && 18 | typeof variables[key]![0] === 'string' 19 | ) { 20 | const tot = variables[key] as string[]; 21 | toDisplay[key] = tot.join('...'); 22 | } 23 | }); 24 | return JSON.stringify({ ...variables, ...toDisplay }, null, 2); 25 | }; 26 | -------------------------------------------------------------------------------- /scripts/mdConcatenate.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const CONCATENATE_FILE = [ 4 | './BASE.md', 5 | './SETTINGS.md', 6 | './CONTRIBUTING.md', 7 | './CHANGELOG.md', 8 | ]; 9 | 10 | const OUTPUT_FILE = './README.md'; 11 | 12 | const run = () => { 13 | const contentToConcatenate = CONCATENATE_FILE.map((f) => { 14 | return fs.readFileSync(f); 15 | }).reduce((a, b) => a + '\n' + b); 16 | 17 | fs.writeFileSync(OUTPUT_FILE, contentToConcatenate); 18 | }; 19 | 20 | run(); 21 | -------------------------------------------------------------------------------- /scripts/mdCopySandbox.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const INPUT_FILE = './README.md'; 4 | const OUTPUT_FILE = './sandbox/generated/doc.md.ts'; 5 | 6 | const run = () => { 7 | const contentToCopy = fs.readFileSync(INPUT_FILE); 8 | 9 | const template = ` 10 | export const doc = \`${contentToCopy.toString().replaceAll('`', '\\`')}\` 11 | `; 12 | 13 | fs.writeFileSync(OUTPUT_FILE, template); 14 | }; 15 | 16 | run(); 17 | -------------------------------------------------------------------------------- /scripts/semver.js: -------------------------------------------------------------------------------- 1 | const inc = require('semver/functions/inc'); 2 | const commander = require('commander'); 3 | const fs = require('fs'); 4 | var clc = require('cli-color'); 5 | 6 | const FILE_NAME = './package.test.json'; 7 | 8 | const program = new commander.Command(); 9 | program.addOption( 10 | new commander.Option('-t, --type ', 'increment type') 11 | .choices(['major', 'minor', 'patch']) 12 | .default('patch', '"patch"') 13 | ); 14 | 15 | const run = () => { 16 | program.parse(); 17 | const options = program.opts(); 18 | const oldPackage = JSON.parse(fs.readFileSync(FILE_NAME, 'utf-8')); 19 | 20 | const newVersionNumber = inc(oldPackage.version, options.type); 21 | const changedValue = { 22 | version: newVersionNumber, 23 | scripts: { 24 | ...oldPackage.scripts, 25 | 'extension:install': `code --install-extension vscode-git-commit-${newVersionNumber}.vsix`, 26 | 'extension:rm': `rm -f ./vscode-git-commit-${oldPackage.version}.vsix`, 27 | }, 28 | }; 29 | const newPackage = JSON.stringify( 30 | { ...oldPackage, ...changedValue }, 31 | null, 32 | ' ' 33 | ); 34 | fs.writeFileSync(FILE_NAME, newPackage); 35 | 36 | console.log( 37 | `Moved ${clc.bold.greenBright(FILE_NAME)} version from ${clc.red( 38 | oldPackage.version 39 | )} to ${clc.green(changedValue.version)}` 40 | ); 41 | }; 42 | 43 | run(); 44 | -------------------------------------------------------------------------------- /scripts/version.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | var clc = require('cli-color'); 3 | 4 | const FILE_NAME = './package.json'; 5 | 6 | const run = () => { 7 | const package = JSON.parse(fs.readFileSync(FILE_NAME, 'utf-8')); 8 | 9 | console.log(`Version: ${clc.green(package.version)}`); 10 | }; 11 | 12 | run(); 13 | -------------------------------------------------------------------------------- /share/README.md: -------------------------------------------------------------------------------- 1 | # Sharing space 2 | 3 | ## Table of content 4 | 5 | - [Default template](./default.md) 6 | - [CaioMizerkowski](./caiomizerkowski.md) 7 | - [Wilson Mar template by jycouet](./wilsonmar.md) 8 | - [Diogo Rossi](./diogorossi.md) 9 | 10 | ## Template for examples 11 | 12 | ````md 13 | # Title (Author) 14 | 15 | Added by: [Name](https://github.com/someone) 16 | Source: [Source](https://url.com) 17 | 18 | Settings: 19 | 20 | ```json 21 | "vscodeGitCommit.template": [""], 22 | "vscodeGitCommit.variables": {} 23 | ```` 24 | -------------------------------------------------------------------------------- /share/caiomizerkowski.md: -------------------------------------------------------------------------------- 1 | # Personal git message heavy inspired on Wilson Mar example (Caio Mizerkowski) 2 | 3 | Added by: [CaioMizerkowski](https://github.com/CaioMizerkowski) 4 | 5 | Settings: 6 | 7 | ```json 8 | "vscodeGitCommit.template": ["{icon}: {message}\nmade by Caio Mizerkowski\nfor {type}"], 9 | "vscodeGitCommit.variables": { 10 | 11 | "prefix": "keke", 12 | "icon": "aliasesIcons", 13 | "aliasesIcons": [ 14 | { 15 | "label": "🎉", 16 | "detail": "🎉 = Initial (NEW) commit." 17 | }, 18 | { 19 | "label": "🚧", 20 | "detail": "🚧 = Work in progress." 21 | }, 22 | { 23 | "label": "🚑", 24 | "detail": "🚑 = Critical hotfix." 25 | }, 26 | { 27 | "label": "✨", 28 | "detail": "✨ = Introducing new features." 29 | }, 30 | { 31 | "label": "🍻", 32 | "detail": "🍻 = Writing code drunkenly." 33 | }, 34 | { 35 | "label": "💩", 36 | "detail": "💩 = Writing bad code that needs to be improved." 37 | }, 38 | { 39 | "label": "🚨", 40 | "detail": "🚨 = Removing linter warnings." 41 | }, 42 | { 43 | "label": "⏪", 44 | "detail": "⏪ = Reverting changes." 45 | }, 46 | { 47 | "label": "🔒", 48 | "detail": "🔒 = Fixing security issues." 49 | }, 50 | { 51 | "label": "⚡️", 52 | "detail": "⚡️ = Improving performance." 53 | }, 54 | { 55 | "label": "🚸", 56 | "detail": "🚸 = Improving user experience / usability." 57 | }, 58 | { 59 | "label": "🐞", 60 | "detail": "🐞 = Fixing a bug." 61 | }, 62 | { 63 | "label": "💡", 64 | "detail": "💡 = Documenting source code" 65 | }, 66 | { 67 | "label": "✅", 68 | "detail": "✅ = Updating tests." 69 | }, 70 | { 71 | "label": "👌", 72 | "detail": "👌 = Updating code due to code review changes." 73 | }, 74 | { 75 | "label": "🤡", 76 | "detail": "🤡 = Mocking things." 77 | }, 78 | { 79 | "label": "💥", 80 | "detail": "💥 = Introducing breaking changes." 81 | }, 82 | { 83 | "label": "⬆️", 84 | "detail": "⬆️ = Upgrading dependencies." 85 | }, 86 | { 87 | "label": "🔥", 88 | "detail": "🔥 = Removing code or files." 89 | }, 90 | { 91 | "label": "✏️", 92 | "detail": "✏️ = Fixing typos." 93 | }, 94 | { 95 | "label": "🔊", 96 | "detail": "🔊 = Adding logs." 97 | }, 98 | { 99 | "label": "🔇", 100 | "detail": "🔇 = Removing logs." 101 | }, 102 | { 103 | "label": "🎨", 104 | "detail": "🎨 = Improving structure / format of the code." 105 | }, 106 | { 107 | "label": "♻️", 108 | "detail": "♻️ = Refactoring code logic." 109 | }, 110 | { 111 | "label": "👷", 112 | "detail": "👷 = Adding CI build system." 113 | }, 114 | { 115 | "label": "🚀", 116 | "detail": "🚀 = Deploying stuff." 117 | }, 118 | { 119 | "label": "🏷️", 120 | "detail": "🏷️ = Adding or updating types (Flow, TypeScript)" 121 | }, 122 | { 123 | "label": "🗃", 124 | "detail": "🗃 = Performing database related changes." 125 | }, 126 | { 127 | "label": "💄", 128 | "detail": "💄 = Updating the UI and style files." 129 | }, 130 | { 131 | "label": "🔀", 132 | "detail": "🔀 = Merging branches." 133 | }, 134 | { 135 | "label": "📦", 136 | "detail": "📦 = Updating compiled files or packages." 137 | }, 138 | { 139 | "label": "🙈", 140 | "detail": "🙈 = Adding or updating a .gitignore file" 141 | } 142 | ], 143 | "type": "aliasesType", 144 | "aliasesType": [ 145 | { 146 | "label": "work" 147 | }, 148 | { 149 | "label": "fun" 150 | }, 151 | { 152 | "label": "studies" 153 | }, 154 | { 155 | "label": "friends" 156 | }, 157 | { 158 | "label": "college" 159 | } 160 | ] 161 | } 162 | ``` 163 | -------------------------------------------------------------------------------- /share/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "vscodeGitCommit.template": [ 3 | "{prefix}: {message}", 4 | ], 5 | "vscodeGitCommit.variables": { 6 | "prefix": "keke", 7 | "keke": [ 8 | { 9 | "label": "✨feature", 10 | "detail": "Select when creating new things", 11 | }, 12 | { 13 | "label": "🐞 fix", 14 | "detail": "Select when fixing a bug", 15 | }, 16 | { 17 | "label": "📄 docs", 18 | "detail": "Select when editing documentation", 19 | }, 20 | { 21 | "label": "🖥️ wip", 22 | "detail": "Select when work is not finished", 23 | }, 24 | { 25 | "label": "🚅 perfs", 26 | "detail": "Select when working on performances", 27 | }, 28 | { 29 | "label": "⏪ rollback", 30 | "detail": "Select when undoing something", 31 | }, 32 | { 33 | "label": "🔵 other", 34 | "detail": "Select when fixing a bug", 35 | } 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /share/default.md: -------------------------------------------------------------------------------- 1 | # Git message template (default) 2 | 3 | Added by: [Diogo-Rossi](https://github.com/Diogo-Rossi) 4 | Source: [Default](./default.json) 5 | 6 | Settings: 7 | 8 | ```json 9 | "vscodeGitCommit.template": [ 10 | "{prefix}: {message}", 11 | ], 12 | "vscodeGitCommit.variables": { 13 | "prefix": "keke", 14 | "keke": [ 15 | { 16 | "label": "✨feature", 17 | "detail": "Select when creating new things", 18 | }, 19 | { 20 | "label": "🐞 fix", 21 | "detail": "Select when fixing a bug", 22 | }, 23 | { 24 | "label": "📄 docs", 25 | "detail": "Select when editing documentation", 26 | }, 27 | { 28 | "label": "🖥️ wip", 29 | "detail": "Select when work is not finished", 30 | }, 31 | { 32 | "label": "🚅 perfs", 33 | "detail": "Select when working on performances", 34 | }, 35 | { 36 | "label": "⏪ rollback", 37 | "detail": "Select when undoing something", 38 | }, 39 | { 40 | "label": "🔵 other", 41 | "detail": "Select when fixing a bug", 42 | } 43 | ] 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /share/diogorossi.md: -------------------------------------------------------------------------------- 1 | # Git message template (Diogo-Rossi) 2 | 3 | Added by: [Diogo-Rossi](https://github.com/Diogo-Rossi) 4 | Source: [Gist](https://gist.github.com/Diogo-Rossi/79f39b5982c7c6eb8a32c5871c926b75) 5 | 6 | Settings: 7 | 8 | ```json 9 | "vscodeGitCommit.template": [ "{type}: {message}" ], 10 | "vscodeGitCommit.variables": { 11 | "type": [ 12 | { 13 | "label": "🚧 wip", 14 | "detail": "Work in progress." 15 | }, 16 | { 17 | "label": "__🔒 private", 18 | "detail": "Add/edit private stuff." 19 | }, 20 | { 21 | "label": "🎨 chore", 22 | "detail": "Improving structure / format of the code." 23 | }, 24 | { 25 | "label": "🎉 new", 26 | "detail": "Initial (NEW) commit or new scripts." 27 | }, 28 | { 29 | "label": "✨ feat", 30 | "detail": "Introducing new features." 31 | }, 32 | { 33 | "label": "🐞 fix", 34 | "detail": "Fixing a bug." 35 | }, 36 | { 37 | "label": "📄 docs", 38 | "detail": "To edit documentation" 39 | }, 40 | { 41 | "label": "👷 build", 42 | "detail": "Changes that affect the build system." 43 | }, 44 | { 45 | "label": "♻️ refactor", 46 | "detail": "A code change that neither fixes a bug nor adds a feature" 47 | }, 48 | { 49 | "label": "⏪ revert", 50 | "detail": "Reverting changes." 51 | }, 52 | { 53 | "label": "💄 style", 54 | "detail": "Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)." 55 | }, 56 | { 57 | "label": "⚡️ perf", 58 | "detail": "Improving performance" 59 | }, 60 | { 61 | "label": "✏️ typo", 62 | "detail": "Fixing typos." 63 | }, 64 | { 65 | "label": "✅ test", 66 | "detail": "Adding missing tests or correcting existing tests." 67 | }, 68 | { 69 | "label": "🏷️ typing", 70 | "detail": "Adding or updating types (Flow, TypeScript)." 71 | }, 72 | { 73 | "label": "💥 change!", 74 | "detail": "Introducing BREAKING CHANGES." 75 | }, 76 | { 77 | "label": "🚀 release", 78 | "detail": "Deploying stuff to release." 79 | }, 80 | { 81 | "label": "📦 compiled", 82 | "detail": "Updating compiled files or packages." 83 | }, 84 | { 85 | "label": "⬆️ dependencies", 86 | "detail": "Upgrading dependencies." 87 | } 88 | ] 89 | } 90 | ``` 91 | -------------------------------------------------------------------------------- /share/wilsonmar.md: -------------------------------------------------------------------------------- 1 | # Git message template (Wilson Mar) 2 | 3 | Added by: [jycouet](https://github.com/jycouet) 4 | Source: [Source](https://wilsonmar.github.io/git-messages/) 5 | 6 | Settings: 7 | 8 | ```json 9 | "vscodeGitCommit.template": ["{icon} {type}: {message}"], 10 | "vscodeGitCommit.variables": { 11 | "icon": "aliasesIcons", 12 | "aliasesIcons": [ 13 | { 14 | "label": ":tada:", 15 | "detail": "🎉 = Initial (NEW) commit." 16 | }, 17 | { 18 | "label": ":construction:", 19 | "detail": "🚧 = Work in progress." 20 | }, 21 | { 22 | "label": ":ambulance:", 23 | "detail": "🚑 = Critical hotfix." 24 | }, 25 | { 26 | "label": ":sparkles:", 27 | "detail": "✨ = Introducing new features." 28 | }, 29 | { 30 | "label": ":beers:", 31 | "detail": "🍻 = Writing code drunkenly." 32 | }, 33 | { 34 | "label": ":hankey:", 35 | "detail": "💩 = Writing bad code that needs to be improved." 36 | }, 37 | { 38 | "label": ":rotating_light:", 39 | "detail": "🚨 :rotating_light: = Removing linter warnings." 40 | }, 41 | { 42 | "label": ":rewind:", 43 | "detail": "⏪ = Reverting changes." 44 | }, 45 | { 46 | "label": ":lock:", 47 | "detail": "🔒 = Fixing security issues." 48 | }, 49 | { 50 | "label": ":zap:", 51 | "detail": "⚡️ = Improving performance." 52 | }, 53 | { 54 | "label": ":children_crossing:", 55 | "detail": "🚸 = Improving user experience / usability." 56 | }, 57 | { 58 | "label": ":bug:", 59 | "detail": "🐛 = Fixing a bug." 60 | }, 61 | { 62 | "label": ":bulb:", 63 | "detail": "💡 = Documenting source code" 64 | }, 65 | { 66 | "label": ":white_check_mark:", 67 | "detail": "✅ = Updating tests." 68 | }, 69 | { 70 | "label": ":ok_hand:", 71 | "detail": "👌 = Updating code due to code review changes." 72 | }, 73 | { 74 | "label": ":clown_face:", 75 | "detail": "🤡 = Mocking things." 76 | }, 77 | { 78 | "label": ":boom:", 79 | "detail": "💥 = Introducing breaking changes." 80 | }, 81 | { 82 | "label": ":arrow_up:", 83 | "detail": "⬆️ = Upgrading dependencies." 84 | }, 85 | { 86 | "label": ":fire:", 87 | "detail": "🔥 = Removing code or files." 88 | }, 89 | { 90 | "label": ":pencil2:", 91 | "detail": "✏️ = Fixing typos." 92 | }, 93 | { 94 | "label": ":loud_sound:", 95 | "detail": "🔊 = Adding logs." 96 | }, 97 | { 98 | "label": ":mute:", 99 | "detail": "🔇 = Removing logs." 100 | }, 101 | { 102 | "label": ":art:", 103 | "detail": "🎨 = Improving structure / format of the code." 104 | }, 105 | { 106 | "label": ":recycle:", 107 | "detail": "♻️ = Refactoring code logic." 108 | }, 109 | { 110 | "label": ":construction_worker:", 111 | "detail": "👷 = Adding CI build system." 112 | }, 113 | { 114 | "label": ":rocket:", 115 | "detail": "🚀 = Deploying stuff." 116 | }, 117 | { 118 | "label": ":label:", 119 | "detail": "🏷️ = Adding or updating types (Flow, TypeScript)" 120 | }, 121 | { 122 | "label": ":card_file_box:", 123 | "detail": "🗃 = Performing database related changes." 124 | }, 125 | { 126 | "label": ":lipstick:", 127 | "detail": "💄 = Updating the UI and style files." 128 | }, 129 | { 130 | "label": ":twisted_rightwards_arrows:", 131 | "detail": "🔀 = Merging branches." 132 | }, 133 | { 134 | "label": ":package:", 135 | "detail": "📦 = Updating compiled files or packages." 136 | }, 137 | { 138 | "label": ":see_no_evil:", 139 | "detail": "🙈 = Adding or updating a .gitignore file" 140 | } 141 | ], 142 | "type": "aliasesType", 143 | "aliasesType": [ 144 | { 145 | "label": "NEW" 146 | }, 147 | { 148 | "label": "REMOVE" 149 | }, 150 | { 151 | "label": "RENAME" 152 | }, 153 | { 154 | "label": "DOC" 155 | }, 156 | { 157 | "label": "UPDATE" 158 | }, 159 | { 160 | "label": "IMPROVE" 161 | }, 162 | { 163 | "label": "FIX" 164 | }, 165 | { 166 | "label": "RELEASE" 167 | } 168 | ] 169 | } 170 | ``` 171 | -------------------------------------------------------------------------------- /src/config/const.ts: -------------------------------------------------------------------------------- 1 | export const EXTENSION_NAME = 'vscodeGitCommit'; 2 | -------------------------------------------------------------------------------- /src/config/constants.ts: -------------------------------------------------------------------------------- 1 | import { IQuickPickSettings } from '../typings/quickPick'; 2 | import { EXTENSION_NAME } from './const'; 3 | 4 | export const QUICKPICKITEMSKEKE: Array = [ 5 | { 6 | label: '✨feature', 7 | detail: 'Select when creating new things', 8 | }, 9 | { 10 | label: '🐞 fix', 11 | detail: 'Select when fixing a bug', 12 | }, 13 | { 14 | label: '📄 docs', 15 | detail: 'Select when editing documentation', 16 | }, 17 | { 18 | label: '🖥️ wip', 19 | detail: 'Select when work is not finished', 20 | }, 21 | { 22 | label: '🚅 perfs', 23 | detail: 'Select when working on performances', 24 | }, 25 | { 26 | label: '⏪ rollback', 27 | detail: 'Select when undoing something', 28 | }, 29 | { 30 | label: '🔵 other', 31 | detail: 'Select when fixing a bug', 32 | }, 33 | ]; 34 | 35 | export const QUICKPICKITEMSANGULAR: Array = [ 36 | { 37 | label: '✨ feat', 38 | detail: 'Select when creating new things', 39 | }, 40 | { 41 | label: '🐞 fix', 42 | detail: 'Select when fixing a bug', 43 | }, 44 | { 45 | label: '📄 docs', 46 | detail: 'Select when editing documentation', 47 | }, 48 | { 49 | label: '🚀 ci', 50 | detail: 'Select when editing CI scripts', 51 | }, 52 | { 53 | label: '🖥️ build', 54 | detail: 'Select when concerning build change or external dependencies', 55 | }, 56 | { 57 | label: '⚙️ refactor', 58 | detail: 'Select when no new performances or no new functionnality', 59 | }, 60 | { 61 | label: '🌈 style', 62 | detail: 'Select when working on code style', 63 | }, 64 | { 65 | label: '⚡ perf', 66 | detail: 'Select when working on performances', 67 | }, 68 | { 69 | label: '⏪ revert', 70 | detail: 'Select when going back', 71 | }, 72 | { 73 | label: '🔵 test', 74 | detail: 'Select when adding or editing tests', 75 | }, 76 | ]; 77 | 78 | export const QUICKPICKITEMSSEMANTIC: Array = [ 79 | { 80 | label: '✨ feat', 81 | detail: 'new feature for the user, not a new feature for build script', 82 | }, 83 | { 84 | label: '🐞 fix', 85 | detail: 'bug fix for the user, not a fix to a build script', 86 | }, 87 | { 88 | label: '📄 docs', 89 | detail: 'changes to the documentation', 90 | }, 91 | { 92 | label: '🌈 style', 93 | detail: 'formatting, missing semi colons, etc; no production code change', 94 | }, 95 | { 96 | label: '⚙️ refactor', 97 | detail: 'refactoring production code, eg. renaming a variable', 98 | }, 99 | { 100 | label: '🔵 test', 101 | detail: 102 | 'adding missing tests, refactoring tests; no production code change', 103 | }, 104 | { 105 | label: '🚀 chore', 106 | detail: 'updating grunt tasks etc; no production code change', 107 | }, 108 | ]; 109 | 110 | export const QUICKPICKITEMSALPHA8: Array = [ 111 | { 112 | label: '✨ feat', 113 | detail: 'A new feature. Correlates with MINOR in SemVer', 114 | }, 115 | { 116 | label: '🐞 fix', 117 | detail: 'A bug fix. Correlates with PATCH in SemVer', 118 | }, 119 | { 120 | label: '📄 docs', 121 | detail: 'Documentation only changes', 122 | }, 123 | { 124 | label: '🌈 style', 125 | detail: 126 | 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', 127 | }, 128 | { 129 | label: '⚙️ refactor', 130 | detail: 'A code change that neither fixes a bug nor adds a feature', 131 | }, 132 | { 133 | label: '🕜 perf', 134 | detail: 'A code change that improves performance', 135 | }, 136 | { 137 | label: '🧪 test', 138 | detail: 'Adding missing or correcting existing tests', 139 | }, 140 | { 141 | label: '🏗️ build', 142 | detail: 143 | 'Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)', 144 | }, 145 | { 146 | label: '🤹 ci', 147 | detail: 148 | 'Changes to our CI configuration files and scripts (example scopes: GitLabCI)', 149 | }, 150 | ]; 151 | 152 | export const QUICKPICKITEMSUNDEFINED: Array = [ 153 | { 154 | label: 'Unknow Variable', 155 | detail: `It seems like you use an undefined preset in setting ${EXTENSION_NAME}.variables`, 156 | }, 157 | ]; 158 | -------------------------------------------------------------------------------- /src/config/dynamics.ts: -------------------------------------------------------------------------------- 1 | import { Repository, Status } from '../typings/git'; 2 | import { IQuickPickSettings } from '../typings/quickPick'; 3 | import { getFiles, getStagedFiles, getUnstagedFiles } from '../utils/files'; 4 | import { getCurrentBranch } from '../utils/git'; 5 | import { QUICKPICKITEMSUNDEFINED } from './constants'; 6 | 7 | export const dynamicsAggregation: { 8 | [key: string]: (repo: Repository) => IQuickPickSettings[]; 9 | } = { 10 | files: getFiles(), 11 | 'files.deleted': getFiles(Status.DELETED), 12 | 'files.modified': getFiles(Status.MODIFIED), 13 | 'files.added': getFiles(Status.INTENT_TO_ADD), 14 | 'files.staged': getStagedFiles(), 15 | 'files.staged.deleted': getStagedFiles(Status.DELETED), 16 | 'files.staged.modified': getStagedFiles(Status.MODIFIED), 17 | 'files.staged.added': getStagedFiles(Status.INTENT_TO_ADD), 18 | 'files.changed': getUnstagedFiles(), 19 | 'files.changed.deleted': getUnstagedFiles(Status.DELETED), 20 | 'files.changed.modified': getUnstagedFiles(Status.MODIFIED), 21 | 'files.changed.added': getUnstagedFiles(Status.INTENT_TO_ADD), 22 | branch: getCurrentBranch, 23 | }; 24 | 25 | export const getDynamicPreset = ( 26 | repo: Repository, 27 | name: string 28 | ): Array => { 29 | if (Object.keys(dynamicsAggregation).includes(name)) { 30 | return dynamicsAggregation[name](repo); 31 | } else { 32 | return QUICKPICKITEMSUNDEFINED; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/config/presets.ts: -------------------------------------------------------------------------------- 1 | import { IQuickPickSettings } from '../typings/quickPick'; 2 | import { 3 | QUICKPICKITEMSALPHA8, 4 | QUICKPICKITEMSANGULAR, 5 | QUICKPICKITEMSKEKE, 6 | QUICKPICKITEMSSEMANTIC, 7 | QUICKPICKITEMSUNDEFINED, 8 | } from './constants'; 9 | 10 | export const presetAggregation: { [key: string]: IQuickPickSettings[] } = { 11 | angular: QUICKPICKITEMSANGULAR, 12 | semantic: QUICKPICKITEMSSEMANTIC, 13 | alpha8: QUICKPICKITEMSALPHA8, 14 | keke: QUICKPICKITEMSKEKE, 15 | }; 16 | 17 | export const getLocalPreset = (name: string): Array => { 18 | if (Object.keys(presetAggregation).includes(name)) { 19 | return presetAggregation[name]; 20 | } else { 21 | return QUICKPICKITEMSUNDEFINED; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { commands, window, ExtensionContext } from 'vscode'; 2 | import { getRepo } from './utils/git'; 3 | import { execute } from './scripts/run'; 4 | import { Repository } from './typings/git'; 5 | import { migrate } from './utils/migrate'; 6 | import { isUpToDate } from './utils/storage'; 7 | 8 | export function activate(context: ExtensionContext) { 9 | // Init 10 | console.log( 11 | 'Congratulations, your extension "rioukkevin.vscode-git-commit" is now active!' 12 | ); 13 | 14 | // MIGRATION 15 | migrate(context); 16 | 17 | // CMD register 18 | const disposable = commands.registerCommand( 19 | 'vscodeGitCommit.setMessage', 20 | (params) => { 21 | commands.executeCommand('workbench.view.scm'); 22 | const repoUri = params?.rootUri?.toString() || undefined; 23 | // params?._quickDiffProvider?.repository?.repositoryRoot || undefined; 24 | let repo: Repository | false = getRepo(repoUri); 25 | if (!!repo) { 26 | setTimeout(async () => { 27 | await execute(repo as Repository); 28 | commands.executeCommand('workbench.scm.focus'); 29 | }, 200); 30 | } 31 | } 32 | ); 33 | setTimeout(() => { 34 | context.subscriptions.push(disposable); 35 | 36 | isUpToDate(context, window); 37 | }, 1000); 38 | } 39 | -------------------------------------------------------------------------------- /src/scripts/run.ts: -------------------------------------------------------------------------------- 1 | import { Repository } from '../typings/git'; 2 | import { IQuickPickSettings } from '../typings/quickPick'; 3 | import { useQuickPick, useQuickText } from '../utils/actions'; 4 | import { setGitMessage } from '../utils/git'; 5 | import { 6 | getDefaultVariablesValues, 7 | getTemplate, 8 | getVariableDisplayTitles, 9 | getVariables, 10 | } from '../utils/settings'; 11 | import { 12 | templateParser, 13 | templateSerialize, 14 | TVariable, 15 | } from '../utils/template'; 16 | import { parseVariable } from '../utils/variables'; 17 | 18 | export const execute = async (repo: Repository) => { 19 | const template = getTemplate(); 20 | 21 | const variables = templateParser(template || '{prefix}: {message}'); 22 | 23 | const variablesReplacement: TVariable[] = []; 24 | 25 | const variablesDisplayTitles = getVariableDisplayTitles(); 26 | 27 | for (let i = 0; i < variables.length; i++) { 28 | const v = variables[i]; 29 | 30 | const title = variablesDisplayTitles[v]; 31 | 32 | const variablesSettings = getVariables(); 33 | const variableValue = variablesSettings[v]; 34 | let result = { 35 | key: v, 36 | value: '', 37 | }; 38 | if (!variableValue) { 39 | const defaultValuesSettings = getDefaultVariablesValues(); 40 | const defaultValue: string | undefined = defaultValuesSettings[v]; 41 | 42 | result.value = await useQuickText({ 43 | title, 44 | prompt: !!defaultValue 45 | ? `The default value is: "${defaultValue}"` 46 | : undefined, 47 | value: defaultValue ?? '', 48 | ignoreFocusOut: true, 49 | placeHolder: `Please type the value for <${v}>`, 50 | }); 51 | } else { 52 | const choices: IQuickPickSettings[] = parseVariable(repo, v); 53 | result.value = await useQuickPick( 54 | { 55 | title, 56 | ignoreFocusOut: true, 57 | placeHolder: `Please select a value for <${v}>`, 58 | }, 59 | choices.map((c) => ({ 60 | ...c, 61 | detail: c.detail ?? '', 62 | })) 63 | ); 64 | } 65 | variablesReplacement.push(result); 66 | } 67 | 68 | if (variablesReplacement.length >= variables.length) { 69 | setGitMessage( 70 | repo, 71 | templateSerialize(template || '{prefix}: {message}', variablesReplacement) 72 | ); 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /src/typings/action.d.ts: -------------------------------------------------------------------------------- 1 | export interface IAction { 2 | name: string; 3 | action: (...params: any) => string; 4 | } 5 | -------------------------------------------------------------------------------- /src/typings/git.d.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri, Event, Disposable, ProviderResult, Command } from 'vscode'; 7 | export { ProviderResult } from 'vscode'; 8 | 9 | export interface Git { 10 | readonly path: string; 11 | } 12 | 13 | export interface InputBox { 14 | value: string; 15 | } 16 | 17 | export const enum ForcePushMode { 18 | Force, 19 | ForceWithLease, 20 | } 21 | 22 | export const enum RefType { 23 | Head, 24 | RemoteHead, 25 | Tag, 26 | } 27 | 28 | export interface Ref { 29 | readonly type: RefType; 30 | readonly name?: string; 31 | readonly commit?: string; 32 | readonly remote?: string; 33 | } 34 | 35 | export interface UpstreamRef { 36 | readonly remote: string; 37 | readonly name: string; 38 | } 39 | 40 | export interface Branch extends Ref { 41 | readonly upstream?: UpstreamRef; 42 | readonly ahead?: number; 43 | readonly behind?: number; 44 | } 45 | 46 | export interface Commit { 47 | readonly hash: string; 48 | readonly message: string; 49 | readonly parents: string[]; 50 | readonly authorDate?: Date; 51 | readonly authorName?: string; 52 | readonly authorEmail?: string; 53 | readonly commitDate?: Date; 54 | } 55 | 56 | export interface Submodule { 57 | readonly name: string; 58 | readonly path: string; 59 | readonly url: string; 60 | } 61 | 62 | export interface Remote { 63 | readonly name: string; 64 | readonly fetchUrl?: string; 65 | readonly pushUrl?: string; 66 | readonly isReadOnly: boolean; 67 | } 68 | 69 | export const enum Status { 70 | INDEX_MODIFIED, 71 | INDEX_ADDED, 72 | INDEX_DELETED, 73 | INDEX_RENAMED, 74 | INDEX_COPIED, 75 | 76 | MODIFIED, 77 | DELETED, 78 | UNTRACKED, 79 | IGNORED, 80 | INTENT_TO_ADD, 81 | 82 | ADDED_BY_US, 83 | ADDED_BY_THEM, 84 | DELETED_BY_US, 85 | DELETED_BY_THEM, 86 | BOTH_ADDED, 87 | BOTH_DELETED, 88 | BOTH_MODIFIED, 89 | } 90 | 91 | export interface Change { 92 | /** 93 | * Returns either `originalUri` or `renameUri`, depending 94 | * on whether this change is a rename change. When 95 | * in doubt always use `uri` over the other two alternatives. 96 | */ 97 | readonly uri: Uri; 98 | readonly originalUri: Uri; 99 | readonly renameUri: Uri | undefined; 100 | readonly status: Status; 101 | } 102 | 103 | export interface RepositoryState { 104 | readonly HEAD: Branch | undefined; 105 | readonly refs: Ref[]; 106 | readonly remotes: Remote[]; 107 | readonly submodules: Submodule[]; 108 | readonly rebaseCommit: Commit | undefined; 109 | 110 | readonly mergeChanges: Change[]; 111 | readonly indexChanges: Change[]; 112 | readonly workingTreeChanges: Change[]; 113 | 114 | readonly onDidChange: Event; 115 | } 116 | 117 | export interface RepositoryUIState { 118 | readonly selected: boolean; 119 | readonly onDidChange: Event; 120 | } 121 | 122 | /** 123 | * Log options. 124 | */ 125 | export interface LogOptions { 126 | /** Max number of log entries to retrieve. If not specified, the default is 32. */ 127 | readonly maxEntries?: number; 128 | readonly path?: string; 129 | } 130 | 131 | export interface CommitOptions { 132 | all?: boolean | 'tracked'; 133 | amend?: boolean; 134 | signoff?: boolean; 135 | signCommit?: boolean; 136 | empty?: boolean; 137 | noVerify?: boolean; 138 | requireUserConfig?: boolean; 139 | useEditor?: boolean; 140 | verbose?: boolean; 141 | postCommitCommand?: string; 142 | } 143 | 144 | export interface FetchOptions { 145 | remote?: string; 146 | ref?: string; 147 | all?: boolean; 148 | prune?: boolean; 149 | depth?: number; 150 | } 151 | 152 | export interface BranchQuery { 153 | readonly remote?: boolean; 154 | readonly pattern?: string; 155 | readonly count?: number; 156 | readonly contains?: string; 157 | } 158 | 159 | export interface Repository { 160 | readonly rootUri: Uri; 161 | readonly inputBox: InputBox; 162 | readonly state: RepositoryState; 163 | readonly ui: RepositoryUIState; 164 | 165 | getConfigs(): Promise<{ key: string; value: string }[]>; 166 | getConfig(key: string): Promise; 167 | setConfig(key: string, value: string): Promise; 168 | getGlobalConfig(key: string): Promise; 169 | 170 | getObjectDetails( 171 | treeish: string, 172 | path: string 173 | ): Promise<{ mode: string; object: string; size: number }>; 174 | detectObjectType( 175 | object: string 176 | ): Promise<{ mimetype: string; encoding?: string }>; 177 | buffer(ref: string, path: string): Promise; 178 | show(ref: string, path: string): Promise; 179 | getCommit(ref: string): Promise; 180 | 181 | add(paths: string[]): Promise; 182 | revert(paths: string[]): Promise; 183 | clean(paths: string[]): Promise; 184 | 185 | apply(patch: string, reverse?: boolean): Promise; 186 | diff(cached?: boolean): Promise; 187 | diffWithHEAD(): Promise; 188 | diffWithHEAD(path: string): Promise; 189 | diffWith(ref: string): Promise; 190 | diffWith(ref: string, path: string): Promise; 191 | diffIndexWithHEAD(): Promise; 192 | diffIndexWithHEAD(path: string): Promise; 193 | diffIndexWith(ref: string): Promise; 194 | diffIndexWith(ref: string, path: string): Promise; 195 | diffBlobs(object1: string, object2: string): Promise; 196 | diffBetween(ref1: string, ref2: string): Promise; 197 | diffBetween(ref1: string, ref2: string, path: string): Promise; 198 | 199 | hashObject(data: string): Promise; 200 | 201 | createBranch(name: string, checkout: boolean, ref?: string): Promise; 202 | deleteBranch(name: string, force?: boolean): Promise; 203 | getBranch(name: string): Promise; 204 | getBranches(query: BranchQuery): Promise; 205 | setBranchUpstream(name: string, upstream: string): Promise; 206 | 207 | getMergeBase(ref1: string, ref2: string): Promise; 208 | 209 | tag(name: string, upstream: string): Promise; 210 | deleteTag(name: string): Promise; 211 | 212 | status(): Promise; 213 | checkout(treeish: string): Promise; 214 | 215 | addRemote(name: string, url: string): Promise; 216 | removeRemote(name: string): Promise; 217 | renameRemote(name: string, newName: string): Promise; 218 | 219 | fetch(options?: FetchOptions): Promise; 220 | fetch(remote?: string, ref?: string, depth?: number): Promise; 221 | pull(unshallow?: boolean): Promise; 222 | push( 223 | remoteName?: string, 224 | branchName?: string, 225 | setUpstream?: boolean, 226 | force?: ForcePushMode 227 | ): Promise; 228 | 229 | blame(path: string): Promise; 230 | log(options?: LogOptions): Promise; 231 | 232 | commit(message: string, opts?: CommitOptions): Promise; 233 | } 234 | 235 | export interface RemoteSource { 236 | readonly name: string; 237 | readonly description?: string; 238 | readonly url: string | string[]; 239 | } 240 | 241 | export interface RemoteSourceProvider { 242 | readonly name: string; 243 | readonly icon?: string; // codicon name 244 | readonly supportsQuery?: boolean; 245 | getRemoteSources(query?: string): ProviderResult; 246 | getBranches?(url: string): ProviderResult; 247 | publishRepository?(repository: Repository): Promise; 248 | } 249 | 250 | export interface RemoteSourcePublisher { 251 | readonly name: string; 252 | readonly icon?: string; // codicon name 253 | publishRepository(repository: Repository): Promise; 254 | } 255 | 256 | export interface Credentials { 257 | readonly username: string; 258 | readonly password: string; 259 | } 260 | 261 | export interface CredentialsProvider { 262 | getCredentials(host: Uri): ProviderResult; 263 | } 264 | 265 | export interface PostCommitCommandsProvider { 266 | getCommands(repository: Repository): Command[]; 267 | } 268 | 269 | export interface PushErrorHandler { 270 | handlePushError( 271 | repository: Repository, 272 | remote: Remote, 273 | refspec: string, 274 | error: Error & { gitErrorCode: GitErrorCodes } 275 | ): Promise; 276 | } 277 | 278 | export type APIState = 'uninitialized' | 'initialized'; 279 | 280 | export interface PublishEvent { 281 | repository: Repository; 282 | branch?: string; 283 | } 284 | 285 | export interface API { 286 | readonly state: APIState; 287 | readonly onDidChangeState: Event; 288 | readonly onDidPublish: Event; 289 | readonly git: Git; 290 | readonly repositories: Repository[]; 291 | readonly onDidOpenRepository: Event; 292 | readonly onDidCloseRepository: Event; 293 | 294 | toGitUri(uri: Uri, ref: string): Uri; 295 | getRepository(uri: Uri): Repository | null; 296 | init(root: Uri): Promise; 297 | openRepository(root: Uri): Promise; 298 | 299 | registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable; 300 | registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; 301 | registerCredentialsProvider(provider: CredentialsProvider): Disposable; 302 | registerPostCommitCommandsProvider( 303 | provider: PostCommitCommandsProvider 304 | ): Disposable; 305 | registerPushErrorHandler(handler: PushErrorHandler): Disposable; 306 | } 307 | 308 | export interface GitExtension { 309 | readonly enabled: boolean; 310 | readonly onDidChangeEnablement: Event; 311 | 312 | /** 313 | * Returns a specific API version. 314 | * 315 | * Throws error if git extension is disabled. You can listen to the 316 | * [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event 317 | * to know when the extension becomes enabled/disabled. 318 | * 319 | * @param version Version number. 320 | * @returns API instance 321 | */ 322 | getAPI(version: 1): API; 323 | } 324 | 325 | export const enum GitErrorCodes { 326 | BadConfigFile = 'BadConfigFile', 327 | AuthenticationFailed = 'AuthenticationFailed', 328 | NoUserNameConfigured = 'NoUserNameConfigured', 329 | NoUserEmailConfigured = 'NoUserEmailConfigured', 330 | NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified', 331 | NotAGitRepository = 'NotAGitRepository', 332 | NotAtRepositoryRoot = 'NotAtRepositoryRoot', 333 | Conflict = 'Conflict', 334 | StashConflict = 'StashConflict', 335 | UnmergedChanges = 'UnmergedChanges', 336 | PushRejected = 'PushRejected', 337 | RemoteConnectionError = 'RemoteConnectionError', 338 | DirtyWorkTree = 'DirtyWorkTree', 339 | CantOpenResource = 'CantOpenResource', 340 | GitNotFound = 'GitNotFound', 341 | CantCreatePipe = 'CantCreatePipe', 342 | PermissionDenied = 'PermissionDenied', 343 | CantAccessRemote = 'CantAccessRemote', 344 | RepositoryNotFound = 'RepositoryNotFound', 345 | RepositoryIsLocked = 'RepositoryIsLocked', 346 | BranchNotFullyMerged = 'BranchNotFullyMerged', 347 | NoRemoteReference = 'NoRemoteReference', 348 | InvalidBranchName = 'InvalidBranchName', 349 | BranchAlreadyExists = 'BranchAlreadyExists', 350 | NoLocalChanges = 'NoLocalChanges', 351 | NoStashFound = 'NoStashFound', 352 | LocalChangesOverwritten = 'LocalChangesOverwritten', 353 | NoUpstreamBranch = 'NoUpstreamBranch', 354 | IsInSubmodule = 'IsInSubmodule', 355 | WrongCase = 'WrongCase', 356 | CantLockRef = 'CantLockRef', 357 | CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', 358 | PatchDoesNotApply = 'PatchDoesNotApply', 359 | NoPathFound = 'NoPathFound', 360 | UnknownPath = 'UnknownPath', 361 | EmptyCommitMessage = 'EmptyCommitMessage', 362 | } 363 | -------------------------------------------------------------------------------- /src/typings/quickPick.d.ts: -------------------------------------------------------------------------------- 1 | import { QuickPickItem } from 'vscode'; 2 | 3 | export interface IQuickPickSettings 4 | extends Omit { 5 | detail?: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/typings/settings.d.ts: -------------------------------------------------------------------------------- 1 | import { IQuickPickSettings } from './quickPick'; 2 | 3 | type ISettingVariable = string | IQuickPickSettings[]; 4 | 5 | type ISettingVariables = { 6 | [key: string]: ISettingVariable; 7 | }; 8 | 9 | type ISettingDefaultVariablesValue = string; 10 | 11 | type ISettingDefaultVariablesValues = { 12 | [key: string]: ISettingDefaultVariablesValue; 13 | }; 14 | 15 | type ISettingVariablesDisplayTitle = string | undefined; 16 | 17 | type ISettingVariablesDisplayTitles = { 18 | [key: string]: ISettingVariablesDisplayTitle; 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/actions.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode'; 2 | import { InputBoxOptions, QuickPickOptions } from 'vscode'; 3 | import { IQuickPickSettings } from '../typings/quickPick'; 4 | import { konsole } from './feedback'; 5 | 6 | export const useQuickPick = async ( 7 | params: QuickPickOptions, 8 | values: IQuickPickSettings[] 9 | ): Promise => { 10 | const value = await window.showQuickPick(values, params); 11 | if (!value) { 12 | konsole.error('Message is cancel'); 13 | throw new Error('Empty'); 14 | } 15 | return value.label; 16 | }; 17 | 18 | export const useQuickText = async ( 19 | params: InputBoxOptions 20 | ): Promise => { 21 | const message = await window.showInputBox(params); 22 | if (message === undefined) { 23 | konsole.error('Message is cancel'); 24 | throw new Error('Canceled'); 25 | } 26 | return message; 27 | }; 28 | -------------------------------------------------------------------------------- /src/utils/feedback.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode'; 2 | 3 | export const konsole = { 4 | info: (message: string) => window.showInformationMessage(message), 5 | error: (message: string) => window.showErrorMessage(message), 6 | }; 7 | -------------------------------------------------------------------------------- /src/utils/files.ts: -------------------------------------------------------------------------------- 1 | import { Change, Repository, Status } from '../typings/git'; 2 | import { IQuickPickSettings } from '../typings/quickPick'; 3 | import { setLengtToX } from './strings'; 4 | 5 | const MAX_LENGTH = 100; 6 | 7 | const getQuickPickItemFromPath = 8 | (repo: Repository) => 9 | (path: string): IQuickPickSettings => { 10 | const repoPath = repo.rootUri.path; 11 | const shortedPath = path.replace('file://', '').replace(repoPath, ''); 12 | const splittedPath = shortedPath.split('/'); 13 | const fileName = splittedPath[splittedPath.length - 1]; 14 | return { 15 | label: fileName, 16 | detail: setLengtToX(MAX_LENGTH)(shortedPath), 17 | }; 18 | }; 19 | 20 | const getEmptyQuickPick = () => { 21 | return [ 22 | { 23 | label: '', 24 | detail: 'No files corresponding to this input, press Enter to continue', 25 | }, 26 | ]; 27 | }; 28 | 29 | const getPathFiltered = (status?: Status) => (c: Change) => { 30 | if (status) { 31 | return c.status === status ? c.originalUri.toString() : ''; 32 | } 33 | return c.originalUri.toString(); 34 | }; 35 | 36 | export const getFiles = 37 | (status?: Status) => 38 | (repo: Repository): IQuickPickSettings[] => { 39 | const result = [ 40 | ...repo.state.indexChanges, 41 | ...repo.state.workingTreeChanges, 42 | ].map(getPathFiltered(status)); 43 | if (result.length === 0) { 44 | return getEmptyQuickPick(); 45 | } 46 | return result.map(getQuickPickItemFromPath(repo)) ?? []; 47 | }; 48 | 49 | export const getStagedFiles = 50 | (status?: Status) => 51 | (repo: Repository): IQuickPickSettings[] => { 52 | const result = repo.state.indexChanges.map(getPathFiltered(status)); 53 | if (result.length === 0) { 54 | return getEmptyQuickPick(); 55 | } 56 | return result.map(getQuickPickItemFromPath(repo)) ?? []; 57 | }; 58 | 59 | export const getUnstagedFiles = 60 | (status?: Status) => 61 | (repo: Repository): IQuickPickSettings[] => { 62 | const result = repo.state.workingTreeChanges.map(getPathFiltered(status)); 63 | if (result.length === 0) { 64 | return getEmptyQuickPick(); 65 | } 66 | return result.map(getQuickPickItemFromPath(repo)) ?? []; 67 | }; 68 | -------------------------------------------------------------------------------- /src/utils/git.ts: -------------------------------------------------------------------------------- 1 | import { window, extensions, Uri } from 'vscode'; 2 | import { GitExtension, Repository } from '../typings/git'; 3 | import { IQuickPickSettings } from '../typings/quickPick'; 4 | import { getMode } from './settings'; 5 | 6 | function getGitExtension() { 7 | const vscodeGit = extensions.getExtension('vscode.git'); 8 | const gitExtension = vscodeGit && vscodeGit.exports; 9 | return gitExtension; 10 | } 11 | 12 | export const getRepo = (repoUri: string): Repository | false => { 13 | const gitExtension = getGitExtension(); 14 | if (!gitExtension?.enabled) { 15 | window.showErrorMessage( 16 | 'Git extensions are not currently enabled, please try again after enabled!' 17 | ); 18 | return false; 19 | } 20 | let repos: Repository[] = gitExtension.getAPI(1).repositories; 21 | const repo = repos.find( 22 | (e) => e.rootUri.toString() === repoUri 23 | // (e) => e._repository.repository.repositoryRoot === repoUri 24 | ); 25 | if (!repo && window.activeTextEditor) { 26 | return getRepoContainingFile(window.activeTextEditor.document.uri, repos); 27 | } 28 | return repo || repos[0]; 29 | }; 30 | 31 | export const setGitMessage = (repo: Repository, msg: string): void => { 32 | let mode: string | undefined = getMode(); 33 | if (mode && mode === 'Concatenate') { 34 | repo.inputBox.value += repo.inputBox.value.length > 1 ? '\n' + msg : msg; 35 | } else { 36 | repo.inputBox.value = msg; 37 | } 38 | }; 39 | 40 | export const getCurrentBranch = (repo: Repository): IQuickPickSettings[] => { 41 | const branch = repo.state.HEAD?.upstream?.name; 42 | const repository = repo.rootUri.path.split('/').pop(); 43 | return [ 44 | { 45 | label: branch ?? 'Branch not found', 46 | detail: '', 47 | }, 48 | { 49 | label: 50 | !!branch || !!repository 51 | ? `${repository}/${branch}` 52 | : 'Repo / Branch not found', 53 | detail: '', 54 | }, 55 | ]; 56 | }; 57 | 58 | /** 59 | * Get the repository that contains the specified file. 60 | * @param fileUri The uri of the file. 61 | * @returns The repository containing the file, or false if no known repository contains the file. 62 | */ 63 | export function getRepoContainingFile(fileUri: Uri, repos: Repository[]) { 64 | let repoUris = repos.map((rep) => rep.rootUri), 65 | repo = null; 66 | for (let i = 0; i < repoUris.length; i++) { 67 | if ( 68 | fileUri 69 | .toString() 70 | .startsWith(pathWithTrailingSlash(repoUris[i].toString())) && 71 | (repo === null || 72 | repo.rootUri.toString().length < repoUris[i].toString().length) 73 | ) 74 | repo = repos[i]; 75 | } 76 | return repo || false; 77 | } 78 | 79 | /** 80 | * Get the path with a trailing slash. 81 | * @param path The path. 82 | * @returns The path with a trailing slash. 83 | */ 84 | export function pathWithTrailingSlash(path: string) { 85 | return path.endsWith('/') ? path : path + '/'; 86 | } 87 | -------------------------------------------------------------------------------- /src/utils/migrate.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, workspace } from 'vscode'; 2 | import { EXTENSION_NAME } from '../config/const'; 3 | import { getVariables } from './settings'; 4 | 5 | export const migrate = (context: ExtensionContext) => { 6 | const inMemoryVersion = 7 | context.globalState.get(`${EXTENSION_NAME}.version`) ?? '0.0.0'; 8 | 9 | // V2 to V2.1 10 | const oldVariables = getVariables(); 11 | if ( 12 | oldVariables.aliases && 13 | !(oldVariables.prefix as string).includes('...') 14 | ) { 15 | const newVariables = { 16 | ...oldVariables, 17 | prefix: `aliases...${oldVariables.prefix}`, 18 | }; 19 | workspace 20 | .getConfiguration(EXTENSION_NAME) 21 | .update('variables', newVariables, true); 22 | console.log('MIGRATION TEMPLATE V2 -> V2.1'); 23 | } 24 | 25 | const v2Variables = getVariables(); 26 | 27 | // V2.1/2 to V3 28 | if (v2Variables && inMemoryVersion !== '3.0.0') { 29 | const newVariables: { [key: string]: any } = {}; 30 | Object.keys(v2Variables).map((key) => { 31 | const oldData = v2Variables[key]; 32 | if (Array.isArray(oldData)) { 33 | const stored = oldData.map((d) => ({ 34 | label: d.label, 35 | detail: d.detail, 36 | })); 37 | newVariables[key] = stored; 38 | } else { 39 | newVariables[key] = oldData; 40 | } 41 | }); 42 | workspace 43 | .getConfiguration(EXTENSION_NAME) 44 | .update('variables', newVariables, true); 45 | console.log('MIGRATION TEMPLATE V2.1/2 -> V3.1 (FIX)'); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /src/utils/settings.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from 'vscode'; 2 | import { EXTENSION_NAME } from '../config/const'; 3 | import { 4 | ISettingDefaultVariablesValues, 5 | ISettingVariables, 6 | ISettingVariablesDisplayTitle, 7 | ISettingVariablesDisplayTitles, 8 | } from '../typings/settings'; 9 | 10 | export const getMode = (): string | undefined => { 11 | const mode: string | undefined = workspace 12 | .getConfiguration(EXTENSION_NAME) 13 | .get('insertMode'); 14 | return mode; 15 | }; 16 | 17 | export const getTemplate = (): string | undefined => { 18 | const template: string[] | undefined = workspace 19 | .getConfiguration(EXTENSION_NAME) 20 | .get('template'); 21 | return template?.join('\n'); 22 | }; 23 | 24 | export const getVariables = (): ISettingVariables => { 25 | let variables: ISettingVariables | undefined = workspace 26 | .getConfiguration(EXTENSION_NAME) 27 | .get('variables'); 28 | return variables as ISettingVariables; 29 | }; 30 | 31 | export const getDefaultVariablesValues = (): ISettingDefaultVariablesValues => { 32 | let variables: ISettingVariables | undefined = workspace 33 | .getConfiguration(EXTENSION_NAME) 34 | .get('defaultVariablesValues'); 35 | return variables as ISettingDefaultVariablesValues; 36 | }; 37 | 38 | export const getVariableDisplayTitles = (): ISettingVariablesDisplayTitles => { 39 | let variables: ISettingVariablesDisplayTitles | undefined = workspace 40 | .getConfiguration(EXTENSION_NAME) 41 | .get('variablesDisplayTitles'); 42 | return variables as ISettingVariablesDisplayTitles; 43 | }; 44 | -------------------------------------------------------------------------------- /src/utils/storage.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext } from 'vscode'; 2 | import { EXTENSION_NAME } from '../config/const'; 3 | import { gt, valid } from 'semver'; 4 | 5 | const KEY = 'version'; 6 | 7 | // TODO not working when reloading window, needs work 8 | 9 | export const isUpToDate = (context: ExtensionContext, window: any) => { 10 | const currentVersion = context.extension.packageJSON.version; 11 | const storedVersion = 12 | context.globalState.get(`${EXTENSION_NAME}.${KEY}`) ?? '0.0.0'; 13 | 14 | if (valid(storedVersion) && valid(currentVersion)) { 15 | if (gt(currentVersion, storedVersion)) { 16 | if (currentVersion === '3.0.1') { 17 | window.showInformationMessage( 18 | `I'm so sorry, I failed the migration script of your settings in V3.0.0, let me know with issues if you encounter problems to revert it to good (${currentVersion}) (Only impact vscodeGitCommit.variables setting) ` 19 | ); 20 | } 21 | window.showInformationMessage( 22 | `VSCode Git Commit message is up to date 😎! (version: v${currentVersion})` 23 | ); 24 | context.globalState.update(`${EXTENSION_NAME}.${KEY}`, currentVersion); 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/utils/strings.ts: -------------------------------------------------------------------------------- 1 | export const setLengtToX = (length: number) => (value: string) => { 2 | return value.slice(length * -1); 3 | }; 4 | -------------------------------------------------------------------------------- /src/utils/template.ts: -------------------------------------------------------------------------------- 1 | export type TVariable = { 2 | key: string; 3 | value: string; 4 | }; 5 | 6 | export const templateParser = (template: string): string[] => { 7 | const matches = [...template.matchAll(/{\s*[\w\.]+\s*}/g)]; 8 | let variables = matches 9 | .map((e) => e[0].match(/[\w\.]+/g)) 10 | .map((e) => (e ? e[0] : null)) 11 | .filter((e) => e !== null); 12 | // @ts-ignore 13 | return variables; 14 | }; 15 | 16 | export const templateSerialize = ( 17 | template: string, 18 | data: TVariable[] 19 | ): string => { 20 | let newTemplate = template; 21 | for (let i = 0; i < data.length; i++) { 22 | const e = data[i]; 23 | newTemplate = newTemplate.replace(`{${e.key}}`, e.value); 24 | } 25 | return newTemplate.trim(); 26 | }; 27 | -------------------------------------------------------------------------------- /src/utils/variables.ts: -------------------------------------------------------------------------------- 1 | import { dynamicsAggregation, getDynamicPreset } from '../config/dynamics'; 2 | import { getLocalPreset, presetAggregation } from '../config/presets'; 3 | import { Repository } from '../typings/git'; 4 | import { IQuickPickSettings } from '../typings/quickPick'; 5 | import { getVariables } from './settings'; 6 | 7 | const SPREAD_OPERATOR = '...'; 8 | 9 | export const parseVariable = ( 10 | repo: Repository, 11 | name: string 12 | ): IQuickPickSettings[] => { 13 | const variables = getVariables(); 14 | const variable = variables[name]; 15 | 16 | if (Object.keys(presetAggregation).includes(name)) { 17 | const localPreset = getLocalPreset(name as string); 18 | return localPreset; 19 | } else if (Object.keys(dynamicsAggregation).includes(name)) { 20 | const dynamicPreset = getDynamicPreset(repo, name); 21 | return dynamicPreset; 22 | } else if (typeof variable === 'string') { 23 | const presetList = variable.split(SPREAD_OPERATOR); 24 | let resolvedQuickPickItem: IQuickPickSettings[] = []; 25 | for (let i = 0; i < presetList.length; i++) { 26 | const p = presetList[i]; 27 | if (p === name) { 28 | return []; 29 | } else { 30 | const newParsedVariable = parseVariable(repo, p); 31 | resolvedQuickPickItem = [ 32 | ...resolvedQuickPickItem, 33 | ...newParsedVariable, 34 | ]; 35 | } 36 | } 37 | return resolvedQuickPickItem; 38 | } else if (typeof variable !== 'string') { 39 | return variable as IQuickPickSettings[]; 40 | } else { 41 | return []; 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2016", 5 | "outDir": "out", 6 | "lib": ["es6", "es2020.string", "es2020", "DOM"], 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "strict": true 10 | }, 11 | "exclude": ["node_modules", ".vscode-test", "sandbox"] 12 | } 13 | --------------------------------------------------------------------------------