Files
swift-mirror/stdlib/tools/swift-lang/SourceKitdResponse.swift
Saleem Abdulrasool 200b96df81 Revert "Revert "swift-lang: shuffle the source directory structure (NFC)""
This reverts commit beb8ecd8cc.  Add a
workaround for the dependency issue.

It is unclear why `sourcekitd` is getting added improperly as a
dependency on `lib/sourcekitd.framework/sourcekitd`.  This workaround
adjusts the dependency such that we end up with a dependency on
`lib/sourcekitd.framework/Versions/A/sourcekitd` as an order-only
dependency.  This should fix the compile issue.  I am unable to
reproduce this issue with the `add_library` usage for adding a Swift
library.  This allows us to cleave the host and target libraries, and so
the workaround is sufficient to make progress and the problem will be
resolved with the migration towards CMake for handling the dependencies.
2020-02-19 16:09:37 -08:00

290 lines
9.5 KiB
Swift

//===--------------------- SourceKitdResponse.swift -----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
//
//===----------------------------------------------------------------------===//
// This file provides convenient APIs to interpret a SourceKitd response.
//===----------------------------------------------------------------------===//
import Foundation
import sourcekitd
public class SourceKitdResponse: CustomStringConvertible {
public struct Dictionary: CustomStringConvertible, CustomReflectable {
// The lifetime of this sourcekitd_variant_t is tied to the response it came
// from, so keep a reference to the response too.
private let dict: sourcekitd_variant_t
private let context: SourceKitdResponse
public init(dict: sourcekitd_variant_t, context: SourceKitdResponse) {
assert(sourcekitd_variant_get_type(dict).rawValue ==
SOURCEKITD_VARIANT_TYPE_DICTIONARY.rawValue)
self.dict = dict
self.context = context
}
public func getString(_ key: SourceKitdUID) -> String {
let value = sourcekitd_variant_dictionary_get_string(dict, key.uid)!
return String(cString: value)
}
public func getInt(_ key: SourceKitdUID) -> Int {
let value = sourcekitd_variant_dictionary_get_int64(dict, key.uid)
return Int(value)
}
public func getBool(_ key: SourceKitdUID) -> Bool {
let value = sourcekitd_variant_dictionary_get_bool(dict, key.uid)
return value
}
public func getUID(_ key: SourceKitdUID) -> SourceKitdUID {
let value = sourcekitd_variant_dictionary_get_uid(dict, key.uid)!
return SourceKitdUID(uid: value)
}
public func getArray(_ key: SourceKitdUID) -> Array {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
return Array(arr: value, context: context)
}
public func getDictionary(_ key: SourceKitdUID) -> Dictionary {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
return Dictionary(dict: value, context: context)
}
public func getData(_ key: SourceKitdUID) -> Data {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
let size = sourcekitd_variant_data_get_size(value)
guard let ptr = sourcekitd_variant_data_get_ptr(value), size > 0 else {
return Data()
}
return Data(bytes: ptr, count: size)
}
public func getOptional(_ key: SourceKitdUID) -> Variant? {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
if sourcekitd_variant_get_type(value).rawValue ==
SOURCEKITD_VARIANT_TYPE_NULL.rawValue {
return nil
}
return Variant(val: value, context: context)
}
public var description: String {
return dict.description
}
public var customMirror: Mirror {
return Mirror(self, children: [:])
}
}
public struct Array: CustomStringConvertible {
// The lifetime of this sourcekitd_variant_t is tied to the response it came
// from, so keep a reference to the response too.
private let arr: sourcekitd_variant_t
private let context: SourceKitdResponse
public var count: Int {
let count = sourcekitd_variant_array_get_count(arr)
return Int(count)
}
public init(arr: sourcekitd_variant_t, context: SourceKitdResponse) {
assert(sourcekitd_variant_get_type(arr).rawValue ==
SOURCEKITD_VARIANT_TYPE_ARRAY.rawValue)
self.arr = arr
self.context = context
}
public func getString(_ index: Int) -> String {
let value = sourcekitd_variant_array_get_string(arr, index)!
return String(cString: value)
}
public func getInt(_ index: Int) -> Int {
let value = sourcekitd_variant_array_get_int64(arr, index)
return Int(value)
}
public func getBool(_ index: Int) -> Bool {
let value = sourcekitd_variant_array_get_bool(arr, index)
return value
}
public func getUID(_ index: Int) -> SourceKitdUID {
let value = sourcekitd_variant_array_get_uid(arr, index)!
return SourceKitdUID(uid: value)
}
public func getArray(_ index: Int) -> Array {
let value = sourcekitd_variant_array_get_value(arr, index)
return Array(arr: value, context: context)
}
public func getDictionary(_ index: Int) -> Dictionary {
let value = sourcekitd_variant_array_get_value(arr, index)
return Dictionary(dict: value, context: context)
}
public func enumerate(_ applier: (_ index: Int, _ value: Variant) -> Bool) {
// The block passed to sourcekit_variant_array_apply() does not actually
// escape, it's synchronous and not called after returning.
let context = self.context
withoutActuallyEscaping(applier) { escapingApplier in
_ = sourcekitd_variant_array_apply(arr) { (index, elem) -> Bool in
return escapingApplier(Int(index), Variant(val: elem, context: context))
}
}
}
public var description: String {
return arr.description
}
}
public struct Variant: CustomStringConvertible {
// The lifetime of this sourcekitd_variant_t is tied to the response it came
// from, so keep a reference to the response too.
private let val: sourcekitd_variant_t
fileprivate let context: SourceKitdResponse
fileprivate init(val: sourcekitd_variant_t, context: SourceKitdResponse) {
self.val = val
self.context = context
}
public func getString() -> String {
let value = sourcekitd_variant_string_get_ptr(val)!
let length = sourcekitd_variant_string_get_length(val)
return fromCStringLen(value, length: length)!
}
public func getStringBuffer() -> UnsafeBufferPointer<Int8> {
return UnsafeBufferPointer(start: sourcekitd_variant_string_get_ptr(val),
count: sourcekitd_variant_string_get_length(val))
}
public func getInt() -> Int {
let value = sourcekitd_variant_int64_get_value(val)
return Int(value)
}
public func getBool() -> Bool {
let value = sourcekitd_variant_bool_get_value(val)
return value
}
public func getUID() -> SourceKitdUID {
let value = sourcekitd_variant_uid_get_value(val)!
return SourceKitdUID(uid:value)
}
public func getArray() -> Array {
return Array(arr: val, context: context)
}
public func getDictionary() -> Dictionary {
return Dictionary(dict: val, context: context)
}
public func getData() -> Data {
let size = sourcekitd_variant_data_get_size(val)
guard let ptr = sourcekitd_variant_data_get_ptr(val), size > 0 else {
return Data()
}
return Data(bytes: ptr, count: size)
}
public var description: String {
return val.description
}
}
private let resp: sourcekitd_response_t
public var value: Dictionary {
return Dictionary(dict: sourcekitd_response_get_value(resp), context: self)
}
/// Copies the raw bytes of the JSON description of this documentation item.
/// The caller is responsible for freeing the associated memory.
public func copyRawJSONDocumentation() -> UnsafeMutablePointer<Int8>? {
return sourcekitd_variant_json_description_copy(
sourcekitd_response_get_value(resp))
}
/// Whether or not this response represents an error.
public var isError: Bool {
return sourcekitd_response_is_error(resp)
}
/// Whether or not this response represents a notification.
public var isNotification: Bool {
return value.getOptional(.key_Notification) != nil
}
/// Whether or not this response represents a connection interruption error.
public var isConnectionInterruptionError: Bool {
return sourcekitd_response_is_error(resp) &&
sourcekitd_response_error_get_kind(resp) ==
SOURCEKITD_ERROR_CONNECTION_INTERRUPTED
}
/// Whether or not this response represents a compiler crash.
public var isCompilerCrash: Bool {
guard let notification = value.getOptional(.key_Notification)?.getUID()
else { return false }
return notification == .compilerCrashedNotification
}
/// If this is a document update notification, returns the name of the
/// document to which this update applies. Otherwise, returns `nil`.
public var documentUpdateNotificationDocumentName: String? {
let response = value
guard let notification = response.getOptional(.key_Notification)?.getUID(),
notification == .source_notification_editor_documentupdate
else { return nil }
return response.getOptional(.key_Name)?.getString()
}
public init(resp: sourcekitd_response_t) {
self.resp = resp
}
deinit {
sourcekitd_response_dispose(resp)
}
public var description: String {
let utf8Str = sourcekitd_response_description_copy(resp)!
let result = String(cString: utf8Str)
free(utf8Str)
return result
}
}
extension sourcekitd_variant_t: CustomStringConvertible {
public var description: String {
let utf8Str = sourcekitd_variant_description_copy(self)!
let result = String(cString: utf8Str)
free(utf8Str)
return result
}
}
private func fromCStringLen(_ ptr: UnsafePointer<Int8>, length: Int) -> String? {
return String(decoding: Array(UnsafeBufferPointer(start: ptr, count: length)).map {
UInt8(bitPattern: $0) }, as: UTF8.self)
}