diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..382b46b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Tab indentation +[*] +indent_style = tab +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +# The indent size used in the `package.json` file cannot be changed +# https://github.com/npm/npm/pull/3180#issuecomment-16336516 +[{.travis.yml,npm-shrinkwrap.json,package.json}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ce0a74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +out +node_modules +.vscode-test +*.vsix diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c696e43 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/out/src/**/*.js" + ], + "preLaunchTask": "npm" + }, + { + "name": "Launch Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/out/test/**/*.js" + ], + "preLaunchTask": "npm" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7877e3f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "out": true // set this to false to include "out" folder in search results + }, + "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..fb7f662 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process + +// A task runner that calls a custom npm script that compiles the extension. +{ + "version": "0.1.0", + + // we want to run npm + "command": "npm", + + // the command is a shell script + "isShellCommand": true, + + // show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // we run the custom script "compile" as defined in package.json + "args": ["run", "compile", "--loglevel", "silent"], + + // The tsc compiler is started in watching mode + "isWatching": true, + + // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..93e28ff --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,9 @@ +.vscode/** +typings/** +out/test/** +test/** +src/** +**/*.map +.gitignore +tsconfig.json +vsc-extension-quickstart.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..63d3c92 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# JMESPath for Visual Studio Code + +JMESPath (pronounced "james path") is a query language for JSON which allows one to extract and transform elements from a JSON document. + +This extension lets you test JMESPath expressions from within Visual Studio Code. + +For more information about JMESPath, please visit [here](http://jmespath.org) + +## Features + +This extension depends on the [JavaScript implementation of JMESPath](https://github.com/jmespath/jmespath.js) which is fully compliant with latest [specification](http://jmespath.org/specification.html). + +![JMESPath Example](images/jmespath-example.gif) + +## How to use this extension + +This extension adds `JMESPath: Query JSON` command to the command palette. + +To use the extension: + +- Open a JSON document +- Run `JMESPath: Query JSON` command from command palette (`F1` on Windows or `Cmd+Shift+P` on Mac) +- Enter JMESPath expression +- Expression output will be shown in `JMESPath Output` window + +## Release Notes + +### 0.0.1 + +- Initial release + + diff --git a/images/jmespath-example.gif b/images/jmespath-example.gif new file mode 100644 index 0000000..9e020aa Binary files /dev/null and b/images/jmespath-example.gif differ diff --git a/images/jmespath.png b/images/jmespath.png new file mode 100644 index 0000000..1c8c1ac Binary files /dev/null and b/images/jmespath.png differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..6db064f --- /dev/null +++ b/package.json @@ -0,0 +1,74 @@ +{ + "name": "vscode-jmespath", + "displayName": "JMESPath for VSCode", + "description": "Evaluate JMESPath queries within Visual Studio Code", + "version": "0.0.1", + "main": "./out/src/jmespathMain", + "scripts": { + "vscode:prepublish": "node ./node_modules/vscode/bin/compile", + "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", + "postinstall": "node ./node_modules/vscode/bin/install", + "test": "node ./node_modules/vscode/bin/test" + }, + "devDependencies": { + "@types/chai": "^3.4.34", + "@types/sinon": "^1.16.34", + "chai": "^3.5.0", + "sinon": "^1.17.7", + "tslint": "^4.2.0", + "typescript": "^2.1.4", + "vscode": "^0.11.0" + }, + "dependencies": { + "jmespath": "^0.15.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/j--wong/vscode-jmespath.git" + }, + "publisher": "joshwong", + "icon": "images/jemspath.png", + "galleryBanner": { + "color": "#13254a", + "theme": "dark" + }, + "private": true, + "engines": { + "vscode": "^1.0.0" + }, + "categories": [ + "Other" + ], + "keywords": [ + "jmespath", + "json", + "query" + ], + "activationEvents": [ + "onLanguage:json", + "onCommand:jmespath.query" + ], + "contributes": { + "commands": [ + { + "title": "JMESPath: Query JSON", + "command": "jmespath.query" + } + ], + "configuration": { + "title": "", + "properties": { + "jmespath.rememberRecentExpressions": { + "type": "boolean", + "default": true, + "description": "Specify whether or not to record recently entered expressions" + }, + "jmespath.maxRecentExpressionsToRemember": { + "type": "integer", + "default": 25, + "description": "Specify the number of expressions to remember" + } + } + } + } +} diff --git a/src/jmespathMain.ts b/src/jmespathMain.ts new file mode 100644 index 0000000..d5705a6 --- /dev/null +++ b/src/jmespathMain.ts @@ -0,0 +1,14 @@ +"use strict"; + +import * as vscode from "vscode"; +import { queryJson } from "./jmespathQuery"; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.commands.registerTextEditorCommand("jmespath.query", + (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) => { + queryJson(context); + } + ) + ); +} diff --git a/src/jmespathQuery.ts b/src/jmespathQuery.ts new file mode 100644 index 0000000..5fe3a8b --- /dev/null +++ b/src/jmespathQuery.ts @@ -0,0 +1,45 @@ +import * as vscode from "vscode"; +let jmespath = require("jmespath"); + +let outputChannel = vscode.window.createOutputChannel("JMESPath Output"); + +export function queryJson(context: vscode.ExtensionContext) { + let editor = vscode.window.activeTextEditor; + + if (editor.document.languageId !== "json") { + vscode.window.showInformationMessage("Please open a JSON document."); + return; + } + + let options: vscode.InputBoxOptions = { + prompt: "Enter JMESPath expression", + placeHolder: "JMESPath expression" + }; + + vscode.window.showInputBox(options).then((expression) => { + if (expression.trim().length === 0) { + return; + } + + try { + jmespath.compile(expression); + } + catch (e) { + vscode.window.showErrorMessage(`${e.message}`); + return; + } + + let data = vscode.window.activeTextEditor.document.getText(); + try { + let jsonData = JSON.parse(data); + let searchResult = jmespath.search(jsonData, expression); + + outputChannel.clear(); + outputChannel.append(JSON.stringify(searchResult, null, " ")); + outputChannel.show(); + } catch (e) { + vscode.window.showErrorMessage(`${e.message}`); + return; + } + }); +} diff --git a/src/util/environment.ts b/src/util/environment.ts new file mode 100644 index 0000000..5113382 --- /dev/null +++ b/src/util/environment.ts @@ -0,0 +1,22 @@ +import * as path from "path"; +import * as vscode from "vscode"; + +export default class Environment { + private context: vscode.ExtensionContext; + + constructor(context: vscode.ExtensionContext) { + this.context = context; + } + + public getHomeDir(): string { + return process.env[(process.platform === "win32") ? "USERPROFILE" : "HOME"]; + } + + public isInsiders(): boolean { + return /insiders/.test(this.context.asAbsolutePath("")); + } + + public getExtensionDir(): string { + return path.join(this.getHomeDir(), this.isInsiders() ? ".vscode-insiders" : ".vscode", "extensions"); + } +} diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 0000000..c7c6237 --- /dev/null +++ b/test/index.ts @@ -0,0 +1,8 @@ +let testRunner = require("vscode/lib/testrunner"); + +testRunner.configure({ + ui: "bdd", + useColors: true +}); + +module.exports = testRunner; diff --git a/test/jmespathMain.test.ts b/test/jmespathMain.test.ts new file mode 100644 index 0000000..0ea5bf4 --- /dev/null +++ b/test/jmespathMain.test.ts @@ -0,0 +1,44 @@ +import * as vscode from "vscode"; +import * as jmespathMain from "../src/jmespathMain"; +import * as assert from "assert"; +import * as sinon from "sinon"; +import chai = require("chai"); + +let expect = chai.expect; + +describe("JMESPath extension", () => { + let extensionId = "joshwong.vscode-jmespath"; + + describe("#activate()", () => { + let context: vscode.ExtensionContext; + let stubTextEditorCommand: sinon.SinonStub; + + beforeEach(() => { + context = { + subscriptions: [], + workspaceState: null, + globalState: null, + extensionPath: null, + asAbsolutePath: sinon.stub() + }; + stubTextEditorCommand = sinon.stub(vscode.commands, "registerTextEditorCommand"); + }); + + afterEach(() => { + stubTextEditorCommand.restore(); + }); + + it("should register 'jmespath.query' command", sinon.test(() => { + jmespathMain.activate(context); + expect(stubTextEditorCommand.calledWith("jmespath.query")).to.be.true; + })); + + it("should add disposable command to subscriptions array", sinon.test(() => { + jmespathMain.activate(context); + + expect(context.subscriptions).to.not.be.empty; + expect(context.subscriptions.length).to.equals(1); + })); + }); + +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c438876 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "outDir": "out", + "noLib": true, + "sourceMap": true, + "rootDir": ".", + "moduleResolution": "node" + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..994841f --- /dev/null +++ b/tslint.json @@ -0,0 +1,55 @@ +{ + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "indent": [ + true, + "tabs" + ], + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": true, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "double" + ], + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/typings/node.d.ts b/typings/node.d.ts new file mode 100644 index 0000000..5ed7730 --- /dev/null +++ b/typings/node.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/typings/sinon.d.ts b/typings/sinon.d.ts new file mode 100644 index 0000000..20da980 --- /dev/null +++ b/typings/sinon.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/typings/vscode-typings.d.ts b/typings/vscode-typings.d.ts new file mode 100644 index 0000000..5590dc8 --- /dev/null +++ b/typings/vscode-typings.d.ts @@ -0,0 +1 @@ +///