LSP icon indicating copy to clipboard operation
LSP copied to clipboard

Add willRename and didRename fileOperations

Open predragnikolic opened this issue 1 year ago • 20 comments

closes #2199

Here is a example video:

https://github.com/sublimelsp/LSP/assets/22029477/441972ba-939c-40e0-899d-13629034a838

Notice the import changing in file a.ts when we rename the b.ts file.

example.zip

predragnikolic avatar Jun 25 '24 16:06 predragnikolic

I will highlight things implemented from the LSP spec.

Renaming a document

Document renames should be signaled to a server sending a document close notification with the document’s old name followed by an open notification using the document’s new name.

That part of the spec can be seen on these two lines:

  • send didClose notification https://github.com/sublimelsp/LSP/blob/6f654623cbedfd7f4dada7269783861d68c5b344/plugin/rename_file.py#L106
  • send didOpen notification https://github.com/sublimelsp/LSP/blob/6f654623cbedfd7f4dada7269783861d68c5b344/plugin/rename_file.py#L119-L120

[!NOTE] The user could rename main.py to main.ts. It is much easer to just close the view and open it, than to keep the file open and try modify the view to switch the syntax, language id, and a few more things... which I initially did, but concluded that requires more code, so I switched to just closing and opening the view.

WillRenameFiles Request

The will rename files request is sent from the client to the server before files are actually renamed as long as the rename is triggered from within the client either by a user action or by applying a workspace edit. The request can return a WorkspaceEdit which will be applied to the workspace before the files are renamed.

That can be seen here: https://github.com/sublimelsp/LSP/blob/6f654623cbedfd7f4dada7269783861d68c5b344/plugin/rename_file.py#L95-L98

Please note that clients might drop results if computing the edit took too long or if a server constantly fails on this request. This is done to keep renames fast and reliable.

I didn't implement this.

DidRenameFiles Notification

When a rename happened, all sessions who have the workspace.fileOperations.didRename capability will get notified.

predragnikolic avatar Jun 26 '24 18:06 predragnikolic

Here is what to expect from this PR.

  • There is a LSP: Rename File command in the command palette and ~LSP: Rename~ LSP: Rename File and LSP: Rename Folder in the sidebar.
  • Uses can rename file and folders. (folders can only be renamed from the sidebar).
  • When remaining a file user can rename a file example.py to hello.py, or to ./hello.py, or to ../../hello.py or to ./newDir/hello.py or to ./existingDir/hello.py. And it will work as expected.
  • The LSP: Rename File is always enabled, even if a session(with a 'workspace.fileOperations.willRename') doesn't exist. https://github.com/sublimelsp/LSP/blob/6f654623cbedfd7f4dada7269783861d68c5b344/plugin/rename_file.py#L41-L42

For session that don't support the 'workspace.fileOperations.willRename', we will still rename the file and notify all sessions that have 'workspace.fileOperations.didRename'

predragnikolic avatar Jun 26 '24 19:06 predragnikolic

The built-in rename_file and rename_path commands are both implemented in Python in Default/rename.py and Default/side_bar.py. I wonder wouldn't it be better to just replace these commands, like LSP already does with show_scope_name? Here we could add on_window_command in https://github.com/sublimelsp/LSP/blob/293f4a4340cca5ab1ad065643e4f20d9b270b2b1/boot.py#L209 to retarget the commands if a language server is running.

While for other features like document symbols or goto symbol, the similar built-in commands are not overridden because they can return different results as the LSP commands, so it might be useful to have both variants. But I think for the rename this is not necessary and to add two more LSP variants into the menus would not be optimal for usability. What do you think?

jwortmann avatar Jun 27 '24 10:06 jwortmann

@jwortmann I tried to implement your suggestion on a different branch, but I hit a bug with ST in which ST never emits a rename_file to on_window_command, although rename_file is a window command. It works fine for the rename_path command. My assumption is maybe because rename_file requires a TextInputHandler, but only ST core code can say whats missing. You can see my attempt here

predragnikolic avatar Jun 27 '24 13:06 predragnikolic

I will try a workaround https://github.com/sublimehq/sublime_text/issues/2234#issuecomment-411973845 EDIT: I can't apply the workaround, because the workaround needs to be added to Default/rename.py.

predragnikolic avatar Jun 27 '24 16:06 predragnikolic

@jwortmann I liked the idea, that way we do not introducing new commands, instead relying on existing ones. But because the on_window_command approach is buggy and needs a fix on ST side, I will quit from that idea now and stick with the approach in this PR. I've subscribed to the ST issue, so once that is fixed I could rethink the approach.

predragnikolic avatar Jun 27 '24 16:06 predragnikolic

Renaming a directory still shows LSP: Rename File in the command palette

Screenshot 2024-07-02 at 15 33 26

rchl avatar Jul 02 '24 13:07 rchl

Will this allow functionality like described in https://github.com/sublimelsp/LSP-intelephense/issues/112 that works in Vscode, where a symbol is mapped to a file name, so the file name would be automatically changed if the symbol is renamed?

willrowe avatar Aug 02 '24 14:08 willrowe

I believe no, this PR would only work in the other direction, i.e. if you rename a file, then the corresponding class name would be updated in that file and in other files.

For the functionality from the linked issue, we need to add support for WorkspaceEditClientCapabilities.resourceOperations.

https://github.com/sublimelsp/LSP/blob/f98334571229215ffed9d7a02b8a9ccfd9d4093a/plugin/core/edit.py#L19-L22

jwortmann avatar Aug 02 '24 14:08 jwortmann

@jwortmann thanks for that info.

willrowe avatar Aug 02 '24 16:08 willrowe

Deploy Preview for sublime-lsp ready!

Name Link
Latest commit 21f1bf7a10dc3bddc2f2a8c28fdde36f605fd666
Latest deploy log https://app.netlify.com/sites/sublime-lsp/deploys/66c4bad9a398f30008528d6a
Deploy Preview https://deploy-preview-2498--sublime-lsp.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

netlify[bot] avatar Aug 18 '24 10:08 netlify[bot]

I've noticed that renaming unsaved file is a bit broken because it triggers a dialog to either save or not the changes but at this point the file is already renamed. Then pressing save tries to save the file to the old location and not saving changes leaves the old view open.

I suppose that's related to not using view.retarget API.

rchl avatar Aug 21 '24 07:08 rchl

Another difference compared to native behavior is that if you try to rename a file that is not "fully open" (only previewed on right clicking in the sidebar) then native behavior will keep the renamed one in preview mode while LSP will open the file in non-preview mode.

That doesn't seem like much of an issue but there might be it might create some extra issues that I'm not seeing right now.

rchl avatar Aug 21 '24 07:08 rchl

Rafal thanks for catching this.

Currently the code closes and opens the file after rename. That will cause the save popup to appear if the view has unsaved changes. Trying to not close the view requires more complex logic, in order to handle renaming hello.ts -> hello.py or something else.

predragnikolic avatar Sep 05 '24 18:09 predragnikolic

🙈

predragnikolic avatar Nov 17 '24 20:11 predragnikolic

Will this get implemented then? 😄

niksy avatar Nov 17 '24 20:11 niksy

@predragnikolic is the last discussed issue something that you would consider the remaining blocker?

I no longer have clear recollection of the issues here but if something is not currently possible due to ST limitations then perhaps it would be good idea to make an upstream issue for what's missing.

rchl avatar Nov 18 '24 07:11 rchl

Will this get implemented then? 😄

I'm not sure :)

@predragnikolic is the last discussed issue something that you would consider the remaining blocker?

Yes.

It is possible to implement this, there are no ST blockers.

I've noticed that renaming unsaved file is a bit broken because it triggers a dialog to either save or not the changes but at this point the file is already renamed.

The easy way to solve the issues you reported, would be to save the file before closing the view. https://github.com/sublimelsp/LSP/pull/2498/files#diff-06bad17710895021d007aba395accf463493660accc8165970646eb9dbde7e0fR101

I don't know if it is expected that a file gets saved before the rename.

I haven't test if that would work in all 3 cases. Lets say a user has 2 language servers set up. (for Python and TypeScript) A user can rename:

  1. a.py -> b.py - Python ls should be still active, after the rename.
  2. a.py -> b.ts - Python should shut down, and TypeScript ls should start.
  3. a.py -> b.some-extension-where-lsp-is-not-running Python should shut down and no new LSP server should start.

The reason why I wanted to close and open the view is to avoid thinking if when to shutdown a LS and when to start a new one. I still think that is the easier way to do it, but I am not sure if it is the proper way to do it.

predragnikolic avatar Nov 18 '24 18:11 predragnikolic

The easy way to solve the issues you reported, would be to save the file before closing the view.

Sounds like a reasonable requirement to me. It may be lifted later when dedicated logic is added, but for the first implementation I would be fine with this.

FichteFoll avatar Feb 15 '25 13:02 FichteFoll

The view is saved before closing 0e0daa7

I did made some changes -> Introduce "LSP: Rename Path" and rename "LSP: Rename" to "LSP: Rename Symbol" to make things more explicit.

predragnikolic avatar Feb 17 '25 16:02 predragnikolic