├── .github └── dependabot.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── LICENSE.md ├── README.md ├── command-history-demo.gif ├── icon.png ├── package-lock.json ├── package.json ├── select-command.gif ├── src ├── JQProvider.ts ├── commands │ └── showPreview.ts ├── extension.ts └── jq.ts ├── tsconfig.json ├── tslint.json └── update-file.gif /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: prettier 11 | versions: 12 | - ">= 2.a, < 3" 13 | - dependency-name: "@types/vscode" 14 | versions: 15 | - 1.52.0 16 | - dependency-name: typescript 17 | versions: 18 | - 4.1.3 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": [ 11 | "--extensionDevelopmentPath=${workspaceRoot}" 12 | ], 13 | "stopOnEntry": false, 14 | "sourceMaps": true, 15 | "outFiles": [ 16 | "${workspaceRoot}/out/src/**/*.js" 17 | ], 18 | "preLaunchTask": "npm: watch" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.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 | "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version 10 | "typescript.tsc.autoDetect": "off" // Turn off tsc task auto detection since we have the necessary task as npm scripts 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 | typings/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 Leonardo Florez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jq Live View (JSON selector) 2 | 3 | Use jq to process a `.json` file, updating the preview as you 4 | save your file. 5 | 6 | ![update-file-demo](update-file.gif) 7 | 8 | Use the `jq` command to [select from a `.json` file](https://stedolan.github.io/jq/manual/) 9 | 10 | ![select-command-demo](select-command.gif) 11 | 12 | This extension will keep a history of the commands that you run 13 | 14 | ![command-history-demo](command-history-demo.gif) 15 | 16 | # Configuration 17 | 18 | This extension can be configured in User Settings or Workspace settings. 19 | 20 | | **Setting** | **Type** | **Description** | 21 | | --------------- | --------- | ------------------------------------------------------------------------------------ | 22 | | `customCommand` | `string` | See [_Running jq with command line options_](##Running-jq-with-command-line-options) | 23 | | `strictMode` | `boolean` | See [_Strict Mode_](##strict-mode) | 24 | 25 | ## Running jq with command line options 26 | 27 | For your convenience, `customCommand` replaces the following variables: 28 | 29 | `$$user_filter` is replaced with the filter you typed 30 | 31 | `$$file_path` is replaced with the current file path 32 | 33 | An example, using modules: 34 | 35 | ```json 36 | { 37 | "jq.customCommand": "jq -L /my/modules/path 'include \"items\"; $$user_filter' $$file_path" 38 | } 39 | ``` 40 | 41 | ## Strict Mode 42 | 43 | By default, this extension only works with files with a `json` file identifier. Turn this off if you want to use it with any file type. 44 | 45 | Alternatively, you can modify VSCode's `files.associations` in your User Settings: 46 | 47 | ```json 48 | "files.associations": { 49 | "*.log.a.txt": "json" 50 | }, 51 | ``` 52 | 53 | Strict Mode may, in the future, include other checks that you will always be able to turn off. 54 | 55 | ## Version History 56 | 57 | - `v0.4.2` 58 | 59 | - Fix #44 (Allow paths with spaces) 60 | 61 | - `v0.4.1` 62 | 63 | - Fix #18 (file save issue) 64 | 65 | - `v0.4.0` 66 | 67 | - Add history of commands on a per-open-file basis 68 | 69 | - `v0.3.0` 70 | 71 | - Add `strictMode` configuration option 72 | (do not check for `json` language identifier) 73 | - Fix escaping input box (no longer opens a live view) 74 | 75 | - `v0.2.0` 76 | 77 | - Deprecate `jqArgs`,`jqPrefix`, `jqPostfix` and replaces its functionality with `customCommand` 78 | 79 | - `v0.1.0` 80 | 81 | - Add configuration options `jqArgs`,`jqPrefix`, `jqPostfix` to run jq qith command line options 82 | 83 | - `v0.0.1` 84 | - Initial Public Release 85 | 86 | ## Acknowledgments 87 | 88 | - [jq](https://stedolan.github.io/jq/) 89 | - [Online JSON generator](https://www.json-generator.com/#) - for the previews 90 | -------------------------------------------------------------------------------- /command-history-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldd/vscode-jq/cc4933bc4d3cd7ba5a92a72d0f18322a90227939/command-history-demo.gif -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldd/vscode-jq/cc4933bc4d3cd7ba5a92a72d0f18322a90227939/icon.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jq-preview", 3 | "version": "0.4.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | } 32 | }, 33 | "@types/vscode": { 34 | "version": "1.47.0", 35 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.47.0.tgz", 36 | "integrity": "sha512-nJA37ykkz9FYA0ZOQUSc3OZnhuzEW2vUhUEo4MiduUo82jGwwcLfyvmgd/Q7b0WrZAAceojGhZybg319L24bTA==", 37 | "dev": true 38 | }, 39 | "ansi-styles": { 40 | "version": "3.2.1", 41 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 42 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 43 | "dev": true, 44 | "requires": { 45 | "color-convert": "^1.9.0" 46 | } 47 | }, 48 | "argparse": { 49 | "version": "1.0.10", 50 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 51 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 52 | "dev": true, 53 | "requires": { 54 | "sprintf-js": "~1.0.2" 55 | } 56 | }, 57 | "balanced-match": { 58 | "version": "1.0.0", 59 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 60 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 61 | "dev": true 62 | }, 63 | "brace-expansion": { 64 | "version": "1.1.11", 65 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 66 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 67 | "dev": true, 68 | "requires": { 69 | "balanced-match": "^1.0.0", 70 | "concat-map": "0.0.1" 71 | } 72 | }, 73 | "builtin-modules": { 74 | "version": "1.1.1", 75 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 76 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 77 | "dev": true 78 | }, 79 | "chalk": { 80 | "version": "2.4.2", 81 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 82 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 83 | "dev": true, 84 | "requires": { 85 | "ansi-styles": "^3.2.1", 86 | "escape-string-regexp": "^1.0.5", 87 | "supports-color": "^5.3.0" 88 | } 89 | }, 90 | "color-convert": { 91 | "version": "1.9.3", 92 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 93 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 94 | "dev": true, 95 | "requires": { 96 | "color-name": "1.1.3" 97 | } 98 | }, 99 | "color-name": { 100 | "version": "1.1.3", 101 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 102 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 103 | "dev": true 104 | }, 105 | "commander": { 106 | "version": "2.20.3", 107 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 108 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 109 | "dev": true 110 | }, 111 | "concat-map": { 112 | "version": "0.0.1", 113 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 114 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 115 | "dev": true 116 | }, 117 | "diff": { 118 | "version": "4.0.2", 119 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 120 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 121 | "dev": true 122 | }, 123 | "escape-string-regexp": { 124 | "version": "1.0.5", 125 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 126 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 127 | "dev": true 128 | }, 129 | "esprima": { 130 | "version": "4.0.1", 131 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 132 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 133 | "dev": true 134 | }, 135 | "fs.realpath": { 136 | "version": "1.0.0", 137 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 138 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 139 | "dev": true 140 | }, 141 | "glob": { 142 | "version": "7.1.6", 143 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 144 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 145 | "dev": true, 146 | "requires": { 147 | "fs.realpath": "^1.0.0", 148 | "inflight": "^1.0.4", 149 | "inherits": "2", 150 | "minimatch": "^3.0.4", 151 | "once": "^1.3.0", 152 | "path-is-absolute": "^1.0.0" 153 | } 154 | }, 155 | "has-flag": { 156 | "version": "3.0.0", 157 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 158 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 159 | "dev": true 160 | }, 161 | "inflight": { 162 | "version": "1.0.6", 163 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 164 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 165 | "dev": true, 166 | "requires": { 167 | "once": "^1.3.0", 168 | "wrappy": "1" 169 | } 170 | }, 171 | "inherits": { 172 | "version": "2.0.4", 173 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 174 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 175 | "dev": true 176 | }, 177 | "js-tokens": { 178 | "version": "4.0.0", 179 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 180 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 181 | "dev": true 182 | }, 183 | "js-yaml": { 184 | "version": "3.14.0", 185 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 186 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 187 | "dev": true, 188 | "requires": { 189 | "argparse": "^1.0.7", 190 | "esprima": "^4.0.0" 191 | } 192 | }, 193 | "minimatch": { 194 | "version": "3.0.4", 195 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 196 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 197 | "dev": true, 198 | "requires": { 199 | "brace-expansion": "^1.1.7" 200 | } 201 | }, 202 | "minimist": { 203 | "version": "1.2.5", 204 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 205 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 206 | "dev": true 207 | }, 208 | "mkdirp": { 209 | "version": "0.5.5", 210 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 211 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 212 | "dev": true, 213 | "requires": { 214 | "minimist": "^1.2.5" 215 | } 216 | }, 217 | "once": { 218 | "version": "1.4.0", 219 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 220 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 221 | "dev": true, 222 | "requires": { 223 | "wrappy": "1" 224 | } 225 | }, 226 | "path-is-absolute": { 227 | "version": "1.0.1", 228 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 229 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 230 | "dev": true 231 | }, 232 | "path-parse": { 233 | "version": "1.0.6", 234 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 235 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 236 | "dev": true 237 | }, 238 | "prettier": { 239 | "version": "1.19.1", 240 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", 241 | "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", 242 | "dev": true 243 | }, 244 | "resolve": { 245 | "version": "1.17.0", 246 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 247 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 248 | "dev": true, 249 | "requires": { 250 | "path-parse": "^1.0.6" 251 | } 252 | }, 253 | "semver": { 254 | "version": "5.7.1", 255 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 256 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 257 | "dev": true 258 | }, 259 | "sprintf-js": { 260 | "version": "1.0.3", 261 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 262 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 263 | "dev": true 264 | }, 265 | "supports-color": { 266 | "version": "5.5.0", 267 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 268 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 269 | "dev": true, 270 | "requires": { 271 | "has-flag": "^3.0.0" 272 | } 273 | }, 274 | "tslib": { 275 | "version": "1.13.0", 276 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 277 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 278 | "dev": true 279 | }, 280 | "tslint": { 281 | "version": "6.1.3", 282 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", 283 | "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", 284 | "dev": true, 285 | "requires": { 286 | "@babel/code-frame": "^7.0.0", 287 | "builtin-modules": "^1.1.1", 288 | "chalk": "^2.3.0", 289 | "commander": "^2.12.1", 290 | "diff": "^4.0.1", 291 | "glob": "^7.1.1", 292 | "js-yaml": "^3.13.1", 293 | "minimatch": "^3.0.4", 294 | "mkdirp": "^0.5.3", 295 | "resolve": "^1.3.2", 296 | "semver": "^5.3.0", 297 | "tslib": "^1.13.0", 298 | "tsutils": "^2.29.0" 299 | } 300 | }, 301 | "tslint-config-prettier": { 302 | "version": "1.18.0", 303 | "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", 304 | "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", 305 | "dev": true 306 | }, 307 | "tsutils": { 308 | "version": "2.29.0", 309 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 310 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", 311 | "dev": true, 312 | "requires": { 313 | "tslib": "^1.8.1" 314 | } 315 | }, 316 | "typescript": { 317 | "version": "3.9.5", 318 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", 319 | "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", 320 | "dev": true 321 | }, 322 | "wrappy": { 323 | "version": "1.0.2", 324 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 325 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 326 | "dev": true 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jq-preview", 3 | "displayName": "jq Live View", 4 | "description": "A sample JQ for virtual document", 5 | "icon": "icon.png", 6 | "version": "0.4.2", 7 | "publisher": "ldd-vs-code", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/ldd/vscode-jq" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/ldd/vscode-jq/issues" 14 | }, 15 | "engines": { 16 | "vscode": "^1.47.0" 17 | }, 18 | "categories": [ 19 | "Other" 20 | ], 21 | "activationEvents": [ 22 | "onCommand:jq.showPreview" 23 | ], 24 | "main": "./out/extension.js", 25 | "contributes": { 26 | "configuration": { 27 | "title": "jq Live View", 28 | "properties": { 29 | "jq.customCommand": { 30 | "type": "string", 31 | "default": "jq '$$user_filter' '$$file_path'", 32 | "markdownDescription": "Custom command that supports jq command line options.\n\n `$$user_filter` is replaced with the filter you typed\n\n `$$file_path` is replaced with the current file path" 33 | }, 34 | "jq.strictMode": { 35 | "type": "boolean", 36 | "default": true, 37 | "markdownDescription": "By default, this extension only works with files with a `json` file identifier. Turn this off if you want to use it everywhere." 38 | } 39 | } 40 | }, 41 | "commands": [ 42 | { 43 | "command": "jq.showPreview", 44 | "title": "jq" 45 | } 46 | ], 47 | "menus": { 48 | "editor/title": [ 49 | { 50 | "command": "jq.showPreview", 51 | "group": "navigation", 52 | "when": "resourceScheme != jq && !config.jq.strictMode || resourceScheme != jq && resourceLangId == json" 53 | } 54 | ], 55 | "commandPalette": [ 56 | { 57 | "command": "jq.showPreview", 58 | "when": "resourceScheme != jq && !config.jq.strictMode || resourceScheme != jq && resourceLangId == json" 59 | } 60 | ] 61 | } 62 | }, 63 | "scripts": { 64 | "vscode:prepublish": "npm run compile", 65 | "compile": "tsc -p ./", 66 | "lint": "tslint -p ./", 67 | "watch": "tsc -watch -p ./" 68 | }, 69 | "devDependencies": { 70 | "@types/vscode": "^1.47.0", 71 | "prettier": "^1.19.1", 72 | "tslint": "^6.1.2", 73 | "tslint-config-prettier": "^1.18.0", 74 | "typescript": "^3.9.5" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /select-command.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldd/vscode-jq/cc4933bc4d3cd7ba5a92a72d0f18322a90227939/select-command.gif -------------------------------------------------------------------------------- /src/JQProvider.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter, TextDocumentContentProvider, Uri } from "vscode"; 2 | 3 | export class JQProvider implements TextDocumentContentProvider { 4 | // emitter and its event 5 | public onDidChangeEmitter = new EventEmitter(); 6 | public onDidChange = this.onDidChangeEmitter.event; 7 | 8 | public provideTextDocumentContent(uri: Uri): string { 9 | return uri.query; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/commands/showPreview.ts: -------------------------------------------------------------------------------- 1 | import { QuickPickItem, Uri, ViewColumn, window, workspace } from "vscode"; 2 | import { spawnJq, stringifyCommand } from "../jq"; 3 | 4 | const initialChoices = ["."]; 5 | const generateItems = () => initialChoices.map((label) => ({ label })); 6 | 7 | async function pickFilter(uri: Uri, histories: WeakMap) { 8 | try { 9 | return await new Promise((resolve, reject) => { 10 | const input = window.createQuickPick(); 11 | input.placeholder = "Type a new command or select one from your history"; 12 | input.items = histories.get(uri) ?? generateItems(); 13 | 14 | let label = ""; 15 | input.onDidChangeValue((newText) => (label = newText)); 16 | 17 | input.onDidAccept(() => { 18 | // if the user selects a new command, add it to our histories map 19 | // if the history map is empty, for whatever reason, we fill it too 20 | const { selectedItems } = input; 21 | const newHistory = [...input.items]; 22 | if (selectedItems.length < 1) newHistory.push({ label }); 23 | histories.set(uri, newHistory); 24 | 25 | resolve(selectedItems[0]?.label ?? label); 26 | input.dispose(); 27 | }); 28 | input.show(); 29 | }); 30 | } finally { 31 | // we can probably reset history for this uri here if we absolutely do not want to keep it 32 | } 33 | } 34 | 35 | const showPreview = ( 36 | queries: WeakMap, 37 | histories: WeakMap 38 | ) => async (uri: Uri) => { 39 | if (!window.activeTextEditor) return; 40 | 41 | const { document } = window.activeTextEditor; 42 | const { fileName, languageId, uri: documentUri } = document; 43 | 44 | const config = workspace.getConfiguration("jq"); 45 | const { strictMode, validLanguageIdentifiers = [] } = config; 46 | 47 | // strict mode requires our document languageId to be part of validLanguageIdentifiers 48 | // you can technically configurate this using fileAssociations, but there may be reasons for users to bypass this check 49 | // see https://github.com/ldd/vscode-jq/issues/17 50 | if (strictMode && languageId !== "json") return; 51 | 52 | let jqCommand: string | undefined; 53 | if (queries.has(uri)) { 54 | jqCommand = queries.get(uri); 55 | } else { 56 | // better QuickPick with history suggestion inspired by this issue: https://github.com/microsoft/vscode/issues/426 57 | jqCommand = await pickFilter(documentUri, histories); 58 | queries.set(documentUri, jqCommand); 59 | } 60 | 61 | // jqCommand could be undefined for a number of reasons, we exit early to avoid trouble 62 | if (jqCommand === undefined) return; 63 | 64 | const query = await spawnJq(jqCommand, fileName, config); 65 | if (query) { 66 | const name = stringifyCommand(jqCommand); 67 | const previewUri = Uri.parse(`jq:${name}.json`).with({ query }); 68 | const doc = await workspace.openTextDocument(previewUri); // calls back into the provider 69 | await window.showTextDocument(doc, { 70 | preserveFocus: true, 71 | preview: true, 72 | viewColumn: ViewColumn.Beside, 73 | }); 74 | } 75 | }; 76 | 77 | export default showPreview; 78 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | commands, 3 | ExtensionContext, 4 | QuickPickItem, 5 | Uri, 6 | workspace, 7 | } from "vscode"; 8 | import showPreview from "./commands/showPreview"; 9 | import { JQProvider } from "./JQProvider"; 10 | 11 | export function activate({ subscriptions }: ExtensionContext) { 12 | const queries = new WeakMap(); 13 | const histories = new WeakMap(); 14 | 15 | // register a content provider for the jq-scheme 16 | const myScheme = "jq"; 17 | const myProvider = new JQProvider(); 18 | subscriptions.push( 19 | workspace.registerTextDocumentContentProvider(myScheme, myProvider) 20 | ); 21 | 22 | // register a command that opens a jq-document 23 | subscriptions.push( 24 | commands.registerCommand("jq.showPreview", showPreview(queries, histories)) 25 | ); 26 | 27 | workspace.onDidSaveTextDocument((document) => { 28 | // only execute command on known documents 29 | if (!queries.has(document.uri)) return; 30 | commands.executeCommand("jq.showPreview", document.uri); 31 | }); 32 | workspace.onDidCloseTextDocument((document) => { 33 | queries.delete(document.uri); 34 | // histories.delete(document.uri); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /src/jq.ts: -------------------------------------------------------------------------------- 1 | import { exec } from "child_process"; 2 | import { promisify } from "util"; 3 | import { WorkspaceConfiguration } from "vscode"; 4 | 5 | const wrappedSpawn = promisify(exec); 6 | 7 | const DEFAULT_FILE = "sample.json"; 8 | 9 | export const DEFAULT_FILTER = "."; 10 | 11 | export async function spawnJq( 12 | userFilter: string = DEFAULT_FILTER, 13 | filePath: string = DEFAULT_FILE, 14 | config: WorkspaceConfiguration 15 | ) { 16 | // VSCode extensions currently do not support variable resolution in settings 17 | // https://github.com/microsoft/vscode/issues/2809 18 | // to work around it, we use well-defined variables starting with $$ that we replace here 19 | const { customCommand = `jq '$$user_filter' $$file_path` } = config; 20 | const parsedCommand = customCommand 21 | .replace("$$user_filter", userFilter) 22 | .replace("$$file_path", filePath); 23 | const { stdout } = await wrappedSpawn(parsedCommand); 24 | return stdout; 25 | } 26 | 27 | export function stringifyCommand(command: string = "") { 28 | return `${command.slice(0, 8)}...`; 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "sourceMap": true, 7 | "strict": true, 8 | "rootDir": "src" 9 | }, 10 | "exclude": [ 11 | "node_modules", 12 | ".vscode-test" 13 | ] 14 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-config-prettier"], 3 | "rules": { "no-implicit-dependencies": [true, ["vscode"]], "curly": false } 4 | } 5 | -------------------------------------------------------------------------------- /update-file.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldd/vscode-jq/cc4933bc4d3cd7ba5a92a72d0f18322a90227939/update-file.gif --------------------------------------------------------------------------------