Files
sourcekit-lsp/SourceKitLSPDevUtils/Sources/ConfigSchemaGen/ConfigSchemaGen.swift

88 lines
3.2 KiB
Swift

import Foundation
import SwiftParser
import SwiftSyntax
/// The main entry point for generating a JSON schema and Markdown documentation
/// for the SourceKit-LSP configuration file format
/// (`.sourcekit-lsp/config.json`) from the Swift type definitions in
/// `SKOptions` Swift module.
public struct ConfigSchemaGen {
static let projectRoot = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
static let sourceDir =
projectRoot
.appendingPathComponent("Sources")
.appendingPathComponent("SKOptions")
static let configSchemaJSONPath =
projectRoot
.appendingPathComponent("config.schema.json")
static let configSchemaDocPath =
projectRoot
.appendingPathComponent("Documentation")
.appendingPathComponent("Configuration File.md")
public static func generate() throws {
let sourceFiles = FileManager.default.enumerator(at: sourceDir, includingPropertiesForKeys: nil)!
let typeNameResolver = TypeDeclResolver()
for case let fileURL as URL in sourceFiles {
guard fileURL.pathExtension == "swift" else {
continue
}
let sourceText = try String(contentsOf: fileURL)
let sourceFile = Parser.parse(source: sourceText)
typeNameResolver.append(sourceFile)
}
let rootTypeDecl = try typeNameResolver.lookupType(fullyQualified: ["SourceKitLSPOptions"])
let context = OptionSchemaContext(typeNameResolver: typeNameResolver)
var schema = try context.buildSchema(from: rootTypeDecl)
// Manually annotate the logging level enum since LogLevel type exists
// outside of the SKOptions module
schema["logging"]?["level"]?.kind = .enum(
OptionTypeSchama.Enum(
name: "LogLevel",
cases: ["debug", "info", "default", "error", "fault"].map {
OptionTypeSchama.Case(name: $0)
}
)
)
schema["logging"]?["privacyLevel"]?.kind = .enum(
OptionTypeSchama.Enum(
name: "PrivacyLevel",
cases: ["sensitive", "private", "public"].map {
OptionTypeSchama.Case(name: $0)
}
)
)
print("Writing schema to \(configSchemaJSONPath.path)")
try generateJSONSchema(from: schema, context: context).write(to: configSchemaJSONPath)
print("Writing schema documentation to \(configSchemaDocPath.path)")
try generateDocumentation(from: schema, context: context).write(
to: configSchemaDocPath,
atomically: true,
encoding: .utf8
)
}
static func generateJSONSchema(from schema: OptionTypeSchama, context: OptionSchemaContext) throws -> Data {
let schemaBuilder = JSONSchemaBuilder(context: context)
var jsonSchema = try schemaBuilder.build(from: schema)
jsonSchema.title = "SourceKit-LSP Configuration"
jsonSchema.comment = "DO NOT EDIT THIS FILE. This file is generated by \(#fileID)."
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
return try encoder.encode(jsonSchema)
}
static func generateDocumentation(from schema: OptionTypeSchama, context: OptionSchemaContext) throws -> String {
let docBuilder = OptionDocumentBuilder(context: context)
return try docBuilder.build(from: schema)
}
}