-
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
Allow label providers to notify that changes require a refresh #5884
Conversation
|
Surely that would require coupling the label provider to the navigator. In any case rerendering the navigator would not in itself update the icon. We would need to make code changes to expose a function to clear the node in the nodes map Furthermore, if other UI components are developed that cache label properties or show them for long periods then they can listen to the emitter too, avoiding coupling between the various label providers and the various label consumers. Although the navigator is currently the only place where stale labels are a problem, we would want the icon to be changed system-wide. I assume that would be the case for most uses, as it would defeat the purpose of the names and icons if they were different in different places.
Ideally, yes. However no other uses in the 'theia' repository cached the property values, that I could see. Ideally the trees (markers, callhierarchy), and perhaps resources in debug and the scm views, would update. In our case that would be overkill, but I could see that other developers updating the label properties might want other consumers to show the updates. In that case those developers would be responsible for updating the consumers.
Most consumers of the labels will not be interested. They don't need to do anything different. The event emitter is there for the few consumers who may be interested. |
They are cached as part of React internal virtual DOM state, which should be rerendered for all clients which you mentioned.
My point is that If it returns one name for the navigator, but another name for other clients then it should not be implemented in |
btw |
There seems to be some confusion here. Label providers don't know if they are being called from the Navigator or from elsewhere (and nor should they), so how could it return one name for the navigator but another name for other clients? We want names and icons to be the same across the application. We do NOT want the icon in the navigator to be different from the icon shown elsewhere. The problem is that currently the icon in the navigator IS different from the icons shown elsewhere. The reason is that a state change resulted in a different icon now being returned by the label provider for a given fixed URI. The navigator shows the old icon, elsewhere we see the new icon (as soon as the icon is rerendered). We really have two choices:
or 2) We add a emitter or something similar so UI components with cache maps can refresh the cache and, ideally but not so important, other components can choose to rerender. |
In your case it seems that you want a template method to customize node names for the navigator rather than change a systemwide name. Just to be clear I'm not against to have an event that a systemwide name is changed, but
|
But we want our custom icon to appear systemwide. That is why we need it to be provided by the label provider. I would be very happy to make the code changes in the other places. This just was not as important to us as the navigator because our icon was rerendered soon enough anyway. |
@westbury got you, i will compile a list of places to be updated, please look at existing comments for now |
a48e7e2
to
ae65026
Compare
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 looks good, but won't work properly for diff editors.
ae65026
to
59b4ad0
Compare
@westbury We've discussed at the dev meeting a month ago to have the example Theia extension in Theia repo to provide internal clients for PRs like that. Could you convert the last commit into such extension? It should be located under Please rebase this PR as well. For testing I'm going to rebase it myself in Gitpod and verify different |
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.
Should not DiffUriLabelProviderContribution
implement onDidChange
as well by delegating to LabelProvider.onDidChange
in alignment with getLongName
and getName
methods?
mutableNode.name = this.labelProvider.getName(uri); | ||
mutableNode.description = this.labelProvider.getLongName(uri); | ||
mutableNode.icon = await this.labelProvider.getIcon(uri); | ||
this.fireChanged(); |
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.
Should be enough to file once after all nodes are processed?
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 see that the git history widget respects
onDidChange
event, but the git diff widget does not. Is there a reason on showing old labels for the diff widget?
This is now covered. Note that we can't simply have the diff label provider 'accepts' function extract the two parts and pass them on. This would cause infinite recursion. I have therefore redesigned this so that the two component uris can be obtained and the recursion handled by LabelProvider.
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.
We could have a flag recursion
in GitLabelProviderContribution
which set to true before calling LabelProvider
again, so it will skip when recursion
is true. There is no need to add another API to LabelProviderContribution
.
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.
Also GitUriLabelProviderContribution
does not implement onDIdChange
, although relies on the delegation to LabelProvider
.
I see that the git history widget respects |
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.
Props of the mini browser are not updated if they are computed from the label provider. W probably should handle it here: https://github.com/westbury/theia/blob/59b4ad001e6f57cb823dab56a23cdb32a4c50868/packages/mini-browser/src/browser/mini-browser-open-handler.ts#L126 It could be a bit tricky, since we should not do it when name and iconClass provided. It would be good to have a follow-up issue at least.
SCM status bar repository entry is not updated when a label is changed: https://github.com/westbury/theia/blob/59b4ad001e6f57cb823dab56a23cdb32a4c50868/packages/scm/src/browser/scm-contribution.ts#L151 |
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.
Generally looks and behaves good. But it would be good to add guards to avoid unnecessary rerendering. Also existing label provider contributions should respect new event.
} | ||
} | ||
} | ||
this.update(); |
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 guard it
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 guard is still missing, it fires even if nothing is affected
59b4ad0
to
fbadf73
Compare
OK, I've done that. Perhaps you wanted just a single extension called api-samples instead of an extension under api-samples directory just for label providers? I should have been at the Tuesday dev meeting, then I would know. Anyway, let me know if this is the structure you were expecting. |
Yes, I meant a single |
@westbury I need these changes to support icon theming VS Code contribution point. Would it be fine if i integrate your commits in my PR? or you are planning to work on it soon? |
Sorry to be so long before getting back to this. I didn't think anyone else was really interested. I have more changes so I will push those shortly. |
fbadf73
to
bb8d685
Compare
|
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.
Other than where I have commented above, I believe the issues have been addressed.
Thank you. Trying it out! |
*/ | ||
readonly onDidChange?: Event<DidChangeLabelEvent>; | ||
|
||
readonly getConstituentUris?: (compositeUri: URI) => URI[]; |
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 thought we agree that URI
should not leak here. Could we remove it and use only onDidChange
? Implementation should check whether passed element in affects
actually is URI
. We would like to pass other objects like FileStat
and any other shapes having uri
property.
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.
Unfortuntely one cannot implement this in onDidChange using the obvious way of putting something like the following in the DiffUriLabelProviderContribution constructor:
`
labelProvider.onDidChange(event => {
this.onDidChangeEmitter.fire({
affects: (element: URI) => {
if (!(element instanceof URI && DiffUris.isDiffUri(element))) {
return false;
}
const label = element.path.toString();
if (label) {
return false;
}
const [left, right] = DiffUris.decode(element);
return event.affects(left) || event.affects(right);
}
});
});
`
The problem is that the code that fires the event does not know what URIs it may get. It could potentially get a diff URI in which one of the nested URIs is also a diff URI, down any number of levels. This above code will recurse indefinitely as it fires an event for a one-level nested diff URI, then an event for a two level nested diff URI etc. There are ways of using flags to stop this, given that we know consumers will never nest diff URIs. However this felt to me more like a hack than a generic solution. So I came up with the current implementation which I feel is more robust in the sense that it is harder for contribution providers to make it all go horribly wrong.
protected affects(element: URI, event: DidChangeLabelEvent, originatingContribution: LabelProviderContribution): boolean { | ||
|
||
const contribs = this.findContribution(element); | ||
const possibleContribsWithDups = [ |
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.
Why cannot we just iterate contribs
? What would be the point to have a label provider contribution doing nothing? Also does not it break priority order applied in findContribution
?
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.
We don't want to process all contributions, just the one that was actually used to obtain the three label properties. As a contribution can implement only one or two of the three property getters, a different contribution may be used for each property. So there could be anywhere from one to three contributions that were actually used to get a property.
'contribs' is all contributions that 'can' process element, in priority order.
'possibleContribsWithDups' contains, in the first element, the actual contribution that would be called to get the icon. This matches the code in getIcon. Similarly the second element for the 'name' property and the third element for the 'longName' property.
'possibleContribsWithoutDups' contains the contributions that are actually called to get one or more of the three properties. So these are the only contributions that we need ask.
* @param uri | ||
* @param event | ||
*/ | ||
protected affects(element: URI, event: DidChangeLabelEvent, originatingContribution: LabelProviderContribution): boolean { |
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.
element
should be of object
type
@@ -0,0 +1,9 @@ | |||
# Theia - Examples - API Samples - Label Provider |
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.
We should add a new extension to top-level tsconfig.json. Otherwise reference search does not work for it. Try to find who implement LabelProviderContribution.didChange
import { Emitter, Event } from '@theia/core'; | ||
|
||
@injectable() | ||
export class DynamicLabelProviderContribution extends DefaultUriLabelProviderContribution { |
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 prefix it with Sample
?
} | ||
} | ||
} | ||
this.update(); |
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 guard is still missing, it fires even if nothing is affected
Please commit modification to .travis.yml file otherwise build is failing with:
|
Let me know if you don't have time, I could clean up my concerns with the additional commit. |
bb8d685
to
51161d2
Compare
@akosyakov I've addessed all your comments except your concerns about getConstituentUris (as I'm not sure what we can do that is better there). |
Signed-off-by: Nigel Westbury <nigelipse@miegel.org>
51161d2
to
b65c3d9
Compare
@westbury I will have a look again now. I'm thinking to go with your proposal and see how it works when I need to change icon theme. I won't consider |
That's fine. I understand why you don't like it. I am interested in seeing what you come up with instead. |
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.
works nicely from user perspective, thank you for taking care of all internal clients! ❤️
@westbury i'm merging it to continue working on icon theme support |
…butions See #5884 (comment) Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
…butions See #5884 (comment) Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
…butions See eclipse-theia#5884 (comment) Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
Currently label providers can provide names, descriptions, and icons. However the result is cached so once the provider has provided a property, the tree cannot be refreshed with a changed value.
We need to be able to refresh the navigation tree with changed values because we have an icon we set to a directory, but the icon depends on other files next to the directory. These other files may change and this requires updating the icon for the directory. Note that the Eclipse label providers provide a listener so that label providers can notify the UI components that they must refresh (see https://help.eclipse.org/neon/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/IBaseLabelProvider.html#addListener-org.eclipse.jface.viewers.ILabelProviderListener- ). The navigation tree already has an emitter to force a refresh so it is not that much more code to connect this to the label providers.
How to test
There is no easy way to test this. I have tested it in our application where I can rename a file back and forth and see the icon for a folder change back and forth.
To test it, one would have to modify a label provider to provide a name or icon that depends on something other than the uri. Perhaps it could depend on the existence of another file. Then add a file watcher to the label provider and fire the event. Here are some bits of code that may help:
and
and
and code to put in init:
which will need this
and the code to get the name could be something like:
Sorry, it's a lot of work to test. Alternatively just test that nothing is broken with this PR which will not require any extra code.
Review checklist
Reminder for reviewers