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

Support refactoring when renaming or moving files #1445

Merged
merged 2 commits into from
Mar 24, 2021

Conversation

testforstephen
Copy link
Contributor

Signed-off-by: Jinbo Wang jinbwan@microsoft.com

@@ -163,7 +163,7 @@ public void testMoveFile() throws JavaModelException, BadLocationException {
assertNotNull(refactorEdit);
assertNotNull(refactorEdit.edit);
List<Either<TextDocumentEdit, ResourceOperation>> changes = refactorEdit.edit.getDocumentChanges();
assertEquals(4, changes.size());
assertEquals(3, changes.size());
Copy link
Contributor

Choose a reason for hiding this comment

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

why would this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The move refactoring code generates some empty textEdit operation, for example, insert empty string at the same location. I refined the ChangeUtils utility to filter out such empty operations. That's why you see the changes list is shorter.

@testforstephen testforstephen marked this pull request as ready for review May 18, 2020 14:32
@testforstephen
Copy link
Contributor Author

test this please

@testforstephen
Copy link
Contributor Author

test this please

@rgrunber
Copy link
Contributor

rgrunber commented Mar 3, 2021

Just a few things I've noticed from trying this out :

  • Performing a file rename, choosing to preview the change, and then discarding the proposed refactorings doesn't undo the rename itself. Not sure if this is intended behaviour in this case though.
  • The Refactor Preview entries seem to display more than just the actual changes when the change occurs in an import
  • Also, even after accepting the refactoring with 'OK', the changes are left unsaved in the files, so an additional save (all) needs to be done manually

refactor-rename-undo-bug

I also see the following error in the output :

[Info  - 4:22:49 PM] Mar 3, 2021, 4:22:49 PM >> document/documentSymbol
[Trace - 4:22:49 PM] Received response 'textDocument/documentSymbol - (1070)' in 5ms. Request failed: The request (id: 1070, method: 'textDocument/documentSymbol') has been cancelled (-32800).
...
...
[Error - 4:22:49 PM] Mar 3, 2021, 4:22:49 PM Problem getting outline forUtil.java
File not found: /tmp/foo/src/org/foo/bar/biz/Util.java.
Java Model Exception: Core Exception [code 368] File not found: /tmp/foo/src/org/foo/bar/biz/Util.java.
	at org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray(Util.java:1214)
	at org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray(Util.java:1186)
	at org.eclipse.jdt.internal.core.CompilationUnit.openBuffer(CompilationUnit.java:1218)
	at org.eclipse.jdt.internal.core.CompilationUnit.buildStructure(CompilationUnit.java:114)
	at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:266)
	at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:596)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:326)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:312)
	at org.eclipse.jdt.internal.core.JavaElement.getChildren(JavaElement.java:267)
	at org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler.getHierarchicalOutline(DocumentSymbolHandler.java:133)
	at org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler.documentSymbol(DocumentSymbolHandler.java:78)
	at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$13(JDTLanguageServer.java:633)
	at org.eclipse.jdt.ls.core.internal.BaseJDTLanguageServer.lambda$0(BaseJDTLanguageServer.java:75)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:642)
	at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Caused by: org.eclipse.core.internal.resources.ResourceException: File not found: /tmp/foo/src/org/foo/bar/biz/Util.java.
	at org.eclipse.core.internal.localstore.FileSystemResourceManager.read(FileSystemResourceManager.java:835)
	at org.eclipse.core.internal.resources.File.getContents(File.java:275)
	at org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray(Util.java:1212)
	... 19 more
Caused by: java.lang.Exception: File not found: /tmp/foo/src/org/foo/bar/biz/Util.java.
	at org.eclipse.core.internal.resources.ResourceException.provideStackTrace(ResourceException.java:42)
	at org.eclipse.core.internal.resources.ResourceException.<init>(ResourceException.java:38)
	... 22 more
Caused by: org.eclipse.core.internal.resources.ResourceException(/foo_caeb84bf/_/src/org/foo/bar/biz/Util.java)[368]: java.lang.Exception: File not found: /tmp/foo/src/org/foo/bar/biz/Util.java.
	at org.eclipse.core.internal.resources.ResourceException.provideStackTrace(ResourceException.java:42)
	at org.eclipse.core.internal.resources.ResourceException.<init>(ResourceException.java:38)
	at org.eclipse.core.internal.localstore.FileSystemResourceManager.read(FileSystemResourceManager.java:835)
	at org.eclipse.core.internal.resources.File.getContents(File.java:275)
	at org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray(Util.java:1212)
	at org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray(Util.java:1186)
	at org.eclipse.jdt.internal.core.CompilationUnit.openBuffer(CompilationUnit.java:1218)
	at org.eclipse.jdt.internal.core.CompilationUnit.buildStructure(CompilationUnit.java:114)
	at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:266)
	at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:596)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:326)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:312)
	at org.eclipse.jdt.internal.core.JavaElement.getChildren(JavaElement.java:267)
	at org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler.getHierarchicalOutline(DocumentSymbolHandler.java:133)
	at org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler.documentSymbol(DocumentSymbolHandler.java:78)
	at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$13(JDTLanguageServer.java:633)
	at org.eclipse.jdt.ls.core.internal.BaseJDTLanguageServer.lambda$0(BaseJDTLanguageServer.java:75)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:642)
	at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

