Skip to content

Add refactoring action to convert stored to computed properties#2622

Open
ahoppen wants to merge 1 commit into
swiftlang:mainfrom
ahoppen:stored-to-computed
Open

Add refactoring action to convert stored to computed properties#2622
ahoppen wants to merge 1 commit into
swiftlang:mainfrom
ahoppen:stored-to-computed

Conversation

@ahoppen

@ahoppen ahoppen commented Apr 27, 2026

Copy link
Copy Markdown
Member

This is the first syntactic refactoring action that needs to perform a cursor info request on codeAction/resolve, so the majority of this PR is to add infrastructure for that.

Based on #2496.

WorkspaceEdit(
changes: [
uri: [
TextEdit(range: positions["1️⃣"]..<positions["3️⃣"], newText: "\n var x :Int{ 25 }")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@Padmashree06: It looks like the refactoring action in swift-syntax doesn’t produce correct whitespaces here (it should be x: Int, not x :Int). Would you be able to work on a fix for that in swift-syntax?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes sure! I will fix it!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@ahoppen I have fixed the whitespace error in swift-syntax.

@ahoppen

ahoppen commented Apr 27, 2026

Copy link
Copy Markdown
Member Author

@swift-ci Please test

return .resolveEditLazily
}
// Cursor info reports type as `_` if it cannot determine the type.
if let type = try? await scope.cursorInfo(at: scope.snapshot.position(of: identifier.position)).only?.typeName,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As I mentioned in #2588 (comment), I don't think we should allow arbitrary cursorInfo in textDocument/codeAction request.
If the client don't support codeAction/resolve for .edit, imo we should drop this action.


package func codeActionResolve(_ req: CodeActionResolveRequest) async throws -> CodeAction {
return req.codeAction
guard let data = UnresolvedCodeActionData(fromLSPAny: req.codeAction.data) else {

@rintaro rintaro May 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IMO the whole mechanism should be something as simple as:

protocol SyntaxCodeActionProvider: SendableMetatype {
  // Provider name. default impl: { String(describing: Self.self) }
  static var name: String

  static func codeActions(in scope: SyntaxCodeActionScope) -> [CodeAction]
  static func resolve(_ codeAction: CodeAction, in snapshot: DocumentSnapshot) -> CodeAction
}

struct UnresolvedCodeActionData: LSPAnyCodable {
  // Provider name generated the code action.
  var provider: String

  /// The document on which the code action should be applied.
  var document: VersionedTextDocumentIdentifier

  // Original `CodeAction.data` attached.
  var orignalData: LSPAny?
}
func codeAction(_ req: CodeActionRequest) -> CodeActionRequestResponse {
  let actions = [(provider: SyntaxCodeActionProvider.Type, action: CodeAction)] = ...

  return .codeActions(
    actions.map { item
      if shouldResolve(item.action) {
        var action = item.action
        action = UnresolvedCodeActionData(provider: provider.name, codeAction: action.data)
        return action
      } else {
        return item.action
      }
    }
  )
}

func codeActionResolve(_ req: CodeActionResolveRequest) -> CodeAction {
  var codeAction = req.codeAction 
  let data = UnresolvedCodeActionData(fromLSPAny: codeAction.data) else {
    return codeAction
  }
  let provider = getCodeActionProvider(name: data.provider)
  codeAction.data = data.originalData
  return provider.resolve(
    codeAction, in: getSnapshot(data.document) // Probably with some more context to request `cursorInfo`.
  ) 
}

Each SyntaxCodeActionProvider should be able to store abitrary LSPAny information necessary for resolving the action, as the CodeAction.data.

This is the first syntactic refactoring action that needs to perform a cursor info request on `codeAction/resolve`, so the majority of this PR is to add infrastructure for that.

Based on swiftlang#2496.

Co-Authored-By: Padmashree S S <padmashreess2006@gmail.com>
@ahoppen

ahoppen commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Finally got back to this and updated it to have two distinct code action + resolve phases. Ready for another review.

@ahoppen

ahoppen commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

swiftlang/swift-tools-protocols#72

@swift-ci Please test

}
let syntaxTree = await syntaxTreeManager.syntaxTree(for: snapshot)

// Replay the code action without resolve support. This should now resolve all properties.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment is not relevant anymore

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.

3 participants