1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
//===----------------------------------------------------------------------===//
//
// 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 LanguageServerProtocol
protocol ReferenceURLData {
static var documentType: String { get }
var displayName: String { get }
var queryItems: [URLQueryItem] { get }
}
/// A Reference Document is a document whose url scheme is `sourcekit-lsp:` and whose content can only be retrieved
/// using `GetReferenceDocumentRequest`. The enum represents a specific type of reference document and its
/// associated value represents the data necessary to generate the document's contents and its url
///
/// The `url` will be of the form: `sourcekit-lsp://<document-type>/<display-name>?<parameters>`
/// Here,
/// - The `<document-type>` denotes the kind of the content present in the reference document
/// - The `<parameters>` denotes the parameter-value pairs such as "p1=v1&p2=v2&..." needed to generate
/// the content of the reference document.
/// - The `<display-name>` is the displayed file name of the reference document. It doesn't involve in generating
/// the content of the reference document.
package enum ReferenceDocumentURL {
package static let scheme = "sourcekit-lsp"
case macroExpansion(MacroExpansionReferenceDocumentURLData)
case generatedInterface(GeneratedInterfaceDocumentURLData)
var url: URL {
get throws {
let data: ReferenceURLData =
switch self {
case .macroExpansion(let data): data
case .generatedInterface(let data): data
}
var components = URLComponents()
components.scheme = Self.scheme
components.host = type(of: data).documentType
components.path = "/\(data.displayName)"
components.queryItems = data.queryItems
guard let url = components.url else {
throw ReferenceDocumentURLError(description: "Unable to create URL for reference document")
}
return url
}
}
var uri: DocumentURI {
get throws {
DocumentURI(try url)
}
}
init(from uri: DocumentURI) throws {
try self.init(from: uri.arbitrarySchemeURL)
}
init(from url: URL) throws {
guard url.scheme == Self.scheme else {
throw ReferenceDocumentURLError(description: "Invalid Scheme for reference document")
}
let documentType = url.host
switch documentType {
case MacroExpansionReferenceDocumentURLData.documentType:
guard let queryItems = URLComponents(string: url.absoluteString)?.queryItems else {
throw ReferenceDocumentURLError(
description: "No queryItems passed for macro expansion reference document: \(url)"
)
}
let macroExpansionURLData = try MacroExpansionReferenceDocumentURLData(
displayName: url.lastPathComponent,
queryItems: queryItems
)
self = .macroExpansion(macroExpansionURLData)
case GeneratedInterfaceDocumentURLData.documentType:
guard let queryItems = URLComponents(string: url.absoluteString)?.queryItems else {
throw ReferenceDocumentURLError(
description: "No queryItems passed for generated interface reference document: \(url)"
)
}
let macroExpansionURLData = try GeneratedInterfaceDocumentURLData(queryItems: queryItems)
self = .generatedInterface(macroExpansionURLData)
case nil:
throw ReferenceDocumentURLError(
description: "Bad URL for reference document: \(url)"
)
case let documentType?:
throw ReferenceDocumentURLError(
description: "Invalid document type in URL for reference document: \(documentType)"
)
}
}
/// The path that should be passed as `keys.sourcefile` to sourcekitd in conjunction with a `keys.primaryFile`.
///
/// For macro expansions, this is the buffer name that the URI references.
var sourcekitdSourceFile: String {
switch self {
case .macroExpansion(let data): return data.bufferName
case .generatedInterface(let data): return data.sourcekitdDocumentName
}
}
/// The file that should be used to retrieve build settings for this reference document.
var buildSettingsFile: DocumentURI {
switch self {
case .macroExpansion(let data): return data.primaryFile
case .generatedInterface(let data): return data.buildSettingsFrom
}
}
var primaryFile: DocumentURI? {
switch self {
case .macroExpansion(let data): return data.primaryFile
case .generatedInterface(let data): return data.buildSettingsFrom.primaryFile
}
}
}
extension DocumentURI {
/// The path that should be passed as `keys.sourcefile` to sourcekitd in conjunction with a `keys.primaryFile`.
///
/// For normal document URIs, this is the pseudo path of this URI. For macro expansions, this is the buffer name
/// that the URI references.
var sourcekitdSourceFile: String {
if let referenceDocument = try? ReferenceDocumentURL(from: self) {
referenceDocument.sourcekitdSourceFile
} else {
self.pseudoPath
}
}
/// If this is a URI to a reference document, the URI of the source file from which this reference document was
/// derived.
///
/// The primary file is used to determine the workspace and language service that is used to generate the reference
/// document as well as getting the reference document's build settings.
var primaryFile: DocumentURI? {
if let referenceDocument = try? ReferenceDocumentURL(from: self) {
return referenceDocument.primaryFile
}
return nil
}
/// The file that should be used to retrieve build settings for this reference document.
var buildSettingsFile: DocumentURI {
if let referenceDocument = try? ReferenceDocumentURL(from: self) {
return referenceDocument.buildSettingsFile
}
return self
}
}
package struct ReferenceDocumentURLError: Error, CustomStringConvertible {
package var description: String
init(description: String) {
self.description = description
}
}
|