-
Notifications
You must be signed in to change notification settings - Fork 58
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
WorkspaceFolders: Fix interpretation of event flags #116
WorkspaceFolders: Fix interpretation of event flags #116
Conversation
…ectly. Avoid sending events with null content.
Tweak mock server initialisation so that the capabilities can be customised Move the logic for unsubscribing from Eclipse resource events a bit higher up LanguageServerWrapper.stop() to avoid potential race condition and NPE
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 for this good patch! To conform with Eclipse license and IP requirements, please sign the ECA (link is on the review part of this PR) and add copyright header to new files.
org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/WorkspaceFoldersTest.java
Show resolved
Hide resolved
|
} else { | ||
return null; | ||
} | ||
} else if (e.getType() != IResourceChangeEvent.POST_CHANGE) { |
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 we do this at the very beginning of the method, so that if this condition is met, we do not need to create an WorkspaceFoldersChangeEvent that we will never 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.
Sure, done. NB I kept this guard (and enhanced it for the extra event type I've added) but if the subscription at line 499 is honoured by Eclipse it should not to be needed, is that correct? I'm a relative newcomer to programming on the Eclipse platform so I'm never totally sure how defensive/paranoid to be...
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 am also a relative newcomer to programming on the Eclipse platform. Normally I would code as by API and remove the guard, since it should never be hit. According to git history @mickaelistria introduced the guard, so maybe he has some insight why he added it in the first place.
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 the subscription at line 499 is honoured by Eclipse it should not to be needed, is that correct?
Correct. However, it's always safer to re-validate important assumptions in the code. Adding a check has no significant impact on the performance but has a very positive impact on our capacity to change the code safely, so overall it's beneficial.
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. Noted for future!
if ((delta.getKind() == IResourceDelta.ADDED || (delta.getKind() == IResourceDelta.CHANGED | ||
&& (delta.getFlags() & IResourceDelta.OPEN) == IResourceDelta.OPEN)) | ||
&& project.isAccessible() | ||
&& wsFolder != null && !wsFolder.getUri().isEmpty()) { |
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.
getUri() can return null, so we need another condition: !wsFolder.getUri() != null
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.
Added as part of tidy up (see below).
|| (delta.getKind() == IResourceDelta.CHANGED | ||
&& (delta.getFlags() & IResourceDelta.OPEN) == IResourceDelta.OPEN)) | ||
&& !project.isAccessible() | ||
&& wsFolder != null && !wsFolder.getUri().isEmpty()) { |
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.
getUri() can return null, so we need another condition: !wsFolder.getUri() != null
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.
Not according to LSP specification. So a LS that does return null here is not compliant with the spec.
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.
Trivial to do, so added the guard anyway as part of the tidy up.
|| (delta.getKind() == IResourceDelta.CHANGED | ||
&& (delta.getFlags() & IResourceDelta.OPEN) == IResourceDelta.OPEN)) | ||
&& !project.isAccessible() | ||
&& wsFolder != null && !wsFolder.getUri().isEmpty()) { |
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 condition and the previous one are almost identical, could we refactor to a private method with a boolean flag isAccessbile
?
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 would be nice to break up the code a bit, but this class is already rather large, and so is e.g. LSPEclipseUtils
. What would you say to replacing the lambda workspaceFolderUpdater
at line 172 with a proper class implementing IResourceChangeListener
, then all of this logic could be broken up and put there?
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 guess it could be a nested class in here so that it could still have direct access to the LanguageServerWrapper.languageServer
field, but it would maybe be nicer to break it out completely?
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.
There was a race condition on shutdown where languageServer
could become null on a worker thread just before a resource event is fired on the event thread. I moved the de-registration above line 456 in stop()
but maybe a null guard in the workspaceFolderUpdater
body wouldn't hurt?
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 have no strong opinion about refactoring to a nested class. Regarding the null check, I think you need it in any case because when a project is deleted the URI will be null. I think this can happen regardless of the de-registration in stop()
.
Sorry for my previous message, build failure was not related. |
No problem - it was in a suspiciously related area of functionality! |
@ahmedneilhussain all those are good ideas. However, please create separate pull request for each of them as much as possible. Smaller PRs allow faster progress. |
@rubenporras @ahmedneilhussain Do you think this pull request is fine to merge in this current state, or are there some necessary changes left to implement ? (and refactoring and other stuff can be considered in further PRs) |
…licated boolean logic to be broken up into nearby helper methods.
Sorry for slow response guys, I had today off officially but decided it was only a little bit of work just to tidy the code into a separate class. |
Thanks! |
@ahmedneilhussain , since Mickael already reviewed and merged I am going to assume it is all fine :) |
I think it was already fine for a few interations, so I merged it so it can be easily tested and further improvements are still welcome in further PRs. |
Yes, the change looked good also to me, I just wanted to say that since you did the review already I would not look at it in much detail, since I did not think that would be needed. |
LSP4e implements the LSP Workspace Folders protocol [if supported by the remote back end] using the Eclipse ResourceListener mechanism. Looking at the current implementation and reading the Eclipse source code, it isn't using the eclipse API quite correctly.
The flags integer for an IResourceDelta seems to be a synthesis of an enum "Kind" (bottom 8 bits) and a bit mask (top 24 bits). The existing LSP4e code tries to detect a project open/close with
delta.getKind() == IResourceDelta.OPEN && project.isAccessible()
.That won't work, because
OPEN
is a qualifying bitmask flag, not a kind -OPEN = 0x4000
butgetKind()
returnsflags & 0xFF
.The
kind
for an open/close event is aCHANGE
, but with theOPEN
flag set.We also add
PRE_DELETE
to the filter ofResourceListener
events we are observing: this gives a cleaner behaviour on hard deletes if the underlying language server is also observing the file system, as sometimes delete events are only sent when the file tree is already gone.