Files

142 lines
5.4 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
/// Generates a markdown document for the configuration file based on the schema.
struct OptionDocumentBuilder {
static let preamble = """
<!-- DO NOT EDIT THIS FILE. This file is generated by \(#fileID). -->
# Configuration File
`.sourcekit-lsp/config.json` configuration files can be used to modify the behavior of SourceKit-LSP in various ways. The following locations are checked. Settings in later configuration files override settings in earlier configuration files
- `~/.sourcekit-lsp/config.json`
- On macOS: `~/Library/Application Support/org.swift.sourcekit-lsp/config.json` from the various `Library` folders on the system
- If the `XDG_CONFIG_HOME` environment variable is set: `$XDG_CONFIG_HOME/sourcekit-lsp/config.json`
- Initialization options passed in the initialize request
- A `.sourcekit-lsp/config.json` file in a workspaces root
Modifying the configuration file requires SourceKit-LSP to be restarted for the new options to take effect.
The structure of the file is currently not guaranteed to be stable. Options may be removed or renamed.
## Structure
`config.json` is a JSON file with the following structure. All keys are optional and unknown keys are ignored.
"""
let context: OptionSchemaContext
/// Builds a markdown document for the configuration file based on the schema.
func build(from schema: OptionTypeSchama) throws -> String {
var doc = Self.preamble
func appendProperty(_ property: OptionTypeSchama.Property, indentLevel: Int) throws {
let indent = String(repeating: " ", count: indentLevel)
let name = property.name
doc += "\(indent)- `\(name)"
let type = property.type
let typeDescription: String?
switch type.kind {
case .struct:
// Skip struct type as we describe its properties in the next level
typeDescription = nil
default:
typeDescription = Self.typeToDisplay(type)
}
if let typeDescription {
doc += ": \(typeDescription)`:"
} else {
doc += "`:"
}
if let description = property.description {
doc += " " + description.split(separator: "\n").joined(separator: "\n\(indent) ")
}
doc += "\n"
switch type.kind {
case .struct(let schema):
for property in schema.properties {
try appendProperty(property, indentLevel: indentLevel + 1)
}
case .enum(let schema):
let hasAssociatedTypes = schema.cases.contains {
$0.associatedProperties != nil && !$0.associatedProperties!.isEmpty
}
if hasAssociatedTypes {
let discriminatorFieldName = schema.discriminatorFieldName ?? "type"
doc +=
"\(indent) - This is a tagged union discriminated by the `\(discriminatorFieldName)` field. Each case has the following structure:\n"
for caseInfo in schema.cases {
doc += """
\(indent) - `\(discriminatorFieldName): "\(caseInfo.name)"`
"""
if let description = caseInfo.description {
doc += ": " + description.split(separator: "\n").joined(separator: "\n\(indent) ")
}
doc += "\n"
if let associatedProperties = caseInfo.associatedProperties {
for assocProp in associatedProperties {
try appendProperty(assocProp, indentLevel: indentLevel + 2)
}
}
}
} else {
for caseInfo in schema.cases {
guard let description = caseInfo.description else {
continue
}
doc += "\(indent) - `\(caseInfo.name)`"
doc += ": " + description.split(separator: "\n").joined(separator: "\n\(indent) ")
doc += "\n"
}
}
default: break
}
}
guard case .struct(let schema) = schema.kind else {
throw ConfigSchemaGenError("Root schema must be a struct")
}
for property in schema.properties {
try appendProperty(property, indentLevel: 0)
}
return doc
}
static func typeToDisplay(_ type: OptionTypeSchama, shouldWrap: Bool = false) -> String {
switch type.kind {
case .boolean: return "boolean"
case .integer: return "integer"
case .number: return "number"
case .string: return "string"
case .array(let value):
return "\(typeToDisplay(value, shouldWrap: true))[]"
case .dictionary(let value):
return "[string: \(typeToDisplay(value))]"
case .struct(let structInfo):
return structInfo.name
case .enum(let enumInfo):
let hasAssociatedTypes = enumInfo.cases.contains {
$0.associatedProperties != nil && !$0.associatedProperties!.isEmpty
}
if hasAssociatedTypes {
return "object"
} else {
let cases = enumInfo.cases.map { "\"\($0.name)\"" }.joined(separator: "|")
return shouldWrap ? "(\(cases))" : cases
}
}
}
}