-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Suggestion: refactor to "move to another (existing) file" #29988
Comments
Yes please. This is slowing me down trying to refactor an existing code base currently. Having to manually copy paste + edit imports means I don't do this when I should be. |
Use case: I work in a large Typescript codebase that does not enforce (e.g. via lint rules) anything about creating cyclic dependencies. We'd like to, but first we need to break the existing cycles! But over the years, there's one dependency cycle that has grown to include 500 files. Half of the work to break a cycle is really just moving definitions between files...so this feature would be super helpful! I'm sure this is harder than to move to a new file since now you'd need to check for name clashes (both the thing being moved and the imports that get moved), merge imports when they already exist on the destination file, etc. This looks kind of fun so I might take a look at it...some guidance would be appreciated though! Recommendations on approaches would be helpful. I'm sure there's a lot of other issues I didn't think of. @andy-ms |
It would be nice to have this feature! @RyanCavanaugh did you still need more feedback for this? |
Another use case: |
+1 IMO "Move to an existing file" is much more useful than merely creating a new file. |
Paging @andrewbranch is this difficult to implement? |
I'm not sure of the internals, but this would probably be tricky - to my knowledge there are no refactor/fixits which require users pick a file or provide some sort of input in order for the fixit to run. You might have to make changes to the TS types and have editors accept the new format ahead of getting this in. |
I find myself needing this far less often now, ever since I started using this VS Code extension: "Copy With Imports" https://marketplace.visualstudio.com/items?itemName=stringham.copy-with-imports |
Simple technique to go around this issue: Let's say you want to move the interface
This is relatively convoluted to get there but still way faster than manually updating everything because you still take advantage of VSCode automatically updating the imports. The step to rename to a unique name is only needed if you are moving a symbol with a relatively common name, say just "State". In that case, there will other symbols with this name and it's not safe to do a global search and replace. |
This is a valid point, I think it would be worth finding out if fixits can accept user inputs. Or if this could be a command instead? @laurent22 that solution is very convoluted is probably more easier/faster to use the extension mentioned above to just copy and paste then remap everything that fails. |
The extension just copy the imports to the destination file but it doesn't update references, so not sure how that solves the issue. |
This would be a very useful feature! As codebases grow, I find myself wanting to shift and move stuff around quite a bit. |
On a related note, I find it a bit annoying that the existing "Move to New File" refactoring guesses the name for you and doesn't pop up a dialog asking you. It uses the name of the class or function as its guess, which I think is reasonable, or as good as it can possibly do, but 9 times out of 10 I'd rather name it something else. So I think rather than creating a completely new refactoring option, I'd suggest renaming the existing one to "Move to Another File" and have it perform both functions. The way I envision it, it would pop up a dialog asking what file to move it to which would:
|
I use abracadabra extension that has option to move to existing files, it works fine, but sometimes it's not possible to invoke it (I did not investigated why tho) |
With the abracadabra extension, don't highlight the entire function - instead position the cursor on the function name, then click on the yellow lightbulb. |
I, too, would love this feature. I was a bit surprised/perplexed when I went to go do this action, and all that was in the Refactor menu was "Move to new file", and I couldn't find a way to do the more common task of "Move to existing file". (basically, you have a function, class, interface, etc. in one file, and you want it to be somewhere else :D) Happy to discuss possible implementation concepts, such as a path box or browse dialog, etc. I now have to go and move 17 functions by hand, and then somehow fix up their many import usages throughout my project. It could take a while :( -EDIT- lol yeah that extension did not work. First time trying to use it, got an error, will have to post an issue on their github :( "😅 I'm sorry, something went wrong: I can't build the AST from the source code. This may be due to a syntax error that you can fix. Here's what went wrong: Unexpected token (309:39)" |
Here's what the VS Code <-> TS Server flow could look like here. I've marked protocol changes/additions with 🚧:
During our sync today, we also noted that
|
Quick thoughts:
Open Questions:
|
I agree with all those thoughts. I don’t see a use case for ever moving something into node_modules. 1 step further would be using a .gitignore/ignore list if supplied but I guess there’s been no precedent of TSServer reading from this file up until now. I’d be ok with just node_modules being ignored as a starting point. Does TSServer already know the list of files or will this impact performance? For me the most common case has been somewhere else within the same directory, so having the “pre-populated” picker be the directory you’re already in would be a great start. For the sake of performance can it lazily traverse from there if the user requires it?
Do we know if any other language server offers this? I don’t know what the UI would look like for such a feature. A fuzzy search in VSCode would be useful over navigating in and out of directories for example
What’s the definition of a file belonging to multiple projects? Do you have an example?
I would expect this to “just work” like before. Is an explicit project required for this feature to work? I assume TS already makes many assumptions on inferred projects. |
Basically what the Pylance team has prototyped is that "move to..." would be a picker that allows a list of files, along with a top-level option to use the native file picker. The native file picker might allow a pre-populated directory and file name.
A file can be included by another file which belongs to a
If
These two are interconnected. Projects have the full file list. Inferred projects only look at open files. For unconfigured JavaScript projects, you'll often get an inferred project, which means this feature won't really work well if we rely on a fuzzy search of files pre-populated by TypeScript. |
@DanielRosenwasser I've updated my proposal for using quick pick to select the target file. Here's some drafts of the relevant protocol additions:
interface GetMoveToRefactoringFileSuggestionsRequest extends FileLocationOrRangeRequestArgs {
// Pass along the same arguments that we first passed to `GetApplicableRefactorsRequest`
}
interface GetMoveToRefactoringFileSuggestionsResponse extends Response {
body?: {
/// Suggested name if a new file is created
newFileName: string;
/// List of file paths that the selected text can be moved to
// TODO: should this be an object instead so that TS could customize how files are shown in the file picker UI?
files: string[];
};
}
interface GetEditsForMoveToFileRefactorRequest extends FileLocationOrRangeRequestArgs { {
// Pass along the same arguments that we first passed to `GetApplicableRefactorsRequest`
/// target file path that the user selected (may also be new file)
filePath: string;
}
interface GetEditsForRefactorResponse extends Response {
body?: RefactorEditInfo; // TODO: maybe use a new type
} |
Search Terms
Suggestion
The "move to new file" refactor is really helpful, but what if you want to move the code to an existing module?
This happens very often, when you realise code has been living in the wrong module, or it has a better home in some other module.
I would like to suggest a new refactor: "move to another (existing) file".
My workaround at the moment is to move the code manually, and then manually update all imports, but this is rather tedious!
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: