'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 Path from "path"; import * as FS from "fs"; import { window, workspace, ExtensionContext, commands, tasks, Task, TaskExecution, ShellExecution, Uri, TaskDefinition, languages, IndentAction } from 'vscode'; import {LanguageClient, LanguageClientOptions, ServerOptions} from "vscode-languageclient"; /** Called when extension is activated */ export function activate(context: ExtensionContext) { console.log('Activating Java'); let javaExecutablePath = findJavaExecutable('java'); if (javaExecutablePath == null) { window.showErrorMessage("Couldn't locate java in $JAVA_HOME or $PATH"); return; } // Options to control the language client let clientOptions: LanguageClientOptions = { // Register the server for java documents documentSelector: ['java'], synchronize: { // Synchronize the setting section 'java' to the server // NOTE: this currently doesn't do anything configurationSection: 'java', // Notify the server about file changes to 'javaconfig.json' files contain in the workspace fileEvents: [ workspace.createFileSystemWatcher('**/javaconfig.json'), workspace.createFileSystemWatcher('**/pom.xml'), workspace.createFileSystemWatcher('**/WORKSPACE'), workspace.createFileSystemWatcher('**/BUILD'), workspace.createFileSystemWatcher('**/*.java') ] }, outputChannelName: 'Java', revealOutputChannelOn: 4 // never } let fatJar = Path.resolve(context.extensionPath, "out", "fat-jar.jar"); let args = [ '-cp', fatJar, '-Xverify:none', // helps VisualVM avoid 'error 62' 'org.javacs.Main' ]; console.log(javaExecutablePath + ' ' + args.join(' ')); // Start the child java process let serverOptions: ServerOptions = { command: javaExecutablePath, args: args, options: { cwd: context.extensionPath } } // Copied from typescript languages.setLanguageConfiguration('java', { indentationRules: { // ^(.*\*/)?\s*\}.*$ decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/, // ^.*\{[^}"']*$ increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/, indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/ }, onEnterRules: [ { // e.g. /** | */ beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, afterText: /^\s*\*\/$/, action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' } }, { // e.g. /** ...| beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, action: { indentAction: IndentAction.None, appendText: ' * ' } }, { // e.g. * ...| beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, action: { indentAction: IndentAction.None, appendText: '* ' } }, { // e.g. */| beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, action: { indentAction: IndentAction.None, removeText: 1 } }, { // e.g. *-----*/| beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, action: { indentAction: IndentAction.None, removeText: 1 } } ] }) // Create the language client and start the client. let languageClient = new LanguageClient('java', 'Java Language Server', serverOptions, clientOptions); let disposable = languageClient.start(); // Push the disposable to the context's subscriptions so that the // client can be deactivated on extension deactivation context.subscriptions.push(disposable); // Register test commands commands.registerCommand('java.command.test.run', runTest); } function findJavaExecutable(binname: string) { binname = correctBinname(binname); // First search java.home setting let userJavaHome = workspace.getConfiguration('java').get('home') as string; if (userJavaHome != null) { console.log('Looking for java in settings java.home ' + userJavaHome + '...'); let candidate = findJavaExecutableInJavaHome(userJavaHome, binname); if (candidate != null) return candidate; } // Then search each JAVA_HOME let envJavaHome = process.env['JAVA_HOME']; if (envJavaHome) { console.log('Looking for java in environment variable JAVA_HOME ' + envJavaHome + '...'); let candidate = findJavaExecutableInJavaHome(envJavaHome, binname); if (candidate != null) return candidate; } // Then search PATH parts if (process.env['PATH']) { console.log('Looking for java in 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; } function findJavaExecutableInJavaHome(javaHome: string, binname: string) { let workspaces = javaHome.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; } return null; } // this method is called when your extension is deactivated export function deactivate() { } interface JavaTestTask extends TaskDefinition { enclosingClass: string method: string } function runTest(sourceUri: string, enclosingClass: string, method: string): Thenable { let kind: JavaTestTask = { type: 'java.task.test', enclosingClass: enclosingClass, method: method, } // TODO make this configurable var shell; if (method != null) shell = new ShellExecution('mvn', ['test', `-Dtest=${enclosingClass}#${method}`]) else shell = new ShellExecution('mvn', ['test', `-Dtest=${enclosingClass}`]) let workspaceFolder = workspace.getWorkspaceFolder(Uri.parse(sourceUri)) let task = new Task(kind, workspaceFolder, 'Java Test', 'Java Language Server', shell) return tasks.executeTask(task) }