Files
sourcekit-lsp/Sources/SourceKitLSP/Swift/CodeActions/ConvertIntegerLiteral.swift
Doug Gregor a8b61a52d3 Introduce new refactoring code actions based on the Swift syntax tree.
This change includes a number of new refactoring code actions that
build on the syntax refactorings for the SwiftRefactor module of swift-syntax:

  * Add digit separators to an integer literal, e.g., `1000000` ->
    `1_000_000`.
  * Remove digit separators from an integer literal, e.g., 1_000_000 ->
    1000000.
  * Format a raw string literal, e.g., `"Hello \#(world)"` ->
    `##"Hello\#(world)"##`
  * Migrate to new if let syntax, e.g., `if let x = x { ... }` ->
    `if let x { ... }`
  * Replace opaque parameters with generic parameters, e.g.,
    `func f(p: some P)` --> `func f<T1: P>(p: T1)`.

This is generally easy to do, requiring one conformance to provide a name for the refactoring:

    extension AddSeparatorsToIntegerLiteral: SyntaxRefactoringCodeActionProvider {
      public static var title: String { "Add digit separators" }
    }
2024-04-16 23:00:20 -07:00

77 lines
2.2 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import LanguageServerProtocol
import SwiftRefactor
import SwiftSyntax
// TODO: Make the type IntegerLiteralExprSyntax.Radix conform to CaseEnumerable
// in swift-syntax.
extension IntegerLiteralExprSyntax.Radix {
static var allCases: [Self] = [.binary, .octal, .decimal, .hex]
}
/// Syntactic code action provider to convert integer literals between
/// different bases.
struct ConvertIntegerLiteral: SyntaxCodeActionProvider {
static func codeActions(in scope: SyntaxCodeActionScope) -> [CodeAction] {
guard
let token = scope.firstToken,
let integerExpr = token.parent?.as(IntegerLiteralExprSyntax.self),
let integerValue = Int(
integerExpr.split().value.filter { $0 != "_" },
radix: integerExpr.radix.size
)
else {
return []
}
var actions = [CodeAction]()
let currentRadix = integerExpr.radix
for radix in IntegerLiteralExprSyntax.Radix.allCases {
guard radix != currentRadix else {
continue
}
//TODO: Add this to swift-syntax?
let prefix: String
switch radix {
case .binary:
prefix = "0b"
case .octal:
prefix = "0o"
case .hex:
prefix = "0x"
case .decimal:
prefix = ""
}
let convertedValue: ExprSyntax =
"\(raw: prefix)\(raw: String(integerValue, radix: radix.size))"
let edit = TextEdit(
range: scope.snapshot.range(of: integerExpr),
newText: convertedValue.description
)
actions.append(
CodeAction(
title: "Convert \(integerExpr) to \(convertedValue)",
kind: .refactorInline,
edit: WorkspaceEdit(changes: [scope.snapshot.uri: [edit]])
)
)
}
return actions
}
}