-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Conversation
@@ -111,9 +107,25 @@ export class JavaClientContribution extends BaseLanguageClientContribution { | |||
}); | |||
} | |||
|
|||
protected createOptions(): LanguageClientOptions { | |||
async activate(): Promise<Disposable> { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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[]> { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
} | ||
|
||
waitForActivation(app: FrontendApplication): Promise<void> { | ||
return Promise.race([super.waitForActivation(app), this.javaService.collectJavaExtensions().then(extensions => { |
There was a problem hiding this comment.
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();
There was a problem hiding this comment.
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[]; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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[]; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
} | ||
} | ||
|
||
dispose(): void { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, thank you!
Hi @akosyakov |
There was a problem hiding this 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>; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
@svor before merging, please clean up history, that we don't have commits like |
protected createOptions(): LanguageClientOptions { | ||
const options = super.createOptions(); | ||
options.initializationOptions = { | ||
bundles: this.javaExtensions, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can override it in https://github.com/theia-ide/theia/blob/master/packages/java/src/node/java-contribution.ts
please make sure to use the super implementation as well
There was a problem hiding this comment.
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>; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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[]; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed README
packages/java/src/node/java-util.ts
Outdated
return this.javaExtensions; | ||
} | ||
|
||
const projectPath = process.cwd(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
packages/java/src/node/java-util.ts
Outdated
} | ||
|
||
const projectPath = process.cwd(); | ||
const manager = new ApplicationPackageManager({ projectPath }); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
@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. 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Red Hat, 2018
There was a problem hiding this 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?
*/ | ||
export interface JavaExtensionContribution { | ||
/** | ||
* Returns an array of paths to the java bundles. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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"
…eters Signed-off-by: Valeriy Svydenko <[email protected]>
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