@testforstephen
Copy link
Contributor Author

Performing a file rename, choosing to preview the change, and then discarding the proposed refactorings doesn't undo the rename itself. Not sure if this is intended behaviour in this case though.

This is by design. Clicking discard button just mean skipping the refactoring changes. It won't stop the file move operation.

The Refactor Preview entries seem to display more than just the actual changes when the change occurs in an import

The preview just shows what we give to it. Do you mean the refactoring job returns some unnecessary changes in the workspace edit? Or just about the displayed diff label for the change?

Also, even after accepting the refactoring with 'OK', the changes are left unsaved in the files, so an additional save (all) needs to be done manually.

We just use the client's file API FileWillRenameEvent.waitUntil to return a workspace edit. The subsequent actions are controlled by the client itself. So the save behavior is a decision from the client implementation. So far, VS Code won't save the edit, If there is more people think it's necessary to auto save the refactoring changes, we can open a feature request in VS Code for saving the rename edit.

Last one about the documentSymbol exception, looks like the client sends an extra document symbol request while the file is moving. There is race condition, where the disk file has been moved when dealing with the document symbol for the old file uri. Since the documentSymbol for old uri is no meaning during move, this failure won't affect any UI experience. There could three possible solutions to fix this:

  • Ask the client not to send documentSymbol for old uri when moving files.
  • JDT language server checks whether the file exists to guard the exception.
  • Keep the exception because it reflects the actual LSP status.

@rgrunber
Copy link
Contributor

rgrunber commented Mar 4, 2021

Ok, then the majority of the points are fine as long as it's expected, or not harmful. It looks like needing to save all the files is the same even prior to this change so should be ok for now.

The Refactor Preview entries seem to display more than just the actual changes when the change occurs in an import

The preview just shows what we give to it. Do you mean the refactoring job returns some unnecessary changes in the workspace edit? Or just about the displayed diff label for the change?

After trying a current release, it seems the behaviour of the diff label is the same so maybe this can be addressed separately. But yes, the labels seem long. I thought they would have just show the text being changed, but I seem to also be getting 'public class Main', in the demo above, though it remains unchanged.

@testforstephen
Copy link
Contributor Author

testforstephen commented Mar 5, 2021

After trying a current release, it seems the behaviour of the diff label is the same so maybe this can be addressed separately. But yes, the labels seem long. I thought they would have just show the text being changed, but I seem to also be getting 'public class Main', in the demo above, though it remains unchanged.

Look at your sample again, actually it updates two references in Main.java. One is the import reference for Util, another is the class reference from getTest() body. It seems we merge these two changes into one long text change in the final WorkspaceEdit. That's why you see a long diff label "Helper;\n\npublic class Main {\n \n public String getTest() {\n return Helper".

After debugging jdt.ls code, looks like the original JDT refactoring utilities return a MultiTextEdit to update the references in Main.java, which contains two child replace edits for import update and getText() body update. But when we use the TextEditConverter helper to convert the eclipse TextEdit to lsp WorkspaceEdit, that MultiTextEdit is converted to one change finally.

Since this is the converter quality issue of the helper method TextEditConverter, i'd suggest to improve it with a separate issue.

@testforstephen
Copy link
Contributor Author

With the latest commit, the "File Not Found" exception above is suppressed.

@rgrunber Could you take a look again to see whether there is any more improvement to fix before we merge it?

@testforstephen testforstephen added this to the End March 2021 milestone Mar 17, 2021
Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>
Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>
@testforstephen
Copy link
Contributor Author

The latest commit has adopted the lsp4j interfaces to handle willRenameFiles request. The last mile is to register FileOperationsServerCapabilities to notify the client we support willRenameFiles file operation.

But there is an issue eclipse-lsp4j/lsp4j#522 in lsp4j to block the adoption of FileOperationsServerCapabilities. What i want is to only listen to "**/*.java" files and "**" folders in file watcher. Currently there is no way to specify a glob pattern for files only. I already pushed a fix to lsp4j, not sure how soon a new lsp4j release will come.

My thought is that we keep using client-side API workspace.onWillRenameFiles to trigger workspace/willRenameFiles request and get this feature out first. Once lsp4j fix is ready, we can add back FileOperationsServerCapabilities and retire the client-side listener.

@testforstephen testforstephen merged commit 30583b9 into eclipse-jdtls:master Mar 24, 2021
@testforstephen testforstephen deleted the jinbo_move branch March 24, 2021 02:52
@testforstephen testforstephen self-assigned this Apr 6, 2021
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.

None yet

5 participants