diff --git a/vscode-extensions/commons-vscode/.gitignore b/vscode-extensions/commons-vscode/.gitignore index 3cd27afdb..108587088 100644 --- a/vscode-extensions/commons-vscode/.gitignore +++ b/vscode-extensions/commons-vscode/.gitignore @@ -1,3 +1,5 @@ coverage/ node_modules/ npm-debug.log +lib/ + diff --git a/vscode-extensions/commons-vscode/README.md b/vscode-extensions/commons-vscode/README.md index 3a476a054..4a4bdd8b5 100644 --- a/vscode-extensions/commons-vscode/README.md +++ b/vscode-extensions/commons-vscode/README.md @@ -1,25 +1,7 @@ -# Using this module in other modules +# commons-vscode -Here is a quick example of how this module can be used in other modules. The [TypeScript Module Resolution Logic](https://www.typescriptlang.org/docs/handbook/module-resolution.html) makes it quite easy. The file `src/index.ts` acts as an aggregator of all the functionality in this module. It imports from other files and re-exports to provide a unified interface for this module. The _package.json_ file contains `main` attribute that points to the generated `lib/index.js` file and `typings` attribute that points to the generated `lib/index.d.ts` file. +A node+typescript module containing some utilites shared between STS4 vscode-extensions. -> If you are planning to have code in multiple files (which is quite natural for a NodeJS module) that users can import, make sure you update `src/index.ts` file appropriately. +Mainly offers utility function(s) to help launch a language-server implemented in +Java and then connect to it. -Now assuming you have published this amazing module to _npm_ with the name `my-amazing-lib`, and installed it in the module in which you need it - - -- To use the `Greeter` class in a TypeScript file - - -```ts -import { Greeter } from "my-amazing-lib"; - -const greeter = new Greeter("World!"); -greeter.greet(); -``` - -- To use the `Greeter` class in a JavaScript file - - -```js -const Greeter = require('my-amazing-lib').Greeter; - -const greeter = new Greeter('World!'); -greeter.greet(); -``` diff --git a/vscode-extensions/commons-vscode/package.json b/vscode-extensions/commons-vscode/package.json index 691f06665..cda13f6d4 100644 --- a/vscode-extensions/commons-vscode/package.json +++ b/vscode-extensions/commons-vscode/package.json @@ -7,8 +7,8 @@ "repository": "", "author": "Kris De Volder ", "engines": { - "node": ">=4.0.0", - "vscode": "^1.5.0" + "node": ">=4.0.0", + "vscode": "^1.5.0" }, "keywords": [ "" @@ -19,15 +19,17 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { - "clean": "rm -rf lib node_modules", - "compile": "tsc -watch -p ./", - "prepublish": "node ./node_modules/vscode/bin/install && tsc -p ./" + "clean": "rm -rf lib node_modules", + "compile": "tsc -watch -p ./", + "prepublish": "node ./node_modules/vscode/bin/install && tsc -p ./" }, "dependencies": { + "portfinder": "^0.4.0" }, "devDependencies": { "typescript": "2.0.x", "@types/node": "6.0.40", - "vscode": "^1.0.0" + "vscode": "^1.0.0", + "vscode-languageclient": "^2.6.2" } } diff --git a/vscode-extensions/commons-vscode/src/index.ts b/vscode-extensions/commons-vscode/src/index.ts index b947feee5..4b4e4858a 100644 --- a/vscode-extensions/commons-vscode/src/index.ts +++ b/vscode-extensions/commons-vscode/src/index.ts @@ -1,3 +1,3 @@ -import {testUtil} from './launch-util'; +import {activate, ActivatorOptions} from './launch-util'; -export {testUtil}; \ No newline at end of file +export {activate, ActivatorOptions}; diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index dfc5af5e4..949f245b3 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -1,5 +1,161 @@ import * as code from 'vscode'; -export function testUtil() { - return 'this is a test'; -} \ No newline at end of file +'use strict'; +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below + +import * as VSCode from 'vscode'; +import * as Path from 'path'; +import * as FS from 'fs'; +import PortFinder = require('portfinder'); +import * as Net from 'net'; +import * as ChildProcess from 'child_process'; +import {LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo} from 'vscode-languageclient'; +import {TextDocument, OutputChannel} from 'vscode'; + +PortFinder.basePort = 45556; + +const DEBUG_ARG = '-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=y'; + +export interface ActivatorOptions { + DEBUG: boolean; + extensionId : string; + clientOptions : LanguageClientOptions; + fatJarFile: string; +} + + +export function activate(options : ActivatorOptions, context: VSCode.ExtensionContext) { + //unpack options object + let DEBUG = options.DEBUG; + let clientOptions = options.clientOptions; + let fatJarFile = Path.resolve(context.extensionPath, options.fatJarFile); + + var log_output = VSCode.window.createOutputChannel(options.extensionId+"-debug-log"); + log("Activating '"+options.extensionId+"' extension"); + + function log(msg : string) { + if (log_output) { + log_output.append(msg +"\n"); + } + } + + function error(msg : string) { + if (log_output) { + log_output.append("ERR: "+msg+"\n"); + } + } + + let javaExecutablePath = findJavaExecutable('java'); + + if (javaExecutablePath == null) { + VSCode.window.showErrorMessage("Couldn't locate java in $JAVA_HOME or $PATH"); + return; + } + log("Found java exe: "+javaExecutablePath); + + + isJava8(javaExecutablePath).then(eight => { + if (!eight) { + VSCode.window.showErrorMessage('Java-based Language Server requires Java 8 (using ' + javaExecutablePath + ')'); + return; + } + log("isJavaEight => true"); + + function createServer(): Promise { + return new Promise((resolve, reject) => { + PortFinder.getPort((err, port) => { + Net.createServer(socket => { + log('Child process connected on port ' + port); + + resolve({ + reader: socket, + writer: socket + }); + }).listen(port, () => { + let options = { + cwd: VSCode.workspace.rootPath + }; + let child: ChildProcess.ChildProcess; + let args = [ + '-Dserver.port=' + port, + '-jar', + fatJarFile, + ]; + if (DEBUG) { + args.unshift(DEBUG_ARG); + } + log("CMD = "+javaExecutablePath + ' ' + args.join(' ')); + + // Start the child java process + child = ChildProcess.execFile(javaExecutablePath, args, options); + child.stdout.on('data', (data) => { + log(""+data); + }); + child.stderr.on('data', (data) => { + error(""+data); + }) + }); + }); + }); + } + + // Create the language client and start the client. + let client = new LanguageClient(options.extensionId, options.extensionId, + createServer, clientOptions + ); + let disposable = client.start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + }); +} + + +function isJava8(javaExecutablePath: string): Promise { + return new Promise((resolve, reject) => { + let result = ChildProcess.execFile(javaExecutablePath, ['-version'], { }, (error, stdout, stderr) => { + let eight = stderr.indexOf('1.8') >= 0; + resolve(eight); + }); + }); +} + +function findJavaExecutable(binname: string) { + binname = correctBinname(binname); + + // First search each JAVA_HOME bin folder + if (process.env['JAVA_HOME']) { + let workspaces = process.env['JAVA_HOME'].split(Path.delimiter); + for (let i = 0; i < workspaces.length; i++) { + let binpath = Path.join(workspaces[i], 'bin', binname); + if (FS.existsSync(binpath)) { + return binpath; + } + } + } + + // Then search PATH parts + if (process.env['PATH']) { + let pathparts = process.env['PATH'].split(Path.delimiter); + for (let i = 0; i < pathparts.length; i++) { + let binpath = Path.join(pathparts[i], binname); + if (FS.existsSync(binpath)) { + return binpath; + } + } + } + + // Else return the binary name directly (this will likely always fail downstream) + return null; +} + +function correctBinname(binname: string) { + if (process.platform === 'win32') + return binname + '.exe'; + else + return binname; +} + + diff --git a/vscode-extensions/commons-vscode/tsconfig.json b/vscode-extensions/commons-vscode/tsconfig.json index c7862608e..6c7cec34b 100644 --- a/vscode-extensions/commons-vscode/tsconfig.json +++ b/vscode-extensions/commons-vscode/tsconfig.json @@ -1,15 +1,19 @@ { "compilerOptions": { "module": "commonjs", - "target": "es5", - "noImplicitAny": true, + "target": "es6", + "lib": [ + "es6" + ], + "noImplicitAny": false, "sourceMap": true, "moduleResolution": "node", "declaration": true, "outDir": "lib" }, "include": [ - "src/**/*" + "src/**/*", + "typings/**.d.ts" ], "exclude": [ "node_modules", diff --git a/vscode-extensions/vscode-manifest-yaml/typings/portfinder.d.ts b/vscode-extensions/commons-vscode/typings/portfinder.d.ts similarity index 100% rename from vscode-extensions/vscode-manifest-yaml/typings/portfinder.d.ts rename to vscode-extensions/commons-vscode/typings/portfinder.d.ts diff --git a/vscode-extensions/vscode-manifest-yaml/lib/Main.ts b/vscode-extensions/vscode-manifest-yaml/lib/Main.ts index aa4f0b40b..d1b2f0d5f 100644 --- a/vscode-extensions/vscode-manifest-yaml/lib/Main.ts +++ b/vscode-extensions/vscode-manifest-yaml/lib/Main.ts @@ -3,17 +3,14 @@ // Import the module and reference it with the alias vscode in your code below import * as VSCode from 'vscode'; -import {testUtil} from 'commons-vscode'; +import * as commons from 'commons-vscode'; import * as Path from 'path'; import * as FS from 'fs'; -import PortFinder = require('portfinder'); import * as Net from 'net'; import * as ChildProcess from 'child_process'; import {LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo} from 'vscode-languageclient'; import {TextDocument, OutputChannel} from 'vscode'; -PortFinder.basePort = 45556; - var DEBUG = false; const DEBUG_ARG = '-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=y'; //If DEBUG is falsy then @@ -38,28 +35,11 @@ function error(msg : string) { /** Called when extension is activated */ export function activate(context: VSCode.ExtensionContext) { - VSCode.window.showInformationMessage(testUtil()); - VSCode.window.showInformationMessage("Activating manifest.yml extension"); - log_output = VSCode.window.createOutputChannel("manifest-yml-debug-log"); - log("Activating manifest.yml extension"); - let javaExecutablePath = findJavaExecutable('java'); - - if (javaExecutablePath == null) { - VSCode.window.showErrorMessage("Couldn't locate java in $JAVA_HOME or $PATH"); - return; - } - log("Found java exe: "+javaExecutablePath); - - isJava8(javaExecutablePath).then(eight => { - if (!eight) { - VSCode.window.showErrorMessage('Java language support requires Java 8 (using ' + javaExecutablePath + ')'); - return; - } - log("isJavaEight => true"); - - // Options to control the language client - let clientOptions: LanguageClientOptions = { - + let options : commons.ActivatorOptions = { + DEBUG : false, + extensionId: 'vscode-manifest-yaml', + fatJarFile: 'target/vscode-manifest-yaml-0.0.1-SNAPSHOT.jar', + clientOptions: { // HACK!!! documentSelector only takes string|string[] where string is language id, but DocumentFilter object is passed instead // Reasons: // 1. documentSelector is just passed over to functions like #registerHoverProvider(documentSelector, ...) that take documentSelector @@ -70,118 +50,14 @@ export function activate(context: VSCode.ExtensionContext) { // TODO: Remove cast ones https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved documentSelector: [ {language: 'yaml', pattern: '**/manifest*.yml'}], synchronize: { - // Synchronize the setting section to the server: - configurationSection: 'languageServerExample', - // Notify the server about file changes to 'javaconfig.json' files contain in the workspace - fileEvents: [ - //What's this for? Don't think it does anything useful for this example: - VSCode.workspace.createFileSystemWatcher('**/.clientrc') - ], - - // TODO: Remove textDocumentFilter property ones https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved + // TODO: Remove textDocumentFilter property once https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved textDocumentFilter: function(textDocument : TextDocument) : boolean { let result : boolean = /^(.*\/)?manifest[^\s\\/]*.yml$/i.test(textDocument.fileName); return result; } } } - - function createServer(): Promise { - return new Promise((resolve, reject) => { - PortFinder.getPort((err, port) => { - Net.createServer(socket => { - log('Child process connected on port ' + port); - - resolve({ - reader: socket, - writer: socket - }); - }).listen(port, () => { - let options = { - cwd: VSCode.workspace.rootPath - }; - let child: ChildProcess.ChildProcess; - let fatJarFile = Path.resolve(context.extensionPath, 'target/vscode-manifest-yaml-0.0.1-SNAPSHOT.jar'); - let args = [ - '-Dserver.port=' + port, - '-jar', - fatJarFile, - ]; - if (DEBUG) { - args.unshift(DEBUG_ARG); - } - log("CMD = "+javaExecutablePath + ' ' + args.join(' ')); - - // Start the child java process - child = ChildProcess.execFile(javaExecutablePath, args, options); - child.stdout.on('data', (data) => { - log(""+data); - }); - child.stderr.on('data', (data) => { - error(""+data); - }) - }); - }); - }); - } - - // Create the language client and start the client. - let client = new LanguageClient('manifest-yaml-extension', 'manifest-yaml-extension', - createServer, clientOptions); - let disposable = client.start(); - - // Push the disposable to the context's subscriptions so that the - // client can be deactivated on extension deactivation - context.subscriptions.push(disposable); - }); + }; + commons.activate(options, context); } -function isJava8(javaExecutablePath: string): Promise { - return new Promise((resolve, reject) => { - let result = ChildProcess.execFile(javaExecutablePath, ['-version'], { }, (error, stdout, stderr) => { - let eight = stderr.indexOf('1.8') >= 0; - - resolve(eight); - }); - }); -} - -function findJavaExecutable(binname: string) { - binname = correctBinname(binname); - - // First search each JAVA_HOME bin folder - if (process.env['JAVA_HOME']) { - let workspaces = process.env['JAVA_HOME'].split(Path.delimiter); - for (let i = 0; i < workspaces.length; i++) { - let binpath = Path.join(workspaces[i], 'bin', binname); - if (FS.existsSync(binpath)) { - return binpath; - } - } - } - - // Then search PATH parts - if (process.env['PATH']) { - let pathparts = process.env['PATH'].split(Path.delimiter); - for (let i = 0; i < pathparts.length; i++) { - let binpath = Path.join(pathparts[i], binname); - if (FS.existsSync(binpath)) { - return binpath; - } - } - } - - // Else return the binary name directly (this will likely always fail downstream) - return null; -} - -function correctBinname(binname: string) { - if (process.platform === 'win32') - return binname + '.exe'; - else - return binname; -} - -// this method is called when your extension is deactivated -export function deactivate() { -} diff --git a/vscode-extensions/vscode-manifest-yaml/package.json b/vscode-extensions/vscode-manifest-yaml/package.json index d21b9cfaf..a79209271 100644 --- a/vscode-extensions/vscode-manifest-yaml/package.json +++ b/vscode-extensions/vscode-manifest-yaml/package.json @@ -50,7 +50,6 @@ "vsce-package": "vsce package" }, "dependencies": { - "portfinder": "^0.4.0", "vscode-languageclient": "2.5.x" }, "devDependencies": {