kelemen / netbeans-gradle-project

This project is a NetBeans plugin able to open Gradle based Java projects. The implementation is based on Geertjan Wielenga's plugin.
170 stars 57 forks source link

Nodes for SourceSets do not update in case of folder creation/deletion/rename #330

Open mklaehn opened 7 years ago

mklaehn commented 7 years ago

Currently the Nodes for SourceSets do not behave correctly in case of file operations on the folders backing the SourceSets on disc.

First, the case that the backing SourceSet folder is no where it was - either by renaming or removing it. Currently renaming the backing folder does not do anything regarding the SourceSet Node. Worse, all operations are still possible and creating a file causes the file to be created in the moved SourceSet folder and thus suggests it is where the user wanted to create it. However once the project is reloaded the SourceSet is no longer there and displays the actual state of the SourceSet correctly. The correct behaviour would have been to remove the Node of the SourceSet. So this has to be fixed. Removing/Deleting the backing folder emties the SourceSet Node in the Project Explorer but does not remove it (which would be expected). Thus allowing further interaction (which should have been prevented). Creating a file for this SourceSet causes a backing folder - at the correct location - to be created (which might actually be a good thing but should have been impossible because it had to be done on a Node that should not have existed any longer). So this needs to be fixed as well.

Second, the case of creating the backing SourceSet folder. Currently no action is taken when the backing folder of a SourceSet is created, when in fact the Node for that SourceSet can be added to the Project explorer.

Both cases are achievable by registering a FileListener to the paths of the folders backing all SourceSets of a project. This FileListener is notified if any change (create, delete or rename) is performed for the FileObject represented by the registered Path(s).

kelemen commented 7 years ago

I had a look at your solution. However, it seems too heavy for a feature which is almost never needed and even if it is, there is workaround (with a little inconvenience). Of course, this feature would be nice but I'd prefer a solution which does not recursively watches the entire project directory (simply because it needs to watch only a very few directories), also source roots are not always in the project directory.

Other solutions I can think of are:

I will try to see, if I can implement the second one with reasonable efficiency.

mklaehn commented 7 years ago

I can not reject the overhead of the recursive listener nor the possible location of source roots outside of a projects folder (although I haven't yet seen that in real life).

The use case of source roots being deleted and created on the other hand is a default use case with generated sources. They are - more or less by default - located in the projects build folder and a simple gradle clean build removes the folder of the source root and recreates it once the generating task is run. This is by the way how I got started on this issue ;)

I'll try and update the pull request with changes addressing the recursive listener overhead and the source root location outside of the project with your second proposed solution today.

kelemen commented 7 years ago

I see, I didn't consider the generated sources. You are right, it is surely an annoyance in that case.

As for sources outside the project folder: I sometimes add some source roots of a subproject to buildSrc to be able to use the same code in the build as what I'm using in the project.

Anyway, in the mean time, I have reimplemented this feature. See, if it works for you.

mklaehn commented 7 years ago

I've checked your reimplementation and source roots that already existed at the time of loading the project - and with exist I mean physically exist on disc - this works as I would expect it to.

However considering the generated sources use case in combination with a clean checkout (i.e. no build folder) the code as it is does not show the source root nodes in the project explorer once they are created.

If you consider the linked build file build.gradle in a clean checkout then the nodes for the source root genSource1 is only displayed after running the build task (or doing anything else causes the source root folder to be created) and reloading the project.

Although this helps with existing source root folder this does not help with not yet existing source root folders.

kelemen commented 7 years ago

I'm not sure if I understand you. If the folder does not exist, then what is there to show? Also, I cannot create a "package view" of a non-existent directory (since it requires a FileObject which can only be created for an existing directory). If you want to just create all the declared source roots, there is an option for you without running any task: Project's context menu/Source roots/Create source roots.

mklaehn commented 7 years ago

The context menu action is known to but only helps up to a point. If the folder does not exist no Node is to be shown in the project view. That is correct. However once you determine that the folder has been created you can show the source root node in the project explorer.

BTW FileObject is a Abstraction of files physically existing on disc, of XML elements in a layer.xml file, and so on. This means that a FileObject must not have an existing file it is representing. However I can get a FileObject that (before a build) is not backed by an actual File and after the build (or a generator task) is done it is backed a File. The org.openide.filesystems.FileChangeListener knows of this contract and would have picked this up.

kelemen commented 7 years ago

However once you determine that the folder has been created you can show the source root node in the project explorer.

It is what happens. I have tried the build script, you have linked and if the directory didn't exist upon loading the project, it is not a problem: The node will appear as soon as the directory gets created.

You probably know more about this than me, so how can I create a FileObject from a path for which there is nothing on the disk yet? I always use FileUtil.toFileObject which will simply return null if there is no such file/directory. If I could create a FileObject in another way, that would surely be a more convenient solution than what I did.