//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 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 // //===----------------------------------------------------------------------===// public import LanguageServerProtocol /// The build target sources request is sent from the client to the server to /// query for the list of text documents and directories that belong to a /// build target. The sources response must not include sources that are /// external to the workspace. public struct BuildTargetSourcesRequest: RequestType, Hashable { public static let method: String = "buildTarget/sources" public typealias Response = BuildTargetSourcesResponse public var targets: [BuildTargetIdentifier] public init(targets: [BuildTargetIdentifier]) { self.targets = targets } } public struct BuildTargetSourcesResponse: ResponseType, Hashable { public var items: [SourcesItem] public init(items: [SourcesItem]) { self.items = items } } public struct SourcesItem: Codable, Hashable, Sendable { public var target: BuildTargetIdentifier /// The text documents and directories that belong to this build target. public var sources: [SourceItem] /// The root directories from where source files should be relativized. /// Example: ["file://Users/name/dev/metals/src/main/scala"] public var roots: [URI]? public init(target: BuildTargetIdentifier, sources: [SourceItem], roots: [URI]? = nil) { self.target = target self.sources = sources self.roots = roots } } public struct SourceItem: Codable, Hashable, Sendable { /// Either a text document or a directory. A directory entry must end with a /// forward slash "/" and a directory entry implies that every nested text /// document within the directory belongs to this source item. public var uri: URI /// Type of file of the source item, such as whether it is file or directory. public var kind: SourceItemKind /// Indicates if this source is automatically generated by the build and is /// not intended to be manually edited by the user. public var generated: Bool /// Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified. public var dataKind: SourceItemDataKind? /// Language-specific metadata about this source item. public var data: LSPAny? /// If `dataKind` is `sourceKit`, the `data` interpreted as `SourceKitSourceItemData`, otherwise `nil`. public var sourceKitData: SourceKitSourceItemData? { guard dataKind == .sourceKit else { return nil } return SourceKitSourceItemData(fromLSPAny: data) } public init( uri: URI, kind: SourceItemKind, generated: Bool, dataKind: SourceItemDataKind? = nil, data: LSPAny? = nil ) { self.uri = uri self.kind = kind self.generated = generated self.dataKind = dataKind self.data = data } } public enum SourceItemKind: Int, Codable, Hashable, Sendable { /// The source item references a normal file. case file = 1 /// The source item references a directory. case directory = 2 } public struct SourceItemDataKind: RawRepresentable, Codable, Hashable, Sendable { public var rawValue: String public init(rawValue: String) { self.rawValue = rawValue } /// `data` field must contain a JvmSourceItemData object. public static let jvm = SourceItemDataKind(rawValue: "jvm") /// `data` field must contain a `SourceKitSourceItemData` object. /// /// **(BSP Extension)** public static let sourceKit = SourceItemDataKind(rawValue: "sourceKit") } /// **(BSP Extension)** public enum SourceKitSourceItemKind: String, Codable { /// A source file that belongs to the target case source = "source" /// A header file that is clearly associated with one target. /// /// For example header files in SwiftPM projects are always associated to one target and SwiftPM can provide build /// settings for that header file. /// /// In general, build systems don't need to list all header files in the `buildTarget/sources` request: Semantic /// functionality for header files is usually provided by finding a main file that includes the header file and /// inferring build settings from it. Listing header files in `buildTarget/sources` allows SourceKit-LSP to provide /// semantic functionality for header files if they haven't been included by any main file. case header = "header" /// A SwiftDocC documentation catalog usually ending in the ".docc" extension. case doccCatalog = "doccCatalog" } public struct SourceKitSourceItemData: LSPAnyCodable, Codable { /// The language of the source file. If `nil`, the language is inferred from the file extension. public var language: Language? /// The kind of source file that this source item represents. If omitted, the item is assumed to be a normal source file, /// ie. omitting this key is equivalent to specifying it as `source`. public var kind: SourceKitSourceItemKind? /// The output path that is used during indexing for this file, ie. the `-index-unit-output-path`, if it is specified /// in the compiler arguments or the file that is passed as `-o`, if `-index-unit-output-path` is not specified. /// /// This allows SourceKit-LSP to remove index entries for source files that are removed from a target but remain /// present on disk. /// /// The server communicates during the initialize handshake whether it populates this property by setting /// `outputPathsProvider: true` in `SourceKitInitializeBuildResponseData`. public var outputPath: String? public init(language: Language? = nil, kind: SourceKitSourceItemKind? = nil, outputPath: String? = nil) { self.language = language self.kind = kind self.outputPath = outputPath } public init?(fromLSPDictionary dictionary: [String: LanguageServerProtocol.LSPAny]) { if case .string(let language) = dictionary[CodingKeys.language.stringValue] { self.language = Language(rawValue: language) } if case .string(let rawKind) = dictionary[CodingKeys.kind.stringValue] { self.kind = SourceKitSourceItemKind(rawValue: rawKind) } // Backwards compatibility for isHeader if case .bool(let isHeader) = dictionary["isHeader"], isHeader { self.kind = .header } if case .string(let outputFilePath) = dictionary[CodingKeys.outputPath.stringValue] { self.outputPath = outputFilePath } } public func encodeToLSPAny() -> LanguageServerProtocol.LSPAny { var result: [String: LSPAny] = [:] if let language { result[CodingKeys.language.stringValue] = .string(language.rawValue) } if let kind { result[CodingKeys.kind.stringValue] = .string(kind.rawValue) } if let outputPath { result[CodingKeys.outputPath.stringValue] = .string(outputPath) } return .dictionary(result) } }