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) } }