Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add existed java extensions into jdt.ls initialization parameters #2593

Merged
merged 1 commit into from
Sep 20, 2018
Merged

Add existed java extensions into jdt.ls initialization parameters #2593

merged 1 commit into from
Sep 20, 2018

Conversation

svor
Copy link
Contributor

@svor svor commented Aug 16, 2018

Signed-off-by: Valeriy Svydenko [email protected]

What does this PR do:

This PR provides an ability to find all plugins(bundles) of jdt.ls in Theia's extensions and add them into initializationOptions when Java Language Server start initializing.

Reference issues

#1878

@svor svor changed the title Add existed java extensions into jdt.ls initialization parameters [WIP] Add existed java extensions into jdt.ls initialization parameters Aug 16, 2018
@@ -111,9 +107,25 @@ export class JavaClientContribution extends BaseLanguageClientContribution {
});
}

protected createOptions(): LanguageClientOptions {
async activate(): Promise<Disposable> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make sure to call super.activate to make sure that restarting works properly, it was changed recently

@@ -0,0 +1,38 @@
/********************************************************************************
* Copyright (C) 2017 TypeFox and others.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please check new copyrights, that it is 2018 and Red Hat for you

*/
@injectable()
export class JavaServiceImpl implements JavaService {
async collectJavaExtensions(): Promise<string[]> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be collected just one, there is no way to change extensions without restart

if (toDeactivate.disposed) {
messageConnection.dispose();
return;
}
toDeactivate.push(messageConnection);

const languageClient = this.createLanguageClient(messageConnection);
const languageClient = await this.createLanguageClient(messageConnection);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a connection is disposed in the meantime then we create a language client for a broken connection. I don't think this code can be async. Would it be possible to prefetch java extension as part of waitForActivation only once?

@svor svor changed the title [WIP] Add existed java extensions into jdt.ls initialization parameters Add existed java extensions into jdt.ls initialization parameters Aug 16, 2018
}

waitForActivation(app: FrontendApplication): Promise<void> {
return Promise.race([super.waitForActivation(app), this.javaService.collectJavaExtensions().then(extensions => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promise.race resolves if one of given promises is resolved, you want Promise.all or simpler:

await super.waitForActivation(app);
this.javaExtensions = await this.javaServices.collectJavaExtensions();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thank you

@@ -42,8 +43,10 @@ export class JavaClientContribution extends BaseLanguageClientContribution {
readonly name = JAVA_LANGUAGE_NAME;
private readonly statusNotificationName = 'java-status-notification';
private statusBarTimeout: number | undefined;
private javaExtensions: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) string[] | undefined if something is not initialized in the contructor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

*/
@injectable()
export class JavaServiceImpl implements JavaService {
private javaExtensions: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) string[] | undefined if something is not initialized in the constructor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
}

dispose(): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaService don't need to be Disposable or a method should not do anything, otherwise, it can throw if someone call dispose

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, thank you!

@svor
Copy link
Contributor Author

svor commented Aug 17, 2018

Hi @akosyakov
Thank you for the reviews!
I've applied fixes, take please another look at it when you have some free time.
Also I've created a new issue for searching java-extensions in Theia plugins: #2600.

Copy link
Member

@akosyakov akosyakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

* Finds all java extensions which are provided in extensions.
* @return an array of paths to the plugins
*/
collectJavaExtensions(): Promise<string[] | undefined>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose to return undefined ? It is better to return an empty array if there are no extensions.

Copy link
Contributor

@tolusha tolusha Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The javadoc is not clear. Does it return extensions or plugins?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"collectExtensionBundleIds()" perhaps? Or is it the path to the bundle jar? A more specific name would help.

Copy link
Contributor Author

@svor svor Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a path to the bundle jar, I think collectExtensionBundlesPaths() would be better

@akosyakov
Copy link
Member

@svor before merging, please clean up history, that we don't have commits like addressed comments or code cleanup

protected createOptions(): LanguageClientOptions {
const options = super.createOptions();
options.initializationOptions = {
bundles: this.javaExtensions,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, the only reason to have a front-end contribution at all is to populate this field. In my mind, it would be nicer to intercept the init message on the back end and to populate the field there. If possible, we should not let these implementation details dictate architecture.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you understand correctly. Is there a way to intercept the init command on the server?

Copy link
Member

@akosyakov akosyakov Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @akosyakov. Moved to the server side.

* Finds all java extensions which are provided in extensions.
* @return an array of paths to the plugins
*/
collectJavaExtensions(): Promise<string[] | undefined>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"collectExtensionBundleIds()" perhaps? Or is it the path to the bundle jar? A more specific name would help.

if (!this.javaExtensions) {
this.javaExtensions = [];
}
const contributes: JavaContributes = extPck.raw.contributes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I don't like this part at all. We should not be nosing around in other extensions package.json. We should be defining an extension interface and let the contributing extensions decide how the want to give us a list of extension jars. Like

interface JdtLsExtension {
    getExtensionJars() : String
}

We can still use package.json for a default implementation that contributors can use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand why declaration in package.json is bad idea. As for me it is simpler to add a path to the my bundle into packaje.json instead of implement some interface and it will work for both: extensions and plugins in Theia.

import * as paths from 'path';

interface JavaContributes extends NodePackage {
javaExtensions?: string[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs documentation. How does this work? I assume I need to add some text to package.json? What is the syntax of the strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed README

return this.javaExtensions;
}

const projectPath = process.cwd();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand this code, in the Java bundle, I would have to declare other bundles who can extend jdt.ls? This is not what we want, we want to have unknown third party bundles contribute bundles to jdt.ls. We should not be reading other extensions package.json by hand, we should declare an extension point, which other extensions can contribute to and then have the system pass us those extensions.

Copy link
Contributor Author

@svor svor Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tsmaeder it's not that. You have not declare bundles in Java extension if you want to extend jdt.ls.
Java extension in Theia just contains a mechanism for scanning all other Theia extensions and if some of them has java bundle (plugin for jdt.ls) it takes a path to this bundle (which was described in packaje.json in this extension) and adds this path to initializationOptions. And when jdt.ls is starting all found bundles from Theia extensions will be installed as well.

Imagine this:

  • Theia has A extension.
  • This extension contains bundle (custom-java-plugin.jar) to jdt.ls (in some subfolder)
  • package.json in A contains the path to the bundle

So, what Java extension does:

  • Before initializing jdt.ls Java extension scans all other Theia extensions
  • It finds that extension A has declaration about jdt.ls plugin (custom-java-plugin.jar)
  • Builds absolute path to this plugin (/..../A/subfolder/custom-java-plugin.jar)
  • Adds this path to initializationOptions and initializes jdt.ls
  • jdt.ls loads custom-java-plugin.jar bundle and installs it.

@tsmaeder tsmaeder mentioned this pull request Sep 11, 2018
19 tasks
}

const projectPath = process.cwd();
const manager = new ApplicationPackageManager({ projectPath });
Copy link
Member

@akosyakov akosyakov Sep 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@svor I am not sure about the state of this PR, but if you continue, please use ApplicationPackage instead and inject it as here: https://github.com/theia-ide/theia/blob/b57e11f1f9635b36efdc5a8478248d26e3a62035/packages/typescript/src/node/typescript-contribution.ts#L33-L49

ApplicationPackageManager brings dev time dependencies, like webpack. We don't want to have them at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akosyakov I'm going to continue my work on this PR, thank you!

@svor
Copy link
Contributor Author

svor commented Sep 17, 2018

@tsmaeder @akosyakov I have another variant to solve current problem, can you please review my last commit.

I've reworked this PR to provide an interface which should be implemented by contributing extensions.
Only one methods in this interface which returns list of paths to jd.ls bundles.

Just one question here: is it possible to get absolute path to the extension to build absolute path of jdt.ls bundle which included into this extension.

@@ -0,0 +1,31 @@
/********************************************************************************
* Copyright (C) 2017 TypeFox and others.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Red Hat, 2018

Copy link
Contributor

@tsmaeder tsmaeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this version a lot better. Also shorter, no?

packages/java/src/node/java-contribution.ts Show resolved Hide resolved
*/
export interface JavaExtensionContribution {
/**
* Returns an array of paths to the java bundles.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be clearer if you say "bundle jar files"

export const JavaExtensionContribution = Symbol('JavaExtensionContribution');

/**
* A contribution point for java extensions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"..for extensions to jdt.ls"

@tsmaeder tsmaeder merged commit 4788b6e into eclipse-theia:master Sep 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants