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

Add willRename and didRename fileOperations #2498

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

predragnikolic
Copy link
Member

@predragnikolic predragnikolic commented Jun 25, 2024

closes #2199

Here is a example video:

Kapture.2024-06-25.at.18.24.36.mp4

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

example.zip

Side Bar.sublime-menu Outdated Show resolved Hide resolved
plugin/rename_file.py Outdated Show resolved Hide resolved
@predragnikolic
Copy link
Member Author

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:

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:

# LSP spec - Apply WorkspaceEdit before the files are renamed
if res:
session.apply_workspace_edit_async(res, is_refactoring=True)
self.rename_path(old_path, new_path)

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
Copy link
Member Author

predragnikolic commented Jun 26, 2024

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.
    def is_enabled(self):
    return True

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 predragnikolic marked this pull request as ready for review June 27, 2024 10:15
@jwortmann
Copy link
Member

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

LSP/boot.py

Line 209 in 293f4a4

class Listener(sublime_plugin.EventListener):
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?

open_file_uri(self.window, new_path).then(restore_regions)

def notify_did_rename(self, rename_file_params: RenameFilesParams):
sessions = [s for s in self.sessions() if s.has_capability('workspace.fileOperations.didRename')]
Copy link
Member Author

Choose a reason for hiding this comment

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

The server capability is defined as 'workspace.fileOperations.didRename':

interface ServerCapabilities {
  workspace: {
  //...
    fileOperations:  {
    // ...
    didRename?: FileOperationRegistrationOptions
  }
}

interface FileOperationRegistrationOptions {
	filters: FileOperationFilter[];
}

export interface FileOperationFilter {
	scheme?: string;
	pattern: FileOperationPattern
}

interface FileOperationPattern {
	glob: string;
	matches?: FileOperationPatternKind;
	options?: FileOperationPatternOptions;
}

To implement this properly I would need to respect the provided pattern in FileOperationRegistrationOptions,
but that seem like more work. The current code also works, at least with the server I am testing this feature.

Copy link
Member Author

Choose a reason for hiding this comment

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

So I am unsure if I need to implement FileOperationRegistrationOptions in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

I think you should try implementing that. filters is not an optional feature so we won't be complaint otherwise.

@predragnikolic
Copy link
Member Author

@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
Copy link
Member Author

predragnikolic commented Jun 27, 2024

I will try a workaround sublimehq/sublime_text#2234 (comment)
EDIT: I can't apply the workaround, because the workaround needs to be added to Default/rename.py.

@predragnikolic
Copy link
Member Author

@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.

…s from that folder to new location

so we do not lose changes

The ST default rename_path command doesn't do this
VS Code does this
LSP will do this
@rchl
Copy link
Member

rchl commented Jul 2, 2024

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

Screenshot 2024-07-02 at 15 33 26

plugin/core/types.py Outdated Show resolved Hide resolved
…ys LSP: Rename File

I cannot tweak the text inside the TextInputHandler
use show_input_panel instead...
Default.sublime-commands Outdated Show resolved Hide resolved
Side Bar.sublime-menu Outdated Show resolved Hide resolved
plugin/core/types.py Outdated Show resolved Hide resolved
plugin/core/types.py Outdated Show resolved Hide resolved
@willrowe
Copy link
Contributor

willrowe commented Aug 2, 2024

Will this allow functionality like described in sublimelsp/LSP-intelephense#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?

@jwortmann
Copy link
Member

jwortmann commented Aug 2, 2024

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.

LSP/plugin/core/edit.py

Lines 19 to 22 in f983345

if 'kind' in document_change:
# TODO: Support resource operations (create/rename/remove)
debug('Ignoring unsupported "resourceOperations" edit type')
continue

@willrowe
Copy link
Contributor

willrowe commented Aug 2, 2024

@jwortmann thanks for that info.

@predragnikolic predragnikolic marked this pull request as ready for review August 18, 2024 10:27
Copy link

netlify bot commented Aug 18, 2024

Deploy Preview for sublime-lsp ready!

Name Link
🔨 Latest commit 21f1bf7
🔍 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.

@rchl
Copy link
Member

rchl commented Aug 21, 2024

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
Copy link
Member

rchl commented Aug 21, 2024

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.

@predragnikolic
Copy link
Member Author

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.

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.

Support workspace/willRenameFiles
4 participants