├── .gitattributes ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── definitions.ts ├── extension.ts ├── server.ts ├── test │ ├── extension.test.ts │ └── index.ts └── tidy.ts ├── tsconfig.json ├── tslint.json └── vsc-extension-quickstart.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | /playground/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "eg2.tslint" 6 | ] 7 | } -------------------------------------------------------------------------------- /.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": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "npm: watch" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | out/**/*.map 5 | src/** 6 | .gitignore 7 | tsconfig.json 8 | vsc-extension-quickstart.md 9 | tslint.json 10 | /playground/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [0.0.1] 9 | Add basic diagnostic support by clang-tidy. 10 | 11 | ## [0.0.2] 12 | Support Windows (not tested). 13 | Add code action(quick fixes) support. 14 | Move to the [language server protocol(LSP)](https://microsoft.github.io/language-server-protocol/) structure. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 alesiong 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clang Tidy Linter 2 | 3 | This extension uses [clang-tidy](http://clang.llvm.org/extra/clang-tidy/) to lint C/C++ files. 4 | 5 | **This extension is in a very early stage of development.** You will need to do some configuration before you can use it. But this extension is still kind of "usable". 6 | 7 | ## Features 8 | 9 | Basic diagnostic(linter) and quick fixes for the C/C++ source files with the clang-tidy. 10 | 11 | ## Requirements 12 | 13 | As for now you have to download/compile the clang-tidy executable. Maybe executables will be shipped with this extension in the future. The download link for official prebuilt binaries is . 14 | 15 | ## Extension Settings 16 | 17 | You may need to modify some default settings to get this extension to work for now. 18 | 19 | `clangTidy.executable`: You need to manually set this to your `clang-tidy` executable file. 20 | 21 | `clangTidy.systemIncludePath`: If you use the downloaded prebuilt binaries, it may fail to find some system headers. So put the system include paths here. 22 | 23 | `clangTidy.lintLanguages`: What languages do you want to lint? 24 | 25 | `clangTidy.extraCompilerArgs`: Extra arguments that pass to the **compiler** (not `clang-tidy`). 26 | 27 | `clangTidy.headerFilter`: Value for `-header-filter` command line argument. 28 | 29 | `clangTidy.args`: Additional arguments to pass to `clang-tidy` 30 | 31 | If you want to configure the checks of clang tidy, create a `.clang-tidy` file in your working directory (please refer to the `clang-tidy`'s document for detail). 32 | 33 | ## Known Issues 34 | 35 | Source files can only be lint when you save or open it. 36 | 37 | You have to modify `clangTidy.extraCompilerArgs` setting if your C/C++ project has custom include paths. 38 | 39 | ## Contribution 40 | 41 | Repository: 42 | 43 | I'm a beginner to vscode extension development, so if you have any suggestions, don't hesitate to post issues and/or pull requests. 44 | 45 | ## Road Map (TODOs) 46 | 47 | 1. Refactor `server.ts`, it is now in a very bad structure (with lots of functions and global variables) 48 | 49 | 2. Write tests 50 | 51 | 3. Support for on-the-fly linting (if possible) 52 | 53 | 4. Support for custom include paths through settings and by reading `.vscode/c_cpp_properties.json` 54 | 55 | 5. Ship clang-tidy binaries 56 | 57 | ## Know Issues 58 | 59 | - When using `-p compile_commands.json`, saving a header or opening a header file for the first time can clear existing quick fixes for that file. 60 | - To regenerate the fixes, touch a cpp file which includes that header file. 61 | - `compile_commands.json` can be generated from CMake when `CMAKE_EXPORT_COMPILE_COMMANDS` is on. 62 | 63 | ## Release Notes 64 | 65 | ### 0.0.1 66 | 67 | The very first beta version published to the market. 68 | 69 | ### 0.0.2 70 | 71 | Support Windows (not tested). 72 | Add code actions(quick fixes) support. 73 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clang-tidy-linter", 3 | "version": "0.0.3", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/js-yaml": { 8 | "version": "3.11.1", 9 | "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.11.1.tgz", 10 | "integrity": "sha512-M5qhhfuTt4fwHGqqANNQilp3Htb5cHwBxlMHDUw/TYRVkEp3s3IIFSH3Fe9HIAeEtnO4p3SSowLmCVavdRYfpw==", 11 | "dev": true 12 | }, 13 | "@types/mocha": { 14 | "version": "2.2.48", 15 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", 16 | "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==", 17 | "dev": true 18 | }, 19 | "@types/node": { 20 | "version": "7.0.65", 21 | "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.65.tgz", 22 | "integrity": "sha512-iUdyWWikcQnGvIZnYh5ZxnxeREykndA9+iGdo068NGNutibWknDjmmNMq/8cnS1eaTCcgqJsPsFppw3XJWNlUg==", 23 | "dev": true 24 | }, 25 | "agent-base": { 26 | "version": "4.2.1", 27 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 28 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", 29 | "dev": true, 30 | "requires": { 31 | "es6-promisify": "^5.0.0" 32 | } 33 | }, 34 | "ajv": { 35 | "version": "6.10.0", 36 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", 37 | "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", 38 | "dev": true, 39 | "requires": { 40 | "fast-deep-equal": "^2.0.1", 41 | "fast-json-stable-stringify": "^2.0.0", 42 | "json-schema-traverse": "^0.4.1", 43 | "uri-js": "^4.2.2" 44 | } 45 | }, 46 | "ansi-regex": { 47 | "version": "2.1.1", 48 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 49 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 50 | "dev": true 51 | }, 52 | "ansi-styles": { 53 | "version": "2.2.1", 54 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 55 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 56 | "dev": true 57 | }, 58 | "argparse": { 59 | "version": "1.0.10", 60 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 61 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 62 | "requires": { 63 | "sprintf-js": "~1.0.2" 64 | } 65 | }, 66 | "asn1": { 67 | "version": "0.2.4", 68 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 69 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 70 | "dev": true, 71 | "requires": { 72 | "safer-buffer": "~2.1.0" 73 | } 74 | }, 75 | "assert-plus": { 76 | "version": "1.0.0", 77 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 78 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", 79 | "dev": true 80 | }, 81 | "asynckit": { 82 | "version": "0.4.0", 83 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 84 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 85 | "dev": true 86 | }, 87 | "aws-sign2": { 88 | "version": "0.7.0", 89 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 90 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", 91 | "dev": true 92 | }, 93 | "aws4": { 94 | "version": "1.8.0", 95 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 96 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", 97 | "dev": true 98 | }, 99 | "babel-code-frame": { 100 | "version": "6.26.0", 101 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 102 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 103 | "dev": true, 104 | "requires": { 105 | "chalk": "^1.1.3", 106 | "esutils": "^2.0.2", 107 | "js-tokens": "^3.0.2" 108 | }, 109 | "dependencies": { 110 | "chalk": { 111 | "version": "1.1.3", 112 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 113 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 114 | "dev": true, 115 | "requires": { 116 | "ansi-styles": "^2.2.1", 117 | "escape-string-regexp": "^1.0.2", 118 | "has-ansi": "^2.0.0", 119 | "strip-ansi": "^3.0.0", 120 | "supports-color": "^2.0.0" 121 | } 122 | } 123 | } 124 | }, 125 | "balanced-match": { 126 | "version": "1.0.0", 127 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 128 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 129 | "dev": true 130 | }, 131 | "bcrypt-pbkdf": { 132 | "version": "1.0.2", 133 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 134 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 135 | "dev": true, 136 | "requires": { 137 | "tweetnacl": "^0.14.3" 138 | } 139 | }, 140 | "brace-expansion": { 141 | "version": "1.1.11", 142 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 143 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 144 | "dev": true, 145 | "requires": { 146 | "balanced-match": "^1.0.0", 147 | "concat-map": "0.0.1" 148 | } 149 | }, 150 | "browser-stdout": { 151 | "version": "1.3.0", 152 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 153 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 154 | "dev": true 155 | }, 156 | "buffer-from": { 157 | "version": "1.1.1", 158 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 159 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 160 | "dev": true 161 | }, 162 | "builtin-modules": { 163 | "version": "1.1.1", 164 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 165 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 166 | "dev": true 167 | }, 168 | "caseless": { 169 | "version": "0.12.0", 170 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 171 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", 172 | "dev": true 173 | }, 174 | "chalk": { 175 | "version": "2.4.1", 176 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 177 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 178 | "dev": true, 179 | "requires": { 180 | "ansi-styles": "^3.2.1", 181 | "escape-string-regexp": "^1.0.5", 182 | "supports-color": "^5.3.0" 183 | }, 184 | "dependencies": { 185 | "ansi-styles": { 186 | "version": "3.2.1", 187 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 188 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 189 | "dev": true, 190 | "requires": { 191 | "color-convert": "^1.9.0" 192 | } 193 | }, 194 | "supports-color": { 195 | "version": "5.4.0", 196 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 197 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 198 | "dev": true, 199 | "requires": { 200 | "has-flag": "^3.0.0" 201 | } 202 | } 203 | } 204 | }, 205 | "color-convert": { 206 | "version": "1.9.2", 207 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", 208 | "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", 209 | "dev": true, 210 | "requires": { 211 | "color-name": "1.1.1" 212 | } 213 | }, 214 | "color-name": { 215 | "version": "1.1.1", 216 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", 217 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", 218 | "dev": true 219 | }, 220 | "combined-stream": { 221 | "version": "1.0.7", 222 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 223 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 224 | "dev": true, 225 | "requires": { 226 | "delayed-stream": "~1.0.0" 227 | } 228 | }, 229 | "commander": { 230 | "version": "2.15.1", 231 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 232 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 233 | "dev": true 234 | }, 235 | "concat-map": { 236 | "version": "0.0.1", 237 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 238 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 239 | "dev": true 240 | }, 241 | "core-util-is": { 242 | "version": "1.0.2", 243 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 244 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 245 | "dev": true 246 | }, 247 | "dashdash": { 248 | "version": "1.14.1", 249 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 250 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 251 | "dev": true, 252 | "requires": { 253 | "assert-plus": "^1.0.0" 254 | } 255 | }, 256 | "debug": { 257 | "version": "3.1.0", 258 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 259 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 260 | "dev": true, 261 | "requires": { 262 | "ms": "2.0.0" 263 | } 264 | }, 265 | "delayed-stream": { 266 | "version": "1.0.0", 267 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 268 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 269 | "dev": true 270 | }, 271 | "diff": { 272 | "version": "3.5.0", 273 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 274 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 275 | "dev": true 276 | }, 277 | "ecc-jsbn": { 278 | "version": "0.1.2", 279 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 280 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 281 | "dev": true, 282 | "requires": { 283 | "jsbn": "~0.1.0", 284 | "safer-buffer": "^2.1.0" 285 | } 286 | }, 287 | "es6-promise": { 288 | "version": "4.2.6", 289 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", 290 | "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", 291 | "dev": true 292 | }, 293 | "es6-promisify": { 294 | "version": "5.0.0", 295 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 296 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 297 | "dev": true, 298 | "requires": { 299 | "es6-promise": "^4.0.3" 300 | } 301 | }, 302 | "escape-string-regexp": { 303 | "version": "1.0.5", 304 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 305 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 306 | "dev": true 307 | }, 308 | "esprima": { 309 | "version": "4.0.1", 310 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 311 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 312 | }, 313 | "esutils": { 314 | "version": "2.0.2", 315 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 316 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 317 | "dev": true 318 | }, 319 | "extend": { 320 | "version": "3.0.2", 321 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 322 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 323 | "dev": true 324 | }, 325 | "extsprintf": { 326 | "version": "1.3.0", 327 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 328 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", 329 | "dev": true 330 | }, 331 | "fast-deep-equal": { 332 | "version": "2.0.1", 333 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 334 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 335 | "dev": true 336 | }, 337 | "fast-json-stable-stringify": { 338 | "version": "2.0.0", 339 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 340 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 341 | "dev": true 342 | }, 343 | "forever-agent": { 344 | "version": "0.6.1", 345 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 346 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", 347 | "dev": true 348 | }, 349 | "form-data": { 350 | "version": "2.3.3", 351 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 352 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 353 | "dev": true, 354 | "requires": { 355 | "asynckit": "^0.4.0", 356 | "combined-stream": "^1.0.6", 357 | "mime-types": "^2.1.12" 358 | } 359 | }, 360 | "fs.realpath": { 361 | "version": "1.0.0", 362 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 363 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 364 | "dev": true 365 | }, 366 | "getpass": { 367 | "version": "0.1.7", 368 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 369 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 370 | "dev": true, 371 | "requires": { 372 | "assert-plus": "^1.0.0" 373 | } 374 | }, 375 | "glob": { 376 | "version": "7.1.2", 377 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 378 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 379 | "dev": true, 380 | "requires": { 381 | "fs.realpath": "^1.0.0", 382 | "inflight": "^1.0.4", 383 | "inherits": "2", 384 | "minimatch": "^3.0.4", 385 | "once": "^1.3.0", 386 | "path-is-absolute": "^1.0.0" 387 | } 388 | }, 389 | "growl": { 390 | "version": "1.10.3", 391 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", 392 | "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", 393 | "dev": true 394 | }, 395 | "har-schema": { 396 | "version": "2.0.0", 397 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 398 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", 399 | "dev": true 400 | }, 401 | "har-validator": { 402 | "version": "5.1.3", 403 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 404 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 405 | "dev": true, 406 | "requires": { 407 | "ajv": "^6.5.5", 408 | "har-schema": "^2.0.0" 409 | } 410 | }, 411 | "has-ansi": { 412 | "version": "2.0.0", 413 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 414 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 415 | "dev": true, 416 | "requires": { 417 | "ansi-regex": "^2.0.0" 418 | } 419 | }, 420 | "has-flag": { 421 | "version": "3.0.0", 422 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 423 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 424 | "dev": true 425 | }, 426 | "he": { 427 | "version": "1.1.1", 428 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 429 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 430 | "dev": true 431 | }, 432 | "http-proxy-agent": { 433 | "version": "2.1.0", 434 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", 435 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", 436 | "dev": true, 437 | "requires": { 438 | "agent-base": "4", 439 | "debug": "3.1.0" 440 | } 441 | }, 442 | "http-signature": { 443 | "version": "1.2.0", 444 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 445 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 446 | "dev": true, 447 | "requires": { 448 | "assert-plus": "^1.0.0", 449 | "jsprim": "^1.2.2", 450 | "sshpk": "^1.7.0" 451 | } 452 | }, 453 | "https-proxy-agent": { 454 | "version": "2.2.1", 455 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", 456 | "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", 457 | "dev": true, 458 | "requires": { 459 | "agent-base": "^4.1.0", 460 | "debug": "^3.1.0" 461 | } 462 | }, 463 | "inflight": { 464 | "version": "1.0.6", 465 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 466 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 467 | "dev": true, 468 | "requires": { 469 | "once": "^1.3.0", 470 | "wrappy": "1" 471 | } 472 | }, 473 | "inherits": { 474 | "version": "2.0.3", 475 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 476 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 477 | "dev": true 478 | }, 479 | "is-typedarray": { 480 | "version": "1.0.0", 481 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 482 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 483 | "dev": true 484 | }, 485 | "isstream": { 486 | "version": "0.1.2", 487 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 488 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 489 | "dev": true 490 | }, 491 | "js-tokens": { 492 | "version": "3.0.2", 493 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 494 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 495 | "dev": true 496 | }, 497 | "js-yaml": { 498 | "version": "3.13.1", 499 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 500 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 501 | "requires": { 502 | "argparse": "^1.0.7", 503 | "esprima": "^4.0.0" 504 | } 505 | }, 506 | "jsbn": { 507 | "version": "0.1.1", 508 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 509 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 510 | "dev": true 511 | }, 512 | "json-schema": { 513 | "version": "0.2.3", 514 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 515 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", 516 | "dev": true 517 | }, 518 | "json-schema-traverse": { 519 | "version": "0.4.1", 520 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 521 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 522 | "dev": true 523 | }, 524 | "json-stringify-safe": { 525 | "version": "5.0.1", 526 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 527 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 528 | "dev": true 529 | }, 530 | "jsprim": { 531 | "version": "1.4.1", 532 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 533 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 534 | "dev": true, 535 | "requires": { 536 | "assert-plus": "1.0.0", 537 | "extsprintf": "1.3.0", 538 | "json-schema": "0.2.3", 539 | "verror": "1.10.0" 540 | } 541 | }, 542 | "mime-db": { 543 | "version": "1.40.0", 544 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 545 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", 546 | "dev": true 547 | }, 548 | "mime-types": { 549 | "version": "2.1.24", 550 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 551 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 552 | "dev": true, 553 | "requires": { 554 | "mime-db": "1.40.0" 555 | } 556 | }, 557 | "minimatch": { 558 | "version": "3.0.4", 559 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 560 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 561 | "dev": true, 562 | "requires": { 563 | "brace-expansion": "^1.1.7" 564 | } 565 | }, 566 | "minimist": { 567 | "version": "0.0.8", 568 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 569 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 570 | "dev": true 571 | }, 572 | "mkdirp": { 573 | "version": "0.5.1", 574 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 575 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 576 | "dev": true, 577 | "requires": { 578 | "minimist": "0.0.8" 579 | } 580 | }, 581 | "mocha": { 582 | "version": "4.1.0", 583 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", 584 | "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", 585 | "dev": true, 586 | "requires": { 587 | "browser-stdout": "1.3.0", 588 | "commander": "2.11.0", 589 | "debug": "3.1.0", 590 | "diff": "3.3.1", 591 | "escape-string-regexp": "1.0.5", 592 | "glob": "7.1.2", 593 | "growl": "1.10.3", 594 | "he": "1.1.1", 595 | "mkdirp": "0.5.1", 596 | "supports-color": "4.4.0" 597 | }, 598 | "dependencies": { 599 | "commander": { 600 | "version": "2.11.0", 601 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 602 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 603 | "dev": true 604 | }, 605 | "diff": { 606 | "version": "3.3.1", 607 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", 608 | "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", 609 | "dev": true 610 | }, 611 | "has-flag": { 612 | "version": "2.0.0", 613 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 614 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 615 | "dev": true 616 | }, 617 | "supports-color": { 618 | "version": "4.4.0", 619 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", 620 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", 621 | "dev": true, 622 | "requires": { 623 | "has-flag": "^2.0.0" 624 | } 625 | } 626 | } 627 | }, 628 | "ms": { 629 | "version": "2.0.0", 630 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 631 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 632 | "dev": true 633 | }, 634 | "oauth-sign": { 635 | "version": "0.9.0", 636 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 637 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", 638 | "dev": true 639 | }, 640 | "once": { 641 | "version": "1.4.0", 642 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 643 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 644 | "dev": true, 645 | "requires": { 646 | "wrappy": "1" 647 | } 648 | }, 649 | "path-is-absolute": { 650 | "version": "1.0.1", 651 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 652 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 653 | "dev": true 654 | }, 655 | "path-parse": { 656 | "version": "1.0.5", 657 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 658 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", 659 | "dev": true 660 | }, 661 | "performance-now": { 662 | "version": "2.1.0", 663 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 664 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 665 | "dev": true 666 | }, 667 | "psl": { 668 | "version": "1.1.31", 669 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", 670 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", 671 | "dev": true 672 | }, 673 | "punycode": { 674 | "version": "2.1.1", 675 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 676 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 677 | "dev": true 678 | }, 679 | "qs": { 680 | "version": "6.5.2", 681 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 682 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", 683 | "dev": true 684 | }, 685 | "querystringify": { 686 | "version": "2.1.0", 687 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", 688 | "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==" 689 | }, 690 | "request": { 691 | "version": "2.88.0", 692 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 693 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 694 | "dev": true, 695 | "requires": { 696 | "aws-sign2": "~0.7.0", 697 | "aws4": "^1.8.0", 698 | "caseless": "~0.12.0", 699 | "combined-stream": "~1.0.6", 700 | "extend": "~3.0.2", 701 | "forever-agent": "~0.6.1", 702 | "form-data": "~2.3.2", 703 | "har-validator": "~5.1.0", 704 | "http-signature": "~1.2.0", 705 | "is-typedarray": "~1.0.0", 706 | "isstream": "~0.1.2", 707 | "json-stringify-safe": "~5.0.1", 708 | "mime-types": "~2.1.19", 709 | "oauth-sign": "~0.9.0", 710 | "performance-now": "^2.1.0", 711 | "qs": "~6.5.2", 712 | "safe-buffer": "^5.1.2", 713 | "tough-cookie": "~2.4.3", 714 | "tunnel-agent": "^0.6.0", 715 | "uuid": "^3.3.2" 716 | } 717 | }, 718 | "requires-port": { 719 | "version": "1.0.0", 720 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 721 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 722 | }, 723 | "resolve": { 724 | "version": "1.7.1", 725 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", 726 | "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", 727 | "dev": true, 728 | "requires": { 729 | "path-parse": "^1.0.5" 730 | } 731 | }, 732 | "safe-buffer": { 733 | "version": "5.1.2", 734 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 735 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 736 | "dev": true 737 | }, 738 | "safer-buffer": { 739 | "version": "2.1.2", 740 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 741 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 742 | "dev": true 743 | }, 744 | "semver": { 745 | "version": "5.5.0", 746 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 747 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", 748 | "dev": true 749 | }, 750 | "source-map": { 751 | "version": "0.6.1", 752 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 753 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 754 | "dev": true 755 | }, 756 | "source-map-support": { 757 | "version": "0.5.12", 758 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", 759 | "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", 760 | "dev": true, 761 | "requires": { 762 | "buffer-from": "^1.0.0", 763 | "source-map": "^0.6.0" 764 | } 765 | }, 766 | "sprintf-js": { 767 | "version": "1.0.3", 768 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 769 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 770 | }, 771 | "sshpk": { 772 | "version": "1.16.1", 773 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 774 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 775 | "dev": true, 776 | "requires": { 777 | "asn1": "~0.2.3", 778 | "assert-plus": "^1.0.0", 779 | "bcrypt-pbkdf": "^1.0.0", 780 | "dashdash": "^1.12.0", 781 | "ecc-jsbn": "~0.1.1", 782 | "getpass": "^0.1.1", 783 | "jsbn": "~0.1.0", 784 | "safer-buffer": "^2.0.2", 785 | "tweetnacl": "~0.14.0" 786 | } 787 | }, 788 | "strip-ansi": { 789 | "version": "3.0.1", 790 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 791 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 792 | "dev": true, 793 | "requires": { 794 | "ansi-regex": "^2.0.0" 795 | } 796 | }, 797 | "supports-color": { 798 | "version": "2.0.0", 799 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 800 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 801 | "dev": true 802 | }, 803 | "tough-cookie": { 804 | "version": "2.4.3", 805 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 806 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 807 | "dev": true, 808 | "requires": { 809 | "psl": "^1.1.24", 810 | "punycode": "^1.4.1" 811 | }, 812 | "dependencies": { 813 | "punycode": { 814 | "version": "1.4.1", 815 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 816 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 817 | "dev": true 818 | } 819 | } 820 | }, 821 | "tslib": { 822 | "version": "1.9.2", 823 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", 824 | "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==", 825 | "dev": true 826 | }, 827 | "tslint": { 828 | "version": "5.10.0", 829 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", 830 | "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", 831 | "dev": true, 832 | "requires": { 833 | "babel-code-frame": "^6.22.0", 834 | "builtin-modules": "^1.1.1", 835 | "chalk": "^2.3.0", 836 | "commander": "^2.12.1", 837 | "diff": "^3.2.0", 838 | "glob": "^7.1.1", 839 | "js-yaml": "^3.7.0", 840 | "minimatch": "^3.0.4", 841 | "resolve": "^1.3.2", 842 | "semver": "^5.3.0", 843 | "tslib": "^1.8.0", 844 | "tsutils": "^2.12.1" 845 | } 846 | }, 847 | "tsutils": { 848 | "version": "2.27.1", 849 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", 850 | "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", 851 | "dev": true, 852 | "requires": { 853 | "tslib": "^1.8.1" 854 | } 855 | }, 856 | "tunnel-agent": { 857 | "version": "0.6.0", 858 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 859 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 860 | "dev": true, 861 | "requires": { 862 | "safe-buffer": "^5.0.1" 863 | } 864 | }, 865 | "tweetnacl": { 866 | "version": "0.14.5", 867 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 868 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 869 | "dev": true 870 | }, 871 | "typescript": { 872 | "version": "3.2.2", 873 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", 874 | "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", 875 | "dev": true 876 | }, 877 | "uri-js": { 878 | "version": "4.2.2", 879 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 880 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 881 | "dev": true, 882 | "requires": { 883 | "punycode": "^2.1.0" 884 | } 885 | }, 886 | "url-parse": { 887 | "version": "1.4.4", 888 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", 889 | "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", 890 | "requires": { 891 | "querystringify": "^2.0.0", 892 | "requires-port": "^1.0.0" 893 | } 894 | }, 895 | "uuid": { 896 | "version": "3.3.2", 897 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 898 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", 899 | "dev": true 900 | }, 901 | "verror": { 902 | "version": "1.10.0", 903 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 904 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 905 | "dev": true, 906 | "requires": { 907 | "assert-plus": "^1.0.0", 908 | "core-util-is": "1.0.2", 909 | "extsprintf": "^1.2.0" 910 | } 911 | }, 912 | "vscode": { 913 | "version": "1.1.33", 914 | "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.33.tgz", 915 | "integrity": "sha512-sXedp2oF6y4ZvqrrFiZpeMzaCLSWV+PpYkIxjG/iYquNZ9KrLL2LujltGxPLvzn49xu2sZkyC+avVNFgcJD1Iw==", 916 | "dev": true, 917 | "requires": { 918 | "glob": "^7.1.2", 919 | "mocha": "^4.0.1", 920 | "request": "^2.88.0", 921 | "semver": "^5.4.1", 922 | "source-map-support": "^0.5.0", 923 | "url-parse": "^1.4.4", 924 | "vscode-test": "^0.1.4" 925 | } 926 | }, 927 | "vscode-jsonrpc": { 928 | "version": "3.6.2", 929 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz", 930 | "integrity": "sha512-T24Jb5V48e4VgYliUXMnZ379ItbrXgOimweKaJshD84z+8q7ZOZjJan0MeDe+Ugb+uqERDVV8SBmemaGMSMugA==" 931 | }, 932 | "vscode-languageclient": { 933 | "version": "4.2.1", 934 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-4.2.1.tgz", 935 | "integrity": "sha512-zeixIe2MiKPHiSNjEUmRhWFiNCGUwUNvefBiA9diZc7fXE8DX+AhfwpsOLYauO8Q8C6gW8f9OQvy3Vn2hBvY4g==", 936 | "requires": { 937 | "vscode-languageserver-protocol": "^3.8.1" 938 | } 939 | }, 940 | "vscode-languageserver": { 941 | "version": "4.2.1", 942 | "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-4.2.1.tgz", 943 | "integrity": "sha512-5WAxaK1nEpe52ZaWNMqmd6rO5CIE72J/7UkGKPUTdGa0l0haWHS69tpRz+LetBlgTpP7PYacl4xhDaLZv82a+g==", 944 | "requires": { 945 | "vscode-languageserver-protocol": "^3.8.1", 946 | "vscode-uri": "^1.0.3" 947 | } 948 | }, 949 | "vscode-languageserver-protocol": { 950 | "version": "3.8.1", 951 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.8.1.tgz", 952 | "integrity": "sha512-KdeetvQ2JavRiuE9afNrV5+xJZocj7NGPQwH4kpSFw5cp+0wijv87qgXfSEvmwPUaknhMBoSuSrSIG/LRrzWJQ==", 953 | "requires": { 954 | "vscode-jsonrpc": "^3.6.2", 955 | "vscode-languageserver-types": "^3.8.1" 956 | } 957 | }, 958 | "vscode-languageserver-types": { 959 | "version": "3.8.2", 960 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.8.2.tgz", 961 | "integrity": "sha512-2RMkyt1O1czGPCnkjKZWSio2D8oh3XlQ4zi4W2xL8q2Dvi4hB3/DEt+wYyzo4hmE2ZFP0RB8PXyzHyed7p1hbw==" 962 | }, 963 | "vscode-test": { 964 | "version": "0.1.5", 965 | "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-0.1.5.tgz", 966 | "integrity": "sha512-s+lbF1Dtasc0yXVB9iQTexBe2JK6HJAUJe3fWezHKIjq+xRw5ZwCMEMBaonFIPy7s95qg2HPTRDR5W4h4kbxGw==", 967 | "dev": true, 968 | "requires": { 969 | "http-proxy-agent": "^2.1.0", 970 | "https-proxy-agent": "^2.2.1" 971 | } 972 | }, 973 | "vscode-uri": { 974 | "version": "1.0.5", 975 | "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.5.tgz", 976 | "integrity": "sha1-O4majvccN/MFTXm9vdoxx7828g0=" 977 | }, 978 | "wrappy": { 979 | "version": "1.0.2", 980 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 981 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 982 | "dev": true 983 | } 984 | } 985 | } 986 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clang-tidy-linter", 3 | "displayName": "Clang-tidy Linter", 4 | "description": "A Visual Studio Code extension to lint code by clang-tidy.", 5 | "version": "0.0.3", 6 | "publisher": "alesiong", 7 | "engines": { 8 | "vscode": "^1.24.0" 9 | }, 10 | "homepage": "https://github.com/alesiong/clang-tidy-linter/blob/master/README.md", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/alesiong/clang-tidy-linter" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/alesiong/clang-tidy-linter/issues" 17 | }, 18 | "license": "SEE LICENSE IN LICENSE", 19 | "categories": [ 20 | "Linters" 21 | ], 22 | "keywords": [ 23 | "clang-tidy", 24 | "C", 25 | "C++", 26 | "linter" 27 | ], 28 | "preview": true, 29 | "main": "./out/extension", 30 | "activationEvents": [ 31 | "onLanguage:cpp", 32 | "onLanguage:c" 33 | ], 34 | "contributes": { 35 | "configuration": { 36 | "type": "object", 37 | "title": "Clang-tidy linter configuration", 38 | "properties": { 39 | "clangTidy.executable": { 40 | "type": "string", 41 | "default": "clang-tidy", 42 | "description": "Path to the clang-tidy executable" 43 | }, 44 | "clangTidy.systemIncludePath": { 45 | "type": "array", 46 | "items": { 47 | "type": "string" 48 | }, 49 | "default": [], 50 | "description": "Extra system include paths to pass to the compiler (-extra-arg=-isystem). Use `echo |clang -x c++ -v -fsyntax-only -` to check your default include path" 51 | }, 52 | "clangTidy.lintLanguages": { 53 | "type": "array", 54 | "items": { 55 | "type": "string" 56 | }, 57 | "default": [ 58 | "c", 59 | "cpp" 60 | ], 61 | "description": "Language type to enable clang-tidy linter" 62 | }, 63 | "clangTidy.extraCompilerArgs": { 64 | "type": "array", 65 | "items": { 66 | "type": "string" 67 | }, 68 | "default": [ 69 | "-Weverything" 70 | ], 71 | "description": "Extra arguments pass to compiler (-extra-arg=)" 72 | }, 73 | "clangTidy.headerFilter": { 74 | "type": "string", 75 | "default": ".*", 76 | "description": "Value for clang-tidy '-header-filter' command line argument" 77 | }, 78 | "clangTidy.args": { 79 | "type": "array", 80 | "items": { 81 | "type": "string" 82 | }, 83 | "default": [], 84 | "description": "Additional arguments to pass to clang-tidy" 85 | } 86 | } 87 | } 88 | }, 89 | "scripts": { 90 | "vscode:prepublish": "npm run compile", 91 | "compile": "tsc -p ./", 92 | "watch": "tsc -watch -p ./", 93 | "postinstall": "node ./node_modules/vscode/bin/install", 94 | "test": "npm run compile && node ./node_modules/vscode/bin/test" 95 | }, 96 | "devDependencies": { 97 | "@types/js-yaml": "^3.11.1", 98 | "@types/mocha": "^2.2.42", 99 | "@types/node": "^7.0.43", 100 | "tslint": "^5.8.0", 101 | "typescript": "^3.2.2", 102 | "vscode": "^1.1.33" 103 | }, 104 | "dependencies": { 105 | "js-yaml": "^3.13.1", 106 | "url-parse": ">=1.4.3", 107 | "vscode-languageclient": "^4.2.1", 108 | "vscode-languageserver": "^4.2.1", 109 | "vscode-uri": "^1.0.5" 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/definitions.ts: -------------------------------------------------------------------------------- 1 | interface Configuration { 2 | executable: string; 3 | systemIncludePath: string[]; 4 | lintLanguages: string[]; 5 | extraCompilerArgs: string[]; 6 | headerFilter: string; 7 | args: string[]; 8 | } 9 | 10 | interface ClangTidyResult { 11 | MainSourceFile: string; 12 | Diagnostics: ClangTidyDiagnostic[]; 13 | } 14 | 15 | interface Position { 16 | line: number; 17 | character: number; 18 | } 19 | 20 | interface Range { 21 | start: Position; 22 | end: Position; 23 | } 24 | 25 | interface ClangTidyDiagnostic { 26 | DiagnosticName: string; 27 | Message: string; 28 | FileOffset: number; 29 | FilePath: string; 30 | Replacements?: ClangTidyReplacement[]; 31 | Range?: Range; // Offset and length translated into line character 32 | } 33 | 34 | interface ClangTidyReplacement { 35 | FilePath: string; 36 | Offset: number; 37 | Length: number; 38 | ReplacementText: string; 39 | Range?: Range; // Offset and length translated into line character 40 | } 41 | 42 | interface CppToolsConfigs { 43 | cppToolsIncludePaths: string[]; 44 | cStandard: string; 45 | cppStandard: string; 46 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as path from 'path'; 4 | import { ExtensionContext, workspace } from 'vscode'; 5 | import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } 6 | from 'vscode-languageclient'; 7 | 8 | let client: LanguageClient; 9 | 10 | export function activate(context: ExtensionContext) { 11 | const serverModule = context.asAbsolutePath(path.join('out', 'server.js')); 12 | const debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; 13 | 14 | const serverOptions: ServerOptions = { 15 | run: { module: serverModule, transport: TransportKind.ipc }, 16 | debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } 17 | }; 18 | 19 | const clientOptions: LanguageClientOptions = { 20 | documentSelector: [ 21 | { scheme: 'file', language: 'cpp' }, 22 | { scheme: 'file', language: 'c'}, 23 | ], 24 | synchronize: { 25 | fileEvents: workspace.createFileSystemWatcher('**/*'), 26 | } 27 | }; 28 | 29 | // Create the language client and start the client. 30 | client = new LanguageClient('clangTidy', 'Clang Tidy Linter Client', 31 | serverOptions, clientOptions); 32 | 33 | console.log('start'); 34 | client.start(); 35 | } 36 | 37 | 38 | export function deactivate() { 39 | } -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { 4 | createConnection, TextDocuments, TextDocument, Diagnostic, 5 | ProposedFeatures, InitializeParams, DidChangeConfigurationNotification, 6 | CodeActionParams, CodeAction, CodeActionKind, TextEdit, PublishDiagnosticsParams, 7 | } from 'vscode-languageserver'; 8 | import Uri from 'vscode-uri'; 9 | import { generateDiagnostics } from './tidy'; 10 | import * as path from 'path'; 11 | import * as fs from 'fs'; 12 | 13 | const connection = createConnection(ProposedFeatures.all); 14 | const documents = new TextDocuments(); 15 | // Keyed on files that raised diagnostics, but were not the mail file; i.e., header files. 16 | // Used to recompile header files as they won't generate diagnostics when using a build database (-p option). 17 | const fileAssociationMap: { [id: string]: string } = {}; 18 | 19 | let hasConfigurationCapability: boolean = false; 20 | let hasWorkspaceFolderCapability: boolean = false; 21 | // let hasDiagnosticRelatedInformationCapability: boolean = false; 22 | 23 | const defaultConfig: Configuration = { 24 | executable: 'clang-tidy', 25 | systemIncludePath: [], 26 | lintLanguages: ["c", "cpp"], 27 | extraCompilerArgs: ["-Weverything"], 28 | headerFilter: ".*", 29 | args: [] 30 | }; 31 | 32 | let globalConfig = defaultConfig; 33 | 34 | const documentConfig: Map> = new Map(); 35 | 36 | connection.onInitialize((params: InitializeParams) => { 37 | const capabilities = params.capabilities; 38 | 39 | hasConfigurationCapability = !!(capabilities.workspace && !!capabilities.workspace.configuration); 40 | hasWorkspaceFolderCapability = !!(capabilities.workspace && !!capabilities.workspace.workspaceFolders); 41 | // hasDiagnosticRelatedInformationCapability = !!(capabilities.textDocument && 42 | // capabilities.textDocument.publishDiagnostics && 43 | // capabilities.textDocument.publishDiagnostics.relatedInformation); 44 | return { 45 | capabilities: { 46 | textDocumentSync: documents.syncKind, 47 | codeActionProvider: true 48 | } 49 | }; 50 | }); 51 | 52 | connection.onInitialized(() => { 53 | if (hasConfigurationCapability) { 54 | connection.client.register(DidChangeConfigurationNotification.type, undefined); 55 | } 56 | if (hasWorkspaceFolderCapability) { 57 | connection.workspace.onDidChangeWorkspaceFolders((_event) => { 58 | connection.console.log('Workspace folder change event received.'); 59 | }); 60 | } 61 | }); 62 | 63 | connection.onDidChangeConfiguration(change => { 64 | if (hasConfigurationCapability) { 65 | documentConfig.clear(); 66 | } else { 67 | globalConfig = change.settings.clangTidy || defaultConfig; 68 | } 69 | documents.all().forEach(validateTextDocument); 70 | }); 71 | 72 | connection.onCodeAction(provideCodeActions); 73 | 74 | documents.onDidClose(e => { 75 | documentConfig.delete(e.document.uri); 76 | }); 77 | 78 | documents.onDidSave(file => { 79 | validateTextDocument(file.document); 80 | }); 81 | 82 | documents.onDidOpen(file => { 83 | validateTextDocument(file.document); 84 | }); 85 | 86 | 87 | function getAlternativeDoc(filePath: string, languageId: string): TextDocument | undefined { 88 | // Try associated file map. 89 | if (filePath in fileAssociationMap) { 90 | const aliasDocPath = fileAssociationMap[filePath]; 91 | if (fs.existsSync(aliasDocPath)) { 92 | const referenceDoc = TextDocument.create("file://" + aliasDocPath, 93 | languageId, 0, 94 | fs.readFileSync(aliasDocPath).toString()); 95 | return referenceDoc; 96 | } 97 | } 98 | 99 | // Assume it's a header file and look for a matching source file in the same directory. 100 | // It would be better to query VSCode for this as it has better overall visibility of matching source to header. 101 | const basePath = path.parse(Uri.parse(filePath).fsPath); 102 | const tryExtensions = ["c", "cpp", "cxx"]; 103 | 104 | for (const ext of tryExtensions) { 105 | const tryPath = path.join(basePath.dir, basePath.name + "." + ext); 106 | // console.warn("Try: " + tryPath); 107 | if (fs.existsSync(tryPath)) { 108 | const referenceDoc = TextDocument.create("file://" + tryPath, 109 | languageId, 0, 110 | fs.readFileSync(tryPath).toString()); 111 | return referenceDoc; 112 | } 113 | } 114 | 115 | return undefined; 116 | } 117 | 118 | // Get config by document url (resource) 119 | function getDocumentConfig(resource: string): Thenable { 120 | if (!hasConfigurationCapability) { 121 | return Promise.resolve(globalConfig); 122 | } 123 | let result = documentConfig.get(resource); 124 | if (!result) { 125 | result = connection.workspace.getConfiguration({ 126 | scopeUri: resource, 127 | section: 'clangTidy' 128 | }); 129 | documentConfig.set(resource, result); 130 | } 131 | return result; 132 | } 133 | 134 | async function validateTextDocument(textDocument: TextDocument): Promise { 135 | const workspaceFolders = await connection.workspace.getWorkspaceFolders(); 136 | const configuration = await getDocumentConfig(textDocument.uri); 137 | const lintLanguages = new Set(configuration.lintLanguages); 138 | 139 | 140 | if (!lintLanguages.has(textDocument.languageId)) { 141 | return; 142 | } 143 | 144 | 145 | const folders = workspaceFolders ? workspaceFolders : []; 146 | 147 | let allowRecursion: boolean = true; 148 | const processResults = (doc: TextDocument, diagnostics: { [id: string]: Diagnostic[] }, 149 | diagnosticsCount: number) => { 150 | const mainFilePath: string = Uri.parse(textDocument.uri).fsPath; 151 | let sentDiagnostics: boolean = false; 152 | 153 | if (diagnosticsCount === 0 && allowRecursion) { 154 | // No diagnostics. Could be because of the build database issue. 155 | // Recurse on the best alternative file. 156 | allowRecursion = false; 157 | const referenceDoc = getAlternativeDoc(mainFilePath, textDocument.languageId); 158 | // console.warn("No diagnostics for " + mainFilePath); 159 | // console.warn("Alternative: " + (referenceDoc ? referenceDoc.uri : "")); 160 | if (referenceDoc) { 161 | sentDiagnostics = true; 162 | generateDiagnostics(connection, referenceDoc, configuration, folders, processResults); 163 | } 164 | } 165 | 166 | if (!sentDiagnostics) { 167 | for (const filePath in diagnostics) { 168 | const diagnosticsParam: PublishDiagnosticsParams = { 169 | uri: "file://" + filePath, 170 | diagnostics: diagnostics[filePath] 171 | }; 172 | // Cache associated files. 173 | if (filePath !== mainFilePath) { 174 | fileAssociationMap[filePath] = mainFilePath; 175 | } 176 | connection.sendDiagnostics(diagnosticsParam); 177 | 178 | // if mainFilePath != textDocument.uri and textDocument.uri not i diagnostics, send empty 179 | // for textDocument.uri 180 | if (doc.uri !== textDocument.uri) { 181 | if (!(Uri.parse(textDocument.uri).fsPath in diagnostics)) { 182 | const diagnosticsParam: PublishDiagnosticsParams = { 183 | uri: textDocument.uri, 184 | diagnostics: [] 185 | }; 186 | connection.sendDiagnostics(diagnosticsParam); 187 | } 188 | } 189 | } 190 | } 191 | }; 192 | 193 | 194 | generateDiagnostics(connection, textDocument, configuration, folders, processResults); 195 | } 196 | 197 | async function provideCodeActions(params: CodeActionParams): Promise { 198 | const diagnostics: Diagnostic[] = params.context.diagnostics; 199 | const actions: CodeAction[] = []; 200 | diagnostics 201 | .filter(d => d.source === 'Clang Tidy') 202 | .forEach(d => { 203 | // console.warn("d.code: " + d.code); 204 | if (d.code && typeof d.code === 'string') { 205 | const replacements = JSON.parse(d.code) as ClangTidyReplacement[]; 206 | 207 | const changes: { [uri: string]: TextEdit[]; } = {}; 208 | for (const replacement of replacements) { 209 | // Only add replacement if we have a range. We should do. 210 | if (replacement.Range) { 211 | // console.warn("replacement: " + replacement.Range); 212 | if (!(params.textDocument.uri in changes)) { 213 | changes[params.textDocument.uri] = []; 214 | } 215 | 216 | changes[params.textDocument.uri].push({ 217 | range: replacement.Range, 218 | newText: replacement.ReplacementText 219 | }); 220 | } 221 | } 222 | 223 | actions.push({ 224 | title: '[Clang Tidy] Change to ' + replacements[0].ReplacementText, 225 | diagnostics: [d], 226 | kind: CodeActionKind.QuickFix, 227 | edit: { 228 | changes 229 | } 230 | }); 231 | 232 | } else { 233 | actions.push({ 234 | title: 'Apply clang-tidy fix [NYI]', 235 | diagnostics: [d], 236 | kind: CodeActionKind.QuickFix 237 | }); 238 | } 239 | }); 240 | return actions; 241 | } 242 | 243 | documents.listen(connection); 244 | 245 | connection.listen(); -------------------------------------------------------------------------------- /src/test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | // import * as vscode from 'vscode'; 12 | // import * as myExtension from '../extension'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", function () { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", function() { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | import * as testRunner from 'vscode/lib/testrunner'; 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /src/tidy.ts: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | Diagnostic, DiagnosticSeverity, TextDocument, WorkspaceFolder, Connection 4 | } from 'vscode-languageserver'; 5 | import { spawn } from 'child_process'; 6 | import { safeLoad } from 'js-yaml'; 7 | import Uri from 'vscode-uri'; 8 | import * as path from 'path'; 9 | import * as fs from 'fs'; 10 | 11 | // Invoke clang-tidy and transform it issues into a file/Diagnostics map. 12 | // 13 | // This invoke clang-tidy with the configured arguments and parses the results into vscode-languageserver.Diagnostic 14 | // objects. Each diagnostic contains the set of replacements clang-tidy recommends to address the issue. 15 | // The original clang-tidy recommendations are stored in the Diagnostic.code member in JSON string format. This can be 16 | // parsed into a ClangTidyDiagnostic. 17 | // 18 | // The ClangTidyDiagnostic and associated ClangTidyReplacement objects are extended to each contain a Range member. 19 | // This member is an alias for the FileOffset/Offset + Length members into a vscode-languageserver.Range value. This 20 | // better supports integration with vscode we have sufficient data to more easily resolve the range value here rather 21 | // than later. 22 | // 23 | // textDocument: The TextDocument to lint using clang-tidy 24 | // configuration: Details of the clang-tidy-linter extension configuration; i.e., how to invoke clang-tidy. 25 | // onParsed: Callback to invoke once the diagnostics are generated. The argument holds a dictionary of results. 26 | // These are keyed on absolute file path (not URI) and the array of associated Diagnostics for that file. 27 | export function generateDiagnostics( 28 | connection: Connection, 29 | textDocument: TextDocument, configuration: Configuration, 30 | workspaceFolders: WorkspaceFolder[], 31 | onParsed: (doc: TextDocument, diagnostics: { [id: string]: Diagnostic[] }, 32 | diagnosticsCount: number) => void) { 33 | 34 | let decoded = ''; 35 | // Dictionary of collated diagnostics keyed on absolute file name. This supports source files generating 36 | // diagnostics for header files. 37 | const diagnostics: { [id: string]: Diagnostic[]; } = {}; 38 | let diagnosticsCount: number = 0; 39 | // Dictionary of text documents used to resolve character offsets into ranges. 40 | // We need to support the textDocument and additional included files (e.g., header files) and use it to resolve 41 | // file level character offsets into line/character offsets used by VSCode. 42 | // Keyed on absolute file name. 43 | const docs: { [id: string]: TextDocument } = {}; 44 | 45 | let cppToolsConfigs: CppToolsConfigs | null = null; 46 | if (workspaceFolders) { 47 | cppToolsConfigs = readConfigFromCppTools(workspaceFolders); 48 | } 49 | 50 | // Immediately add entries for the textDocument. 51 | diagnostics[Uri.parse(textDocument.uri).fsPath] = []; 52 | docs[Uri.parse(textDocument.uri).fsPath] = textDocument; 53 | 54 | const args = [Uri.parse(textDocument.uri).fsPath, '--export-fixes=-']; 55 | 56 | if (configuration.headerFilter) { 57 | args.push('-header-filter=' + configuration.headerFilter); 58 | } 59 | 60 | configuration.systemIncludePath.forEach(path => { 61 | const arg = '-extra-arg=-isystem' + path; 62 | args.push(arg); 63 | }); 64 | 65 | configuration.extraCompilerArgs.forEach(arg => { 66 | args.push('-extra-arg-before=' + arg); 67 | }); 68 | 69 | configuration.args.forEach(arg => { 70 | args.push(arg); 71 | }); 72 | 73 | if (cppToolsConfigs) { 74 | const { cppToolsIncludePaths, cStandard, cppStandard } = cppToolsConfigs; 75 | if (textDocument.languageId === 'c') { 76 | args.push('-extra-arg-before=-xc'); 77 | if (cStandard) { 78 | args.push('-extra-arg-before=-std=' + cStandard); 79 | } 80 | } 81 | 82 | if (textDocument.languageId === 'cpp') { 83 | args.push('-extra-arg-before=-xc++'); 84 | if (cppStandard) { 85 | args.push('-extra-arg-before=-std=' + cppStandard); 86 | } 87 | } 88 | 89 | cppToolsIncludePaths.forEach(path => { 90 | const arg = '-extra-arg=-I' + path; 91 | args.push(arg); 92 | }); 93 | } 94 | 95 | // Replace ${workspaceFolder} in arguments. Seem to be a number of issues open regarding 96 | // support for this in the VSCode API, but I can't find a solution. 97 | if (workspaceFolders) { 98 | const workspaceFolder = Uri.parse(workspaceFolders[0].uri).fsPath; 99 | args.forEach(function (arg, index) { 100 | args[index] = arg.replace("${workspaceFolder}", workspaceFolder); 101 | }); 102 | } 103 | 104 | // console.warn("clang-tidy with args: " + args); 105 | const childProcess = spawn(configuration.executable, args); 106 | 107 | childProcess.on('error', console.error); 108 | if (childProcess.pid) { 109 | childProcess.stdout.on('data', (data) => { 110 | decoded += data; 111 | }); 112 | childProcess.stdout.on('end', () => { 113 | const match = decoded.match(/(^\-\-\-(.*\n)*\.\.\.$)/gm); 114 | if (match && match[0]) { 115 | const yaml = match[0]; 116 | const parsed = safeLoad(yaml) as ClangTidyResult; 117 | parsed.Diagnostics.forEach((element: ClangTidyDiagnostic) => { 118 | const name: string = element.DiagnosticName; 119 | const severity = name.endsWith('error') ? 120 | DiagnosticSeverity.Error : DiagnosticSeverity.Warning; 121 | const message: string = `${element.Message} (${name})`; 122 | 123 | // Helper function to ensure absolute paths and required registrations are made. 124 | function fixPath(filePath: string): string { 125 | if (filePath && !path.isAbsolute(filePath)) { 126 | filePath = path.resolve(path.dirname(Uri.parse(textDocument.uri).fsPath), 127 | filePath); 128 | } 129 | 130 | if (!(filePath in diagnostics)) { 131 | diagnostics[filePath] = []; 132 | } 133 | 134 | // Resolve replacement.FileOffset and replacement.Length into a range. 135 | let doc: TextDocument | undefined = undefined; 136 | 137 | // Resolve the document. 138 | if (!(filePath in docs)) { 139 | // Unresolved. We'll create a new TextDocument reference loading the content. 140 | // This is potentially inefficient, and it would be nice to see if we can leverage 141 | // VSCode to manage this. 142 | if (fs.existsSync(filePath)) { 143 | doc = TextDocument.create("file://" + filePath, 144 | textDocument.languageId, 0, 145 | fs.readFileSync(filePath).toString()); 146 | docs[filePath] = doc; 147 | } 148 | } 149 | 150 | return filePath; 151 | } 152 | 153 | // Create a dictionary of diagnostics ensuring we use absolute paths to handle errors from headers. 154 | const clangTidySourceName: string = 'Clang Tidy'; 155 | 156 | // Ensure an absolute path for the main clang-tidy element. 157 | element.FilePath = fixPath(element.FilePath); 158 | 159 | // Iterate the replacements to: 160 | // - Ensure absolute paths. 161 | // - Resolve clang's character offset and length to a line and character range. 162 | if (element.Replacements) { 163 | for (const replacement of element.Replacements) { 164 | // Ensure replacement FilePath entries use absolute paths. 165 | replacement.FilePath = fixPath(element.FilePath); 166 | 167 | // Create a diagnostic for the replacement. The context of each replacement may be a 168 | // different file from the element's FilePath. 169 | let doc: TextDocument; 170 | if (replacement.FilePath in docs) { 171 | doc = docs[replacement.FilePath]; 172 | 173 | //replacement.Offset is byte offset, not the character offset 174 | //when your source file contains some symbol which takes more than one byte 175 | //to encode, it will cause error. 176 | const doc_buff = Buffer.from(doc.getText()); 177 | const character_offset = doc_buff.toString('utf-8',0,replacement.Offset).length; 178 | 179 | replacement.Range = { 180 | start: doc.positionAt(character_offset), 181 | end: doc.positionAt(character_offset + replacement.Length) 182 | }; 183 | } 184 | } 185 | } 186 | // Create a VSCode Diagnostic. Use the original textDocument if we fail to resolve the document 187 | // path. This ensures the user gets feedback. 188 | const doc = element.FilePath in docs ? docs[element.FilePath] : textDocument; 189 | element.Range = { 190 | start: doc.positionAt(element.FileOffset), 191 | end: doc.positionAt(element.FileOffset) 192 | }; 193 | 194 | const diagnostic: Diagnostic = Diagnostic.create(element.Range, message, severity, 195 | element.Replacements && JSON.stringify(element.Replacements), clangTidySourceName); 196 | 197 | diagnostics[element.FilePath].push(diagnostic); 198 | ++diagnosticsCount; 199 | 200 | }); 201 | } 202 | 203 | onParsed(textDocument, diagnostics, diagnosticsCount); 204 | }); 205 | } 206 | } 207 | 208 | function readConfigFromCppTools(workspaceFolders: WorkspaceFolder[]): CppToolsConfigs { 209 | const cppToolsIncludePaths: string[] = []; 210 | let cStandard: string = ''; 211 | let cppStandard: string = ''; 212 | 213 | workspaceFolders.forEach(folder => { 214 | const config = path.join(Uri.parse(folder.uri).fsPath, '.vscode/c_cpp_properties.json'); 215 | if (fs.existsSync(config)) { 216 | const content = fs.readFileSync(config, { encoding: 'utf8' }); 217 | const configJson = JSON.parse(content); 218 | if (configJson.configurations) { 219 | configJson.configurations.forEach((config: any) => { 220 | if (config.includePath) { 221 | config.includePath.forEach((path: string) => { 222 | cppToolsIncludePaths.push(path.replace('${workspaceFolder}', '.')); 223 | }); 224 | } 225 | cStandard = config.cStandard; 226 | cppStandard = config.cppStandard; 227 | }); 228 | } 229 | } 230 | }); 231 | 232 | return { 233 | cppToolsIncludePaths, cStandard, cppStandard 234 | }; 235 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | /* Strict Type-Checking Option */ 12 | "strict": true, /* enable all strict type-checking options */ 13 | /* Additional Checks */ 14 | "noUnusedLocals": true /* Report errors on unused locals. */ 15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test" 22 | ] 23 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "prefer-const": true, 9 | "semicolon": [ 10 | true, 11 | "always" 12 | ], 13 | "triple-equals": true, 14 | "max-line-length": [ 15 | true, 16 | 120 17 | ] 18 | }, 19 | "defaultSeverity": "warning" 20 | } -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | * This folder contains all of the files necessary for your extension. 5 | * `package.json` - this is the manifest file in which you declare your extension and command. 6 | The sample plugin registers a command and defines its title and command name. With this information 7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | The file exports one function, `activate`, which is called the very first time your extension is 10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 11 | We pass the function containing the implementation of the command as the second parameter to 12 | `registerCommand`. 13 | 14 | ## Get up and running straight away 15 | * Press `F5` to open a new window with your extension loaded. 16 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 17 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension. 18 | * Find output from your extension in the debug console. 19 | 20 | ## Make changes 21 | * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. 22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | ## Explore the API 25 | * You can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`. 26 | 27 | ## Run tests 28 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`. 29 | * Press `F5` to run the tests in a new window with your extension loaded. 30 | * See the output of the test result in the debug console. 31 | * Make changes to `test/extension.test.ts` or create new test files inside the `test` folder. 32 | * By convention, the test runner will only consider files matching the name pattern `**.test.ts`. 33 | * You can create folders inside the `test` folder to structure your tests any way you want. 34 | --------------------------------------------------------------------------------