-
Notifications
You must be signed in to change notification settings - Fork 11
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
Feat: Rename imports #119
Feat: Rename imports #119
Conversation
Notes:
|
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 a bunch of pyright and mypy issues in those changes.
rename_imports.py
Outdated
if len(queued_events) == 2: | ||
first_event, new_name = queued_events[0] | ||
second_event, old_name = queued_events[1] | ||
if (first_event == 'create' and second_event == 'delete'): |
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'm afraid that there is not really a reliable way to detect renames. It could be just two unrelated files being added and removed (for example when changing branches).
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 that chokidar has a way to detect a rename, but that is not the case.
there is a different lib, that supports rename detection => paulmillr/chokidar#303 (comment)
For the time being, I would rely on this logic:
if len(self.events) == 2:
create_event = next((event for event in self.events if event[0] == 'create'), None)
delete_event = next((event for event in self.events if event[0] == 'delete'), None)
if (create_event and delete_event):
# Rename detected
new_name = create_event[1]
old_name = delete_event[1]
self.prompt_rename(old_name, new_name)
If we have 2 events,
and when we have a create and delete event,
only then prompt the rename....
It could be just two unrelated files being added and removed (for example when changing branches).
While than can happen,
I do not know how to better handle that at the moment.
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 that chokidar has a way to detect a rename, but that is not the case.
there is a different lib, that supports rename detection => paulmillr/chokidar#303 (comment)
Interesting watcher. At least if we are to trust the author's claims.
I wouldn't want to switch to using it as that's too much work and the claims would have to be tested in practice also but we could take inspiration from it and implement support for rename events in our watcher.
I do not know how to better to handle that than at the moment.
Me neither that's why I'm suggesting to remove the potentially dangerous option to rename without confirmation.
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.
there is a different lib, that supports rename detection => paulmillr/chokidar#303 (comment)
BTW. The code style of that project is... interesting.
- Blank lines between (almost) every line of code
- excessive use of spaces (leading/trailing spaces within parenthesis for example)
https://github.com/fabiospampinato/watcher/blob/master/src/watcher.ts
It would not be fun to contribute to such project :)
plugin.py
Outdated
if workspace_folders: | ||
register_rename_import_file_watcher(workspace_folders[0].path) |
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 done for every folder?
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 plan to add support form multiple folders,
I just need more time to think about how to handle the cleanup logic (when one folder closes, and so on)
I was thinking of having a Map, where the key is the root_path
and the value is a RenameImportHandler
instance.
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.
How would you know if/when the folders have changed?
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.
Multiple folders are semi supported.
The current code already supports sublime project.
When having a ST project with multiple folders:
// example.sublime-project
{
"folders":
[
{
"path": "/home/predragnikolic/Documents/sandbox/my-react-ts-app"
},
{
"path": "/home/predragnikolic/Documents/sandbox/coc-tsserver"
},
{
"path": "/home/predragnikolic/Documents/sandbox/volar"
},
{
"path": "/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript"
}
]
}
It works until the moment you try to add/remove folders to a project while ST is open. :)
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 current code only takes the path of the first folder.
if len(self.events) == 2: | ||
create_event = next((event for event in self.events if event[0] == 'create'), None) | ||
delete_event = next((event for event in self.events if event[0] == 'delete'), None) |
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.
This seems very fragile and implementation dependent. The logic should just be: if there are two sibling events where one is 'create' and one is 'delete' then trigger the rename.
Same for the timeout that calls this method. There is no guarantee that both events will come within 10ms and if they don't, we'll clear the list and miss the whole rename.
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.
And to clarify, I meant without checking if there are exactly 2 events as that is not guaranteed.
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 logic should just be: if there are two sibling events where one is 'create' and one is 'delete' then trigger the rename.
We cannot rely on sibling events. :)
Here are the logs when renaming multiple files at once on Linux, .
the folder structure is the following:
├── test
│ ├── file1.ts
│ ├── file2.ts
│ └── file3.ts
if we rename multiple files at once,
we will have the following events logged:
events [('delete', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file1.ts')]
events [('delete', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file2.ts')]
events [('delete', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file3.ts')]
events [('create', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file11.ts')]
events [('create', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file21.ts')]
events [('create', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file31.ts')]
So to jump to my point is,
we cannot rely on two sibilling events,
as that would result in incorrect behavior.
In the above example, it would mean to rename file3.ts
to file11.ts
, which is not correct.
events [('delete', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file3.ts')]
events [('create', '/home/predragnikolic/.config/sublime-text/Packages/LSP-typescript/test/file11.ts')]
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.
In that case it seems like we need more sophisticated solution. Something similar to what is mentioned in the link you've posted before (paulmillr/chokidar#303 (comment))
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 also can imagine that 3 renames in a row would not be handled well by the quick panel that we are triggering.
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 intentionally didn't want to support multiple file renames at once. only because I do not want to spam the user will prompts.
I only wanted to cover the case when a user renames a single file.
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.
In that case it seems like we need more sophisticated solution. Something similar to what is mentioned in the link you've posted before ...
Will improve the logic by checkin the iNode
number of a file. :)
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.
well,
I tried to use os.stat(file_name).st_ino
to get the ino number for a file.
But I quickly run into problems.
Here was my soloution:
events_with_ino = [(event[0], event[1], os.stat(event[1]).st_ino) for event in events]
# ^^^^^^^^ event_type
# ^^^^^^^^ file_name
# ^^^^^^^^^^^^^^^^^^^^^^^^ ino number for that file_name
I tried to attach an ino number for a file_name,
but this works only for files that exist.
That said os.stat(event[1]).st_ino will work only for create
or change
events,
but not for when the event type is delete
.
Because by the time we receive the delete event in python,
the file no longer exists,
so calling os.stat(file_name_that_no_longer_exist).st_ino
will throw.
Ideally I would shift this logic to be the responsibility of the file_watcher,
so we have an interface like this:
https://github.com/neoclide/coc-tsserver/blob/master/src/server/features/updatePathOnRename.ts#L33
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.
Supporting this in the watcher is the way to go but not sure if we would be able to do it in LSP-file-watcher-chokidar
. Might need to be implemented upstream in chokidar
instead.
instead register it on_ready, that way I avoid the confusion I had with self/cls
…enameOfImportPath
super().__init__(*args, **kwargs) | ||
|
||
def register_rename_import_file_watcher(self, session: Session) -> None: | ||
allow_rename_of_import_path = session.config.init_options.get('preferences.allowRenameOfImportPath') | ||
workspace_folders = session.get_workspace_folders() |
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.
This variable is only needed with the if file_watcher
condition.
Quite a lot of work was put into it but ultimately I don't think that this is the way to go. It would not be fine to trigger disrupting rename prompts whenever something would be renamed. Renames should only be handled by the server when user explicitly asks to rename some file so we need LSP-specific rename actions instead. Maybe if there would be a way to offer the user suggestion to update imports in a non-distracting way then it would be more acceptable but it might be better to just only do that on explicit request to rename. |
Agreed, I will close this PR. I saw the willRename PR on the ts server repo. |
When we rename or move a file,
the user is prompted to rename all imports.
This PR also adds a new setting
updateImportsOnFileMove
with the following options"prompt", "always", "never"
.