Files
spring-tools/atom-extensions/atom-commons/lib/jar-language-client.js
2017-08-15 19:21:17 -04:00

191 lines
6.4 KiB
JavaScript

const cp = require('child_process');
const fs = require('fs');
const path = require('path');
const url = require('url');
const remote = require('remote-file-size');
const PortFinder = require('portfinder');
const net = require('net');
const rpc = require('vscode-jsonrpc');
const {AutoLanguageClient, DownloadFile} = require('atom-languageclient');
const { Disposable } = require('atom');
export class JarLanguageClient extends AutoLanguageClient {
constructor(serverDownloadUrl, serverHome, serverLauncherJar) {
super();
this.serverHome = serverHome;
this.serverDownloadUrl = serverDownloadUrl;
this.serverLauncherJar = serverLauncherJar;
}
startServerProcess () {
// //TODO: Remove when debugging is over
atom.config.set('core.debugLSP', true);
let childProcess;
return new Promise((resolve, reject) => {
PortFinder.getPort((err, port) => {
this.server = net.createServer(socket => {
this.socket = socket;
resolve(childProcess);
});
this.server.listen(port, 'localhost', () => {
this.launchProcess(port).then(p => childProcess = p);
});
});
});
}
launchProcess(port) {
const command = this.findJavaExecutable('java');
return this.compatibleJavaVersion(command).then(version => {
if (version) {
var args = this.launchVmArgs(version);
if (version >= 9) {
args.push('--add-modules=java.se.ee');
}
return this.getOrInstallLauncher().then(launcher => this.doLaunchProcess(command, launcher, port, args));
} else {
this.logger.error('Java executable is not Java 8 or higher');
}
});
}
launchVmArgs(version) {
return [];
}
doLaunchProcess(javaExecutable, launcher, port, args=[]) {
let vmArgs = args.concat([
`-Dserver.port=${port}`,
// Atom doesn't have lazy completion proposals support - completionItem/resolve message. Disable lazy completions
'-Dlsp.lazy.completions.disable=true',
'-Dlsp.completions.indentation.enable=true',
'-Dlsp.yaml.completions.errors.disable=true',
'-jar',
launcher
]);
this.logger.debug(`starting "${javaExecutable} ${vmArgs.join(' ')}"`);
return cp.spawn(javaExecutable, vmArgs, { cwd: this.serverHome })
}
getOrInstallLauncher() {
const fullLauncherJar = path.join(this.serverHome, this.serverLauncherJar);
return this.fileExists(fullLauncherJar).then(doesExist =>
doesExist ? fullLauncherJar : this.installServer().then(() => fullLauncherJar)
);
}
installServer () {
const localFileName = path.join(this.serverHome, this.serverLauncherJar);
this.logger.log(`Downloading ${this.serverDownloadUrl} to ${localFileName}`);
return this.fileExists(this.serverHome)
.then(doesExist => { if (!doesExist) fs.mkdir(this.serverHome) })
.then(() => this.remoteFileSize(this.serverDownloadUrl))
.then((size) => DownloadFile(this.serverDownloadUrl, localFileName, (bytesDone, percent) => this.handleDownlaodPercentChange(bytesDone, size, percent), size))
.then(() => this.fileExists(path.join(this.serverHome, this.serverLauncherJar)))
.then(doesExist => { if (!doesExist) throw Error(`Failed to install the ${this.getServerName()} language server`) })
.then(() => this.handleServerInstalled())
.then(() => Promise.resolve(true));
}
handleDownlaodPercentChange(bytesDone, size, percent) {
}
handleServerInstalled() {
}
preInitialization(connection) {
connection.onCustom('language/status', (e) => this.updateStatusBar(`${e.type.replace(/^Started$/, '')} ${e.message}`));
}
remoteFileSize(url) {
return new Promise((resolve, reject) => {
remote(url, (e,s) => {
if (e) {
reject(e);
} else {
resolve(s);
}
});
});
}
fileExists (path) {
return new Promise((resolve, reject) => {
fs.access(path, fs.R_OK, error => {
resolve(!error || error.code !== 'ENOENT');
})
})
}
findJavaExecutable(binname) {
binname = this.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;
}
correctBinname(binname) {
if (process.platform === 'win32')
return binname + '.exe';
else
return binname;
}
compatibleJavaVersion(javaExecutablePath) {
return new Promise((resolve, reject) => {
cp.execFile(javaExecutablePath, ['-version'], {}, (error, stdout, stderr) => {
cp.execFile(javaExecutablePath, ['-version'], {}, (error, stdout, stderr) => {
if (stderr.indexOf('1.8') >= 0) {
resolve(8);
} else if (stderr.indexOf('java version "9"') >= 0) {
resolve(9);
} else {
resolve(0);
}
});
});
});
}
// Late wire-up of listeners after initialize method has been sent
postInitialization(server) {
server.disposable.add(new Disposable(() => {
this.server.close()
}));
}
}