mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2025-12-17 12:03:01 +01:00
134 lines
4.5 KiB
Swift
134 lines
4.5 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 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 {
|
|
public struct WritePlan {
|
|
public let category: String
|
|
public let path: URL
|
|
public let contents: () throws -> Data
|
|
|
|
public func write() throws {
|
|
try contents().write(to: path)
|
|
}
|
|
}
|
|
|
|
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 plans = try plan()
|
|
for plan in plans {
|
|
print("Writing \(plan.category) to \"\(plan.path.path)\"")
|
|
try plan.write()
|
|
}
|
|
}
|
|
|
|
public static func plan() throws -> [WritePlan] {
|
|
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.collect(from: 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)
|
|
}
|
|
)
|
|
)
|
|
|
|
var plans: [WritePlan] = []
|
|
|
|
plans.append(WritePlan(
|
|
category: "JSON Schema",
|
|
path: configSchemaJSONPath,
|
|
contents: { try generateJSONSchema(from: schema, context: context) }
|
|
))
|
|
|
|
plans.append(WritePlan(
|
|
category: "Schema Documentation",
|
|
path: configSchemaDocPath,
|
|
contents: { try generateDocumentation(from: schema, context: context) }
|
|
))
|
|
return plans
|
|
}
|
|
|
|
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 -> Data {
|
|
let docBuilder = OptionDocumentBuilder(context: context)
|
|
guard let data = try docBuilder.build(from: schema).data(using: .utf8) else {
|
|
throw ConfigSchemaGenError("Failed to encode documentation as UTF-8")
|
|
}
|
|
return data
|
|
}
|
|
}
|
|
|
|
struct ConfigSchemaGenError: Error, CustomStringConvertible {
|
|
let description: String
|
|
|
|
init(_ description: String) {
|
|
self.description = description
|
|
}
|
|
}
|