Files
spring-tools/atom-extensions/atom-commons/lib/java-process-language-client.ts

191 lines
6.5 KiB
TypeScript

import * as path from 'path';
import {getPort} from 'portfinder';
import {Server, createServer, connect} from 'net';
import {AtomEnvironment, Disposable} from 'atom';
import {HighlightParams, ProgressParams, CursorMovementParams, StsAdapter} from './sts-adapter';
import {ActiveServer, LanguageServerProcess} from 'atom-languageclient';
import {AutoLanguageClient} from 'atom-languageclient';
import {findJdk, findJvm, JVM} from '@pivotal-tools/jvm-launch-utils';
import {Writable} from 'stream';
export class JavaProcessLanguageClient extends AutoLanguageClient {
DEBUG = false;
private server: Server;
constructor(protected serverHome: string, protected serverLauncherJar: string) {
super();
}
getServerJar(): string {
return path.resolve(this.serverHome, this.serverLauncherJar);
}
showErrorMessage(detail: string, desc?: string): Promise<any> {
const atomEnv: AtomEnvironment = atom;
const notification = atomEnv.notifications.addError('Cannot start Language Server', {
dismissable: true,
detail: detail,
description: desc,
buttons: [{
text: 'OK',
onDidClick: () => {
notification.dismiss()
},
}]
});
return Promise.reject(new Error(detail));
}
protected startServerProcess(projectPath: string): LanguageServerProcess | Promise<LanguageServerProcess> {
// TODO: Remove when debugging is over
const atomEnv: AtomEnvironment = atom;
atomEnv.config.set('core.debugLSP', true);
let childProcess: LanguageServerProcess;
if (this.DEBUG) {
return this.connectToLS();
}
return new Promise((resolve, reject) => {
let basePort = Math.floor(Math.random() * 10000) + 40000;
getPort({port: basePort}, (err, port) => {
this.server = createServer(socket => {
this.socket = socket;
resolve(childProcess);
});
this.server.listen(port, 'localhost', () => {
this.launchProcess(port).then(p => childProcess = p);
});
});
});
}
private connectToLS(): LanguageServerProcess | Promise<LanguageServerProcess> {
return new Promise(resolve => {
this.socket = connect({
port: 5007
});
const stdout: any = {
setEncoding: () => {
return stdout;
},
on: () => stdout
};
const process: LanguageServerProcess = {
stdin: <Writable>{},
stdout: stdout,
stderr: stdout,
pid: -1,
kill: () => {
console.log('fake shutdown');
},
on: () => process,
addListener: () => process,
prependListener: () => process,
once: () => process,
prependOnceListener: () => process,
removeListener: () => process,
removeAllListeners: () => process,
setMaxListeners: () => process,
getMaxListeners: () => 0,
listeners: () => [],
emit: () => false,
eventNames: () => [],
listenerCount: () => 0
};
resolve(process);
});
}
// Start adapters that are not shared between servers
protected postInitialization(server: ActiveServer): void {
const stsAdapter = this.createStsAdapter() || new StsAdapter();
(<any>server.connection)._onRequest({method: 'sts/moveCursor'}, (params: CursorMovementParams) => stsAdapter.onMoveCursor(params));
server.connection.onCustom('sts/progress', (params: ProgressParams) => stsAdapter.onProgress(params));
server.connection.onCustom('sts/highlight', (params: HighlightParams) => stsAdapter.onHighlight(params));
server.disposable.add(new Disposable(() => {
if (this.server) {
this.server.close()
}
}));
}
preferJdk(): boolean {
return false;
}
findJvm(): Promise<JVM | null> {
const options = this.getJavaOptions();
const javaHome = options ? this.getJavaOptions().home : '';
return this.preferJdk() ? findJdk(javaHome) : findJvm(javaHome);
}
private launchProcess(port: number): Promise<LanguageServerProcess> {
return this.findJvm()
.catch(error => {
return this.showErrorMessage("Error trying to find JVM", ""+error);
})
.then(jvm => {
if (!jvm) {
return this.showErrorMessage("Couldn't locate java in $JAVA_HOME or $PATH");
}
let version = jvm.getMajorVersion();
if (version<8) {
return this.showErrorMessage(
'No compatible Java Runtime Environment found',
'The Java Runtime Environment is either below version "1.8" or is missing from the system'
);
}
return this.launchVmArgs(jvm).then(args => {
args.push(`-Dserver.port=${port}`);
return this.doLaunchProcess(
jvm,
this.getServerJar(),
args
);
});
});
}
protected launchVmArgs(jvm: JVM): Promise<string[]> {
return Promise.resolve([]);
}
private doLaunchProcess(jvm: JVM, launcher: string, args: string[] =[]): LanguageServerProcess {
let vmArgs = args.concat([
'-Dsts.lsp.client=atom',
// '-Dlsp.completions.indentation.enable=true', // Looks like Atom has magic indents same like VSCode - comment it out
'-Dlsp.yaml.completions.errors.disable=true',
]);
const javaOptions = this.getJavaOptions();
if (javaOptions && javaOptions.vmargs) {
vmArgs.push(...javaOptions.vmargs);
}
this.logger.debug(`starting "${jvm.getJavaExecutable()} ${vmArgs.join('\n')}\n-jar ${launcher}"`);
return jvm.jarLaunch(launcher, vmArgs, { cwd: this.serverHome });
}
createStsAdapter(): StsAdapter | null {
return null;
}
protected getJavaOptions(): JavaOptions {
return {};
}
}
export interface JavaOptions {
home?: string;
vmargs?: string[];
}