Proposal: Destination Selection UX for swift.moveMember Refactoring in SourceKit-LSP

Hi everyone,

I’m currently working on adding support for a new refactoring, swift.moveMember, in SourceKit-LSP that allows moving a member (e.g. a method or property) from one type to another.

One open design question is around how the destination type should be selected by the user when this refactoring is invoked via an LSP client (such as VS Code).

Since SourceKit-LSP cannot present UI directly, the expected interaction is:

  1. The server provides a CodeAction that returns a command.

  2. The client executes a discovery command to request valid destination types.

  3. The client prompts the user to choose a destination.

  4. The client then calls workspace/executeCommand with the selected destination.

  5. The server computes and returns the final WorkspaceEdit.

The key question is: what should the list of destination types include?

A naïve approach would be to offer:

  • all types in the same file,

  • or all types in the same module,

  • or potentially all types in the indexed project.

However, this could easily result in dozens or even hundreds of options, which may not provide a good user experience and would likely require global symbol queries.

Instead, I’m considering limiting the destination list to structurally valid containers within the immediate lexical hierarchy, such as:

  • the enclosing type,

  • nested types,

  • extensions of the same nominal type (in the same file),

  • or moving between a primary declaration and its extensions.

This would align the refactoring more closely with the syntactic structure of the program and keep the number of destination options small and contextually relevant, while avoiding the need for project-wide searches.

I wanted to gather feedback on whether this more constrained, structure-driven approach would be appropriate for an initial implementation of swift.moveMember, or if a broader scope (e.g. file- or module-wide destinations) would be expected.

Any thoughts on the expected UX or scope for this kind of refactoring in SourceKit-LSP would be greatly appreciated.

Thanks!

CC @plemarquand

I think a key design decision here would be how the user would select the destination type. I.e. what kind of picker would be displayed to them. As far as I can tell, none of the current LSP requests would support that kind of selection, so we’d need to role our own UI in the client + our own request for it.

@amanmrya Do you know if there are other IDEs or LSP servers that offer a refactoring action like this? If so, how do they let the user pick where they want to move the member to?

I looked into a few popular LSP-backed implementations but didn’t find a strong precedent for this kind of refactoring flow exposed directly through LSP.

In IDEs that do support similar refactorings (e.g. Roslyn or IntelliJ), the destination is typically not selected from a project-wide list of types. Instead, moves are limited to structurally or semantically related classes (such as enclosing or locally reachable types), with the picker presenting a small, contextually relevant set of candidates.

For an initial implementation, I’m thinking of scoping this to allow moving methods between structurally related classes that are reachable within the same lexical context (e.g. enclosing or nested types in the same file), rather than attempting to support arbitrary project-wide destinations up front.

1 Like

If we can keep the list of destinations small (say typically less than 3 and rarely more than 5), we could just offer different code actions for the different destinations. Eg.

  • Move to superclass
  • Move to top-level scope

What do you think?

I like that approach, one code action per destination fits well with the small set of destinations we’re considering and avoids any custom picker.

On a related note: we could also add a code action that sorts members in a type (e.g. alphabetically by name, or grouped by kind/return type). That would stay within the same type and wouldn’t need destination selection, and it could complement “move member” for organizing type bodies.

I don’t think we need a code action to sort members. We recently laid out requirements that code actions should satisfy to be considered in sourcekit-lsp/Contributor Documentation/Code Action.md at main · swiftlang/sourcekit-lsp · GitHub and while sorting members alphabetically, I think it’s neither common nor hard to get right to do so.

Thank you for the closure. I will look into code actions that follow the new criteria.