From 841efd64dea40485bab899abe58ba521757b4d1d Mon Sep 17 00:00:00 2001 From: Norio Nomura Date: Wed, 7 Feb 2018 15:59:15 +0900 Subject: [PATCH 1/6] Add `SourceKitObject` that represents `sourcekitd_object_t` in Swift Fix #487 --- CHANGELOG.md | 11 +- Source/SourceKittenFramework/File.swift | 8 +- Source/SourceKittenFramework/Request.swift | 165 ++++++++---------- .../SourceKitObject.swift | 132 ++++++++++++++ Source/SourceKittenFramework/SwiftDocs.swift | 2 +- Source/SourceKittenFramework/UID.swift | 56 ++++++ Tests/LinuxMain.swift | 1 + .../SourceKitObjectTests.swift | 38 ++++ sourcekitten.xcodeproj/project.pbxproj | 12 ++ 9 files changed, 327 insertions(+), 98 deletions(-) create mode 100644 Source/SourceKittenFramework/SourceKitObject.swift create mode 100644 Source/SourceKittenFramework/UID.swift create mode 100644 Tests/SourceKittenFrameworkTests/SourceKitObjectTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 204f80651..58c4d75cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,19 @@ ##### Breaking -* None. +* Change type of parameter from `sourcekitd_object_t` to `SourceKitObject?`. + - `File.process(dictionary:cursorInfoRequest:syntaxMap:)` + - `Request.customRequest(request:)` + - `SwiftDocs.init(file:dictionary:cursorInfoRequest:)` + + [Norio Nomura](https://github.com/norio-nomura) ##### Enhancements +* Add `SourceKitObject` that represents `sourcekitd_object_t` in Swift. + [Norio Nomura](https://github.com/norio-nomura) + [#489](https://github.com/jpsim/SourceKitten/issues/489) + * Replaced linear index search with binary search in NSString extension. [Tamas Lustyik](https://github.com/lvsti) diff --git a/Source/SourceKittenFramework/File.swift b/Source/SourceKittenFramework/File.swift index 242cabe0f..8f821adfd 100644 --- a/Source/SourceKittenFramework/File.swift +++ b/Source/SourceKittenFramework/File.swift @@ -191,7 +191,7 @@ public final class File { - parameter dictionary: Dictionary to process. - parameter cursorInfoRequest: Cursor.Info request to get declaration information. */ - public func process(dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t? = nil, + public func process(dictionary: [String: SourceKitRepresentable], cursorInfoRequest: SourceKitObject? = nil, syntaxMap: SyntaxMap? = nil) -> [String: SourceKitRepresentable] { var dictionary = dictionary if let cursorInfoRequest = cursorInfoRequest { @@ -233,7 +233,7 @@ public final class File { - parameter cursorInfoRequest: Cursor.Info request to get declaration information. */ internal func furtherProcess(dictionary: [String: SourceKitRepresentable], documentedTokenOffsets: [Int], - cursorInfoRequest: sourcekitd_object_t, + cursorInfoRequest: SourceKitObject, syntaxMap: SyntaxMap) -> [String: SourceKitRepresentable] { var dictionary = dictionary let offsetMap = makeOffsetMap(documentedTokenOffsets: documentedTokenOffsets, dictionary: dictionary) @@ -261,7 +261,7 @@ public final class File { `processDictionary(_:cursorInfoRequest:syntaxMap:)` on its elements, only keeping comment marks and declarations. */ - private func newSubstructure(_ dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t?, + private func newSubstructure(_ dictionary: [String: SourceKitRepresentable], cursorInfoRequest: SourceKitObject?, syntaxMap: SyntaxMap?) -> [SourceKitRepresentable]? { return SwiftDocKey.getSubstructure(dictionary)? .filter(isDeclarationOrCommentMark) @@ -277,7 +277,7 @@ public final class File { - parameter cursorInfoRequest: Cursor.Info request to get declaration information. */ private func dictWithCommentMarkNamesCursorInfo(_ dictionary: [String: SourceKitRepresentable], - cursorInfoRequest: sourcekitd_object_t) -> [String: SourceKitRepresentable]? { + cursorInfoRequest: SourceKitObject) -> [String: SourceKitRepresentable]? { guard let kind = SwiftDocKey.getKind(dictionary) else { return nil } diff --git a/Source/SourceKittenFramework/Request.swift b/Source/SourceKittenFramework/Request.swift index 53ccc821a..6e22c7995 100644 --- a/Source/SourceKittenFramework/Request.swift +++ b/Source/SourceKittenFramework/Request.swift @@ -196,7 +196,7 @@ public enum Request { /// A `cursorinfo` request for an offset in the given file, using the `arguments` given. case cursorInfo(file: String, offset: Int64, arguments: [String]) /// A custom request by passing in the sourcekitd_object_t directly. - case customRequest(request: sourcekitd_object_t) + case customRequest(request: SourceKitObject) /// A request generated by sourcekit using the yaml representation. case yamlRequest(yaml: String) /// A `codecomplete` request by passing in the file name, contents, offset @@ -217,45 +217,42 @@ public enum Request { /// A documentation request for the given module. case moduleInfo(module: String, arguments: [String]) - fileprivate var sourcekitObject: sourcekitd_object_t { - let dict: [sourcekitd_uid_t: sourcekitd_object_t?] + fileprivate var sourcekitObject: SourceKitObject { switch self { case .editorOpen(let file): if let path = file.path { - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.open")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(path), - sourcekitd_uid_get_from_cstr("key.sourcefile")!: sourcekitd_request_string_create(path) + return [ + "key.request": UID("source.request.editor.open"), + "key.name": path, + "key.sourcefile": path ] } else { - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.open")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(String(file.contents.hash)), - sourcekitd_uid_get_from_cstr("key.sourcetext")!: sourcekitd_request_string_create(file.contents) + return [ + "key.request": UID("source.request.editor.open"), + "key.name": String(file.contents.hash), + "key.sourcetext": file.contents ] } case .cursorInfo(let file, let offset, let arguments): - var compilerargs = arguments.map({ sourcekitd_request_string_create($0) }) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.cursorinfo")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.sourcefile")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.offset")!: sourcekitd_request_int64_create(offset), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count) + return [ + "key.request": UID("source.request.cursorinfo"), + "key.name": file, + "key.sourcefile": file, + "key.offset": offset, + "key.compilerargs": arguments ] case .customRequest(let request): return request case .yamlRequest(let yaml): - return sourcekitd_request_create_from_yaml(yaml, nil)! + return .init(sourcekitd_request_create_from_yaml(yaml, nil)!) case .codeCompletionRequest(let file, let contents, let offset, let arguments): - var compilerargs = arguments.map({ sourcekitd_request_string_create($0) }) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.codecomplete")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.sourcefile")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.sourcetext")!: sourcekitd_request_string_create(contents), - sourcekitd_uid_get_from_cstr("key.offset")!: sourcekitd_request_int64_create(offset), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count) + return [ + "key.request": UID("source.request.codecomplete"), + "key.name": file, + "key.sourcefile": file, + "key.sourcetext": contents, + "key.offset": offset, + "key.compilerargs": arguments ] case .interface(let file, let uuid, var arguments): if !arguments.contains("-x") { @@ -264,70 +261,58 @@ public enum Request { if !arguments.contains("-isysroot") { arguments.append(contentsOf: ["-isysroot", sdkPath()]) } - var compilerargs = ([file] + arguments).map({ sourcekitd_request_string_create($0) }) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: - sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.open.interface.header")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(uuid), - sourcekitd_uid_get_from_cstr("key.filepath")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count) + return [ + "key.request": UID("source.request.editor.open.interface.header"), + "key.name": uuid, + "key.filepath": file, + "key.compilerargs": [file] + arguments ] case .findUSR(let file, let usr): - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.find_usr")!), - sourcekitd_uid_get_from_cstr("key.usr")!: sourcekitd_request_string_create(usr), - sourcekitd_uid_get_from_cstr("key.sourcefile")!: sourcekitd_request_string_create(file) + return [ + "key.request": UID("source.request.editor.find_usr"), + "key.usr": usr, + "key.sourcefile": file ] case .index(let file, let arguments): - var compilerargs = arguments.map({ sourcekitd_request_string_create($0) }) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.indexsource")!), - sourcekitd_uid_get_from_cstr("key.sourcefile")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count) + return [ + "key.request": UID("source.request.indexsource"), + "key.sourcefile": file, + "key.compilerargs": arguments ] case .format(let file, let line, let useTabs, let indentWidth): - let formatOptions = [ - sourcekitd_uid_get_from_cstr("key.editor.format.indentwidth")!: sourcekitd_request_int64_create(indentWidth), - sourcekitd_uid_get_from_cstr("key.editor.format.tabwidth")!: sourcekitd_request_int64_create(indentWidth), - sourcekitd_uid_get_from_cstr("key.editor.format.usetabs")!: sourcekitd_request_int64_create(useTabs ? 1 : 0) - ] - var formatOptionsKeys = Array(formatOptions.keys.map({ $0 as sourcekitd_uid_t? })) - var formatOptionsValues = Array(formatOptions.values) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.formattext")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.line")!: sourcekitd_request_int64_create(line), - sourcekitd_uid_get_from_cstr("key.editor.format.options")!: - sourcekitd_request_dictionary_create(&formatOptionsKeys, &formatOptionsValues, formatOptions.count) + return [ + "key.request": UID("source.request.editor.formattext"), + "key.name": file, + "key.line": line, + "key.editor.format.options": [ + "key.editor.format.indentwidth": indentWidth, + "key.editor.format.tabwidth": indentWidth, + "key.editor.format.usetabs": useTabs ? 1 : 0 + ] ] case .replaceText(let file, let offset, let length, let sourceText): - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.replacetext")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(file), - sourcekitd_uid_get_from_cstr("key.offset")!: sourcekitd_request_int64_create(offset), - sourcekitd_uid_get_from_cstr("key.length")!: sourcekitd_request_int64_create(length), - sourcekitd_uid_get_from_cstr("key.sourcetext")!: sourcekitd_request_string_create(sourceText) + return [ + "key.request": UID("source.request.editor.replacetext"), + "key.name": file, + "key.offset": offset, + "key.length": length, + "key.sourcetext": sourceText ] case .docInfo(let text, let arguments): - var compilerargs = arguments.map({ sourcekitd_request_string_create($0) }) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.docinfo")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(NSUUID().uuidString), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count), - sourcekitd_uid_get_from_cstr("key.sourcetext")!: sourcekitd_request_string_create(text) + return [ + "key.request": UID("source.request.docinfo"), + "key.name": NSUUID().uuidString, + "key.compilerargs": arguments, + "key.sourcetext": text ] case .moduleInfo(let module, let arguments): - var compilerargs = arguments.map({ sourcekitd_request_string_create($0) }) - dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.docinfo")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(NSUUID().uuidString), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count), - sourcekitd_uid_get_from_cstr("key.modulename")!: sourcekitd_request_string_create(module) + return [ + "key.request": UID("source.request.docinfo"), + "key.name": NSUUID().uuidString, + "key.compilerargs": arguments, + "key.modulename": module ] } - var keys = Array(dict.keys.map({ $0 as sourcekitd_uid_t? })) - var values = Array(dict.values) - return sourcekitd_request_dictionary_create(&keys, &values, dict.count)! } /** @@ -338,7 +323,7 @@ public enum Request { - returns: sourcekitd_object_t representation of the Request, if successful. */ - internal static func cursorInfoRequest(filePath: String?, arguments: [String]) -> sourcekitd_object_t? { + internal static func cursorInfoRequest(filePath: String?, arguments: [String]) -> SourceKitObject? { if let path = filePath { return Request.cursorInfo(file: path, offset: 0, arguments: arguments).sourcekitObject } @@ -353,11 +338,11 @@ public enum Request { - returns: SourceKit response if successful. */ - internal static func send(cursorInfoRequest: sourcekitd_object_t, atOffset offset: Int64) -> [String: SourceKitRepresentable]? { + internal static func send(cursorInfoRequest: SourceKitObject, atOffset offset: Int64) -> [String: SourceKitRepresentable]? { if offset == 0 { return nil } - sourcekitd_request_dictionary_set_int64(cursorInfoRequest, sourcekitd_uid_get_from_cstr(SwiftDocKey.offset.rawValue)!, offset) + cursorInfoRequest.updateValue(offset, forKey: SwiftDocKey.offset) return try? Request.customRequest(request: cursorInfoRequest).send() } @@ -369,7 +354,7 @@ public enum Request { */ public func send() throws -> [String: SourceKitRepresentable] { initializeSourceKitFailable - let response = sourcekitd_send_request_sync(sourcekitObject) + let response = sourcekitd_send_request_sync(sourcekitObject.sourceKitObject!) defer { sourcekitd_response_dispose(response!) } if sourcekitd_response_is_error(response!) { let error = Request.Error(response: response!) @@ -432,20 +417,16 @@ public enum Request { extension Request: CustomStringConvertible { /// A textual representation of `Request`. - public var description: String { return String(validatingUTF8: sourcekitd_request_description_copy(sourcekitObject)!)! } + public var description: String { return sourcekitObject.description } } private func interfaceForModule(_ module: String, compilerArguments: [String]) throws -> [String: SourceKitRepresentable] { - var compilerargs = compilerArguments.map { sourcekitd_request_string_create($0) } - let dict = [ - sourcekitd_uid_get_from_cstr("key.request")!: sourcekitd_request_uid_create(sourcekitd_uid_get_from_cstr("source.request.editor.open.interface")!), - sourcekitd_uid_get_from_cstr("key.name")!: sourcekitd_request_string_create(NSUUID().uuidString), - sourcekitd_uid_get_from_cstr("key.compilerargs")!: sourcekitd_request_array_create(&compilerargs, compilerargs.count), - sourcekitd_uid_get_from_cstr("key.modulename")!: sourcekitd_request_string_create("SourceKittenFramework.\(module)") - ] - var keys = Array(dict.keys.map({ $0 as sourcekitd_uid_t? })) - var values = Array(dict.values) - return try Request.customRequest(request: sourcekitd_request_dictionary_create(&keys, &values, dict.count)!).send() + return try Request.customRequest(request: [ + "key.request": UID("source.request.editor.open.interface"), + "key.name": NSUUID().uuidString, + "key.compilerargs": compilerArguments, + "key.modulename": "SourceKittenFramework.\(module)" + ]).send() } extension String { diff --git a/Source/SourceKittenFramework/SourceKitObject.swift b/Source/SourceKittenFramework/SourceKitObject.swift new file mode 100644 index 000000000..f29b39560 --- /dev/null +++ b/Source/SourceKittenFramework/SourceKitObject.swift @@ -0,0 +1,132 @@ +// +// SourceKitObject.swift +// SourceKitten +// +// Created by Norio Nomura on 2/7/18. +// Copyright © 2018 SourceKitten. All rights reserved. +// + +import Foundation +#if SWIFT_PACKAGE + import SourceKit +#endif + +// MARK: - SourceKitObjectConvertible +public protocol SourceKitObjectConvertible { + var sourceKitObject: sourcekitd_object_t? { get } +} + +extension Array: SourceKitObjectConvertible { + public var sourceKitObject: sourcekitd_object_t? { + guard Element.self is SourceKitObjectConvertible.Type else { + fatalError("Array confirms to SourceKitObjectConvertible when Elements is SourceKitObjectConvertible!") + } + let objects: [sourcekitd_object_t?] = map { ($0 as! SourceKitObjectConvertible).sourceKitObject } + return sourcekitd_request_array_create(objects, objects.count) + } +} + +extension Dictionary: SourceKitObjectConvertible { + public var sourceKitObject: sourcekitd_object_t? { + let keys: [sourcekitd_uid_t?] + if Key.self is UID.Type { + keys = self.keys.map { ($0 as! UID).uid } + } else if Key.self is String.Type { + keys = self.keys.map { UID($0 as! String).uid } + } else { + fatalError("Dictionary confirms to SourceKitObjectConvertible when `Key` is `UID` or `String`!") + } + guard Value.self is SourceKitObjectConvertible.Type else { + fatalError("Dictionary confirms to SourceKitObjectConvertible when `Value` is `SourceKitObjectConvertible`!") + } + let values: [sourcekitd_object_t?] = self.map { ($0.value as! SourceKitObjectConvertible).sourceKitObject } + return sourcekitd_request_dictionary_create(keys, values, count) + } +} + +extension Int: SourceKitObjectConvertible { + public var sourceKitObject: sourcekitd_object_t? { + return sourcekitd_request_int64_create(Int64(self)) + } +} + +extension Int64: SourceKitObjectConvertible { + public var sourceKitObject: sourcekitd_object_t? { + return sourcekitd_request_int64_create(self) + } +} + +extension String: SourceKitObjectConvertible { + public var sourceKitObject: sourcekitd_object_t? { + return sourcekitd_request_string_create(self) + } +} + +// MARK: - SourceKitObject + +/// Swift representation of sourcekitd_object_t +public struct SourceKitObject { + public let sourceKitObject: sourcekitd_object_t? + + public init(_ sourceKitObject: sourcekitd_object_t) { + self.sourceKitObject = sourceKitObject + } + + /// Updates the value stored in the dictionary for the given key, + /// or adds a new key-value pair if the key does not exist. + /// + /// - Parameters: + /// - value: The new value to add to the dictionary. + /// - key: The key to associate with value. If key already exists in the dictionary, + /// value replaces the existing associated value. If key isn't already a key of the dictionary + public func updateValue(_ value: SourceKitObjectConvertible, forKey key: UID) { + precondition(sourceKitObject != nil) + precondition(value.sourceKitObject != nil) + sourcekitd_request_dictionary_set_value(sourceKitObject!, key.uid, value.sourceKitObject!) + } + + public func updateValue(_ value: SourceKitObjectConvertible, forKey key: String) { + updateValue(value, forKey: UID(key)) + } + + public func updateValue(_ value: SourceKitObjectConvertible, forKey key: T) where T: RawRepresentable, T.RawValue == String { + updateValue(value, forKey: UID(key.rawValue)) + } +} + +extension SourceKitObject: SourceKitObjectConvertible {} + +extension SourceKitObject: CustomStringConvertible { + public var description: String { + guard let object = sourceKitObject else { return "" } + let bytes = sourcekitd_request_description_copy(object)! + let length = Int(strlen(bytes)) + return String(bytesNoCopy: bytes, length: length, encoding: .utf8, freeWhenDone: true)! + } +} + +extension SourceKitObject: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: SourceKitObject...) { + sourceKitObject = elements.sourceKitObject + } +} + +extension SourceKitObject: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (UID, SourceKitObjectConvertible)...) { + let keys: [sourcekitd_uid_t?] = elements.map { $0.0.uid } + let values: [sourcekitd_object_t?] = elements.map { $0.1.sourceKitObject } + sourceKitObject = sourcekitd_request_dictionary_create(keys, values, elements.count) + } +} + +extension SourceKitObject: ExpressibleByIntegerLiteral { + public init(integerLiteral value: IntegerLiteralType) { + sourceKitObject = value.sourceKitObject + } +} + +extension SourceKitObject: ExpressibleByStringLiteral { + public init(stringLiteral value: StringLiteralType) { + sourceKitObject = value.sourceKitObject + } +} diff --git a/Source/SourceKittenFramework/SwiftDocs.swift b/Source/SourceKittenFramework/SwiftDocs.swift index 87afc45a0..fbc736c14 100644 --- a/Source/SourceKittenFramework/SwiftDocs.swift +++ b/Source/SourceKittenFramework/SwiftDocs.swift @@ -47,7 +47,7 @@ public struct SwiftDocs { - parameter dictionary: editor.open response from SourceKit. - parameter cursorInfoRequest: SourceKit dictionary to use to send cursorinfo request. */ - public init(file: File, dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t?) { + public init(file: File, dictionary: [String: SourceKitRepresentable], cursorInfoRequest: SourceKitObject?) { self.file = file var dictionary = dictionary let syntaxMapData = dictionary.removeValue(forKey: SwiftDocKey.syntaxMap.rawValue) as! [SourceKitRepresentable] diff --git a/Source/SourceKittenFramework/UID.swift b/Source/SourceKittenFramework/UID.swift new file mode 100644 index 000000000..2c29cb845 --- /dev/null +++ b/Source/SourceKittenFramework/UID.swift @@ -0,0 +1,56 @@ +// +// UID.swift +// SourceKitten +// +// Created by Norio Nomura on 2/07/18. +// Copyright © 2018 SourceKitten. All rights reserved. +// + +import Foundation +#if SWIFT_PACKAGE + import SourceKit +#endif + +/// Swift representation of sourcekitd_uid_t +public struct UID { + let uid: sourcekitd_uid_t + init(_ uid: sourcekitd_uid_t) { + self.uid = uid + } + + public init(_ string: String) { + self.init(sourcekitd_uid_get_from_cstr(string)!) + } + + var string: String { + return String(cString: sourcekitd_uid_get_string_ptr(uid)!) + } +} + +extension UID: CustomStringConvertible { + public var description: String { + return string + } +} + +extension UID: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(value) + } +} + +extension UID: Hashable { + public var hashValue: Int { + return uid.hashValue + } + + public static func == (lhs: UID, rhs: UID) -> Bool { + return lhs.uid == rhs.uid + } +} + +extension UID: SourceKitObjectConvertible { + public var sourceKitObject: sourcekitd_object_t? { + return sourcekitd_request_uid_create(uid) + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 7e18fd776..24c35c3cb 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -8,6 +8,7 @@ XCTMain([ testCase(FileTests.allTests), testCase(ModuleTests.allTests), testCase(OffsetMapTests.allTests), + testCase(SourceKitObjectTests.allTests), testCase(SourceKitTests.allTests), testCase(StringTests.allTests), testCase(StructureTests.allTests), diff --git a/Tests/SourceKittenFrameworkTests/SourceKitObjectTests.swift b/Tests/SourceKittenFrameworkTests/SourceKitObjectTests.swift new file mode 100644 index 000000000..4687e705d --- /dev/null +++ b/Tests/SourceKittenFrameworkTests/SourceKitObjectTests.swift @@ -0,0 +1,38 @@ +// +// SourceKitObjectTests.swift +// SourceKitten +// +// Created by Norio Nomura on 2/7/18. +// Copyright © 2018 SourceKitten. All rights reserved. +// + +import SourceKittenFramework +import XCTest + +class SourceKitObjectTests: XCTestCase { + + func testExample() { + let path = #file + let object: SourceKitObject = [ + "key.request": UID("source.request.editor.open"), + "key.name": path, + "key.sourcefile": path + ] + let expected = """ + { + key.request: source.request.editor.open, + key.name: \"\(#file)\", + key.sourcefile: \"\(#file)\" + } + """ + XCTAssertEqual(object.description, expected) + } +} + +extension SourceKitObjectTests { + static var allTests: [(String, (SourceKitObjectTests) -> () throws -> Void)] { + return [ + ("testExample", testExample) + ] + } +} diff --git a/sourcekitten.xcodeproj/project.pbxproj b/sourcekitten.xcodeproj/project.pbxproj index faa903140..f1eed515b 100644 --- a/sourcekitten.xcodeproj/project.pbxproj +++ b/sourcekitten.xcodeproj/project.pbxproj @@ -18,6 +18,9 @@ 6C4CF6521C798082008532C5 /* library_wrapper_CXString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4CF6481C79802A008532C5 /* library_wrapper_CXString.swift */; }; 6C4CF6551C798086008532C5 /* library_wrapper_Documentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4CF6491C79802A008532C5 /* library_wrapper_Documentation.swift */; }; 6C4CF6581C79808C008532C5 /* library_wrapper_Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4CF6471C79802A008532C5 /* library_wrapper_Index.swift */; }; + 6CB68C23202AC40B00D82E91 /* SourceKitObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB68C22202AC40B00D82E91 /* SourceKitObjectTests.swift */; }; + 6CC1639C202AA3AF0086C459 /* SourceKitObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC1639A202AA3AE0086C459 /* SourceKitObject.swift */; }; + 6CC1639D202AA3AF0086C459 /* UID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC1639B202AA3AE0086C459 /* UID.swift */; }; 6CC381641ECACB6F000C6F81 /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC381621ECACB50000C6F81 /* Version.swift */; }; 6CCFCE891CFECFED003239EB /* SWXMLHash.framework in Embed Frameworks into SourceKittenFramework.framework */ = {isa = PBXBuildFile; fileRef = E8C0DFCC1AD349DB007EE3D4 /* SWXMLHash.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 6CCFCE8C1CFECFF1003239EB /* Yams.framework in Embed Frameworks into SourceKittenFramework.framework */ = {isa = PBXBuildFile; fileRef = E80678041CF2749300AFC816 /* Yams.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -143,6 +146,9 @@ 6C4CF6481C79802A008532C5 /* library_wrapper_CXString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = library_wrapper_CXString.swift; sourceTree = ""; }; 6C4CF6491C79802A008532C5 /* library_wrapper_Documentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = library_wrapper_Documentation.swift; sourceTree = ""; }; 6C7F0A841EDB0AAD008EC581 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; + 6CB68C22202AC40B00D82E91 /* SourceKitObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceKitObjectTests.swift; sourceTree = ""; }; + 6CC1639A202AA3AE0086C459 /* SourceKitObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceKitObject.swift; sourceTree = ""; }; + 6CC1639B202AA3AE0086C459 /* UID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UID.swift; sourceTree = ""; }; 6CC381621ECACB50000C6F81 /* Version.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = ""; }; 6CFC18F01C7F2FB900CD70E1 /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; B2FA804AA9D4427FF571EFB2 /* SwiftLangSyntax.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftLangSyntax.swift; sourceTree = ""; }; @@ -408,6 +414,7 @@ E806D2901BE058C400D1BE41 /* Parameter.swift */, E86847391A587B4D0043DC65 /* Request.swift */, E8A9B8911B56D1B100CD17D4 /* SourceDeclaration.swift */, + 6CC1639A202AA3AE0086C459 /* SourceKitObject.swift */, D0D1217119E87B05005E4BAA /* SourceKittenFramework.h */, E806D28C1BE0589B00D1BE41 /* SourceLocation.swift */, E8AE53C61A5B5FCA0092D24A /* String+SourceKitten.swift */, @@ -420,6 +427,7 @@ E8CC8A2C1A587FD300D1FEC7 /* SyntaxMap.swift */, E80F236A1A5CB04100FD2352 /* SyntaxToken.swift */, E806D28E1BE058B100D1BE41 /* Text.swift */, + 6CC1639B202AA3AE0086C459 /* UID.swift */, 6CC381621ECACB50000C6F81 /* Version.swift */, E8A9B88F1B56CB5500CD17D4 /* Xcode.swift */, B2FA804AA9D4427FF571EFB2 /* SwiftLangSyntax.swift */, @@ -451,6 +459,7 @@ E805A0491B560FCA00EA654A /* FileTests.swift */, E8241CA21A5E01840047687E /* ModuleTests.swift */, E8C9EA091A5C9A2900A6D4D1 /* OffsetMapTests.swift */, + 6CB68C22202AC40B00D82E91 /* SourceKitObjectTests.swift */, E805A0471B55CBAF00EA654A /* SourceKitTests.swift */, E8C9EA071A5C99C400A6D4D1 /* StringTests.swift */, E8C9EA031A5C986A00A6D4D1 /* StructureTests.swift */, @@ -661,6 +670,7 @@ files = ( E82882541DAEEDD1002E0564 /* LinuxCompatibility.swift in Sources */, E806D2931BE058D600D1BE41 /* Documentation.swift in Sources */, + 6CC1639D202AA3AF0086C459 /* UID.swift in Sources */, 3F0CBB411BAAFF160015BBA8 /* Clang+SourceKitten.swift in Sources */, 2E8FF7101C6268C100F280F0 /* StatementKind.swift in Sources */, 6C4CF6551C798086008532C5 /* library_wrapper_Documentation.swift in Sources */, @@ -673,6 +683,7 @@ 3F56EAD01BAB251C006433D0 /* JSONOutput.swift in Sources */, E8A18A3B1A58971D000362B7 /* Language.swift in Sources */, E806D28F1BE058B100D1BE41 /* Text.swift in Sources */, + 6CC1639C202AA3AF0086C459 /* SourceKitObject.swift in Sources */, E8241CA51A5E01A10047687E /* Module.swift in Sources */, E877D9271B5693E70095BB2B /* ObjCDeclarationKind.swift in Sources */, 6C4CF6521C798082008532C5 /* library_wrapper_CXString.swift in Sources */, @@ -704,6 +715,7 @@ E845EFEC1B9941AA00CFA57B /* CodeCompletionTests.swift in Sources */, E805A04A1B560FCA00EA654A /* FileTests.swift in Sources */, E8241CA31A5E01840047687E /* ModuleTests.swift in Sources */, + 6CB68C23202AC40B00D82E91 /* SourceKitObjectTests.swift in Sources */, 3DEF4C591DBF9C2D00B3B54A /* DocInfoTests.swift in Sources */, E8C9EA0A1A5C9A2900A6D4D1 /* OffsetMapTests.swift in Sources */, E805A0481B55CBAF00EA654A /* SourceKitTests.swift in Sources */, From 73ec05c615be75ba8e1b4c5963afc9ebf7eddc2a Mon Sep 17 00:00:00 2001 From: Norio Nomura Date: Wed, 7 Feb 2018 16:32:06 +0900 Subject: [PATCH 2/6] Add `UID.init(_:)` taking `RawRepresentable` of `String` --- Source/SourceKittenFramework/UID.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/SourceKittenFramework/UID.swift b/Source/SourceKittenFramework/UID.swift index 2c29cb845..960c90576 100644 --- a/Source/SourceKittenFramework/UID.swift +++ b/Source/SourceKittenFramework/UID.swift @@ -22,6 +22,10 @@ public struct UID { self.init(sourcekitd_uid_get_from_cstr(string)!) } + public init(_ rawRepresentable: T) where T: RawRepresentable, T.RawValue == String { + self.init(rawRepresentable.rawValue) + } + var string: String { return String(cString: sourcekitd_uid_get_string_ptr(uid)!) } From e3747184ed751d9531eb895efea41ba46f8988e0 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Tue, 6 Mar 2018 23:08:06 -0800 Subject: [PATCH 3/6] Rename SourceKitObjectConvertible.sourceKitObject member to sourcekitdObject to avoid confusion between 'SourceKitObject's and sourcekitd "objects". --- Source/SourceKittenFramework/Request.swift | 2 +- .../SourceKitObject.swift | 41 ++++++++++--------- Source/SourceKittenFramework/UID.swift | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/Source/SourceKittenFramework/Request.swift b/Source/SourceKittenFramework/Request.swift index 6e22c7995..aace9a7f4 100644 --- a/Source/SourceKittenFramework/Request.swift +++ b/Source/SourceKittenFramework/Request.swift @@ -354,7 +354,7 @@ public enum Request { */ public func send() throws -> [String: SourceKitRepresentable] { initializeSourceKitFailable - let response = sourcekitd_send_request_sync(sourcekitObject.sourceKitObject!) + let response = sourcekitd_send_request_sync(sourcekitObject.sourcekitdObject!) defer { sourcekitd_response_dispose(response!) } if sourcekitd_response_is_error(response!) { let error = Request.Error(response: response!) diff --git a/Source/SourceKittenFramework/SourceKitObject.swift b/Source/SourceKittenFramework/SourceKitObject.swift index f29b39560..a249c368a 100644 --- a/Source/SourceKittenFramework/SourceKitObject.swift +++ b/Source/SourceKittenFramework/SourceKitObject.swift @@ -12,22 +12,23 @@ import Foundation #endif // MARK: - SourceKitObjectConvertible + public protocol SourceKitObjectConvertible { - var sourceKitObject: sourcekitd_object_t? { get } + var sourcekitdObject: sourcekitd_object_t? { get } } extension Array: SourceKitObjectConvertible { - public var sourceKitObject: sourcekitd_object_t? { + public var sourcekitdObject: sourcekitd_object_t? { guard Element.self is SourceKitObjectConvertible.Type else { fatalError("Array confirms to SourceKitObjectConvertible when Elements is SourceKitObjectConvertible!") } - let objects: [sourcekitd_object_t?] = map { ($0 as! SourceKitObjectConvertible).sourceKitObject } + let objects: [sourcekitd_object_t?] = map { ($0 as! SourceKitObjectConvertible).sourcekitdObject } return sourcekitd_request_array_create(objects, objects.count) } } extension Dictionary: SourceKitObjectConvertible { - public var sourceKitObject: sourcekitd_object_t? { + public var sourcekitdObject: sourcekitd_object_t? { let keys: [sourcekitd_uid_t?] if Key.self is UID.Type { keys = self.keys.map { ($0 as! UID).uid } @@ -39,25 +40,25 @@ extension Dictionary: SourceKitObjectConvertible { guard Value.self is SourceKitObjectConvertible.Type else { fatalError("Dictionary confirms to SourceKitObjectConvertible when `Value` is `SourceKitObjectConvertible`!") } - let values: [sourcekitd_object_t?] = self.map { ($0.value as! SourceKitObjectConvertible).sourceKitObject } + let values: [sourcekitd_object_t?] = self.map { ($0.value as! SourceKitObjectConvertible).sourcekitdObject } return sourcekitd_request_dictionary_create(keys, values, count) } } extension Int: SourceKitObjectConvertible { - public var sourceKitObject: sourcekitd_object_t? { + public var sourcekitdObject: sourcekitd_object_t? { return sourcekitd_request_int64_create(Int64(self)) } } extension Int64: SourceKitObjectConvertible { - public var sourceKitObject: sourcekitd_object_t? { + public var sourcekitdObject: sourcekitd_object_t? { return sourcekitd_request_int64_create(self) } } extension String: SourceKitObjectConvertible { - public var sourceKitObject: sourcekitd_object_t? { + public var sourcekitdObject: sourcekitd_object_t? { return sourcekitd_request_string_create(self) } } @@ -66,10 +67,10 @@ extension String: SourceKitObjectConvertible { /// Swift representation of sourcekitd_object_t public struct SourceKitObject { - public let sourceKitObject: sourcekitd_object_t? + public let sourcekitdObject: sourcekitd_object_t? - public init(_ sourceKitObject: sourcekitd_object_t) { - self.sourceKitObject = sourceKitObject + public init(_ sourcekitdObject: sourcekitd_object_t) { + self.sourcekitdObject = sourcekitdObject } /// Updates the value stored in the dictionary for the given key, @@ -80,9 +81,9 @@ public struct SourceKitObject { /// - key: The key to associate with value. If key already exists in the dictionary, /// value replaces the existing associated value. If key isn't already a key of the dictionary public func updateValue(_ value: SourceKitObjectConvertible, forKey key: UID) { - precondition(sourceKitObject != nil) - precondition(value.sourceKitObject != nil) - sourcekitd_request_dictionary_set_value(sourceKitObject!, key.uid, value.sourceKitObject!) + precondition(sourcekitdObject != nil) + precondition(value.sourcekitdObject != nil) + sourcekitd_request_dictionary_set_value(sourcekitdObject!, key.uid, value.sourcekitdObject!) } public func updateValue(_ value: SourceKitObjectConvertible, forKey key: String) { @@ -98,7 +99,7 @@ extension SourceKitObject: SourceKitObjectConvertible {} extension SourceKitObject: CustomStringConvertible { public var description: String { - guard let object = sourceKitObject else { return "" } + guard let object = sourcekitdObject else { return "" } let bytes = sourcekitd_request_description_copy(object)! let length = Int(strlen(bytes)) return String(bytesNoCopy: bytes, length: length, encoding: .utf8, freeWhenDone: true)! @@ -107,26 +108,26 @@ extension SourceKitObject: CustomStringConvertible { extension SourceKitObject: ExpressibleByArrayLiteral { public init(arrayLiteral elements: SourceKitObject...) { - sourceKitObject = elements.sourceKitObject + sourcekitdObject = elements.sourcekitdObject } } extension SourceKitObject: ExpressibleByDictionaryLiteral { public init(dictionaryLiteral elements: (UID, SourceKitObjectConvertible)...) { let keys: [sourcekitd_uid_t?] = elements.map { $0.0.uid } - let values: [sourcekitd_object_t?] = elements.map { $0.1.sourceKitObject } - sourceKitObject = sourcekitd_request_dictionary_create(keys, values, elements.count) + let values: [sourcekitd_object_t?] = elements.map { $0.1.sourcekitdObject } + sourcekitdObject = sourcekitd_request_dictionary_create(keys, values, elements.count) } } extension SourceKitObject: ExpressibleByIntegerLiteral { public init(integerLiteral value: IntegerLiteralType) { - sourceKitObject = value.sourceKitObject + sourcekitdObject = value.sourcekitdObject } } extension SourceKitObject: ExpressibleByStringLiteral { public init(stringLiteral value: StringLiteralType) { - sourceKitObject = value.sourceKitObject + sourcekitdObject = value.sourcekitdObject } } diff --git a/Source/SourceKittenFramework/UID.swift b/Source/SourceKittenFramework/UID.swift index 960c90576..e78016e23 100644 --- a/Source/SourceKittenFramework/UID.swift +++ b/Source/SourceKittenFramework/UID.swift @@ -54,7 +54,7 @@ extension UID: Hashable { } extension UID: SourceKitObjectConvertible { - public var sourceKitObject: sourcekitd_object_t? { + public var sourcekitdObject: sourcekitd_object_t? { return sourcekitd_request_uid_create(uid) } } From 9af4f4f613279a94865055efe96b450959730089 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Tue, 6 Mar 2018 23:10:09 -0800 Subject: [PATCH 4/6] changelog formatting --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58c4d75cb..86a440958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,9 @@ ##### Breaking * Change type of parameter from `sourcekitd_object_t` to `SourceKitObject?`. - - `File.process(dictionary:cursorInfoRequest:syntaxMap:)` + - `File.process(dictionary:cursorInfoRequest:syntaxMap:)` - `Request.customRequest(request:)` - - `SwiftDocs.init(file:dictionary:cursorInfoRequest:)` - + - `SwiftDocs.init(file:dictionary:cursorInfoRequest:)` [Norio Nomura](https://github.com/norio-nomura) ##### Enhancements From b182a2391baca54530dde63e92de90e089eaa898 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Tue, 6 Mar 2018 23:12:32 -0800 Subject: [PATCH 5/6] Fix typos --- Source/SourceKittenFramework/SourceKitObject.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/SourceKittenFramework/SourceKitObject.swift b/Source/SourceKittenFramework/SourceKitObject.swift index a249c368a..05c024815 100644 --- a/Source/SourceKittenFramework/SourceKitObject.swift +++ b/Source/SourceKittenFramework/SourceKitObject.swift @@ -20,7 +20,7 @@ public protocol SourceKitObjectConvertible { extension Array: SourceKitObjectConvertible { public var sourcekitdObject: sourcekitd_object_t? { guard Element.self is SourceKitObjectConvertible.Type else { - fatalError("Array confirms to SourceKitObjectConvertible when Elements is SourceKitObjectConvertible!") + fatalError("Array conforms to SourceKitObjectConvertible when Elements is SourceKitObjectConvertible!") } let objects: [sourcekitd_object_t?] = map { ($0 as! SourceKitObjectConvertible).sourcekitdObject } return sourcekitd_request_array_create(objects, objects.count) @@ -35,10 +35,10 @@ extension Dictionary: SourceKitObjectConvertible { } else if Key.self is String.Type { keys = self.keys.map { UID($0 as! String).uid } } else { - fatalError("Dictionary confirms to SourceKitObjectConvertible when `Key` is `UID` or `String`!") + fatalError("Dictionary conforms to SourceKitObjectConvertible when `Key` is `UID` or `String`!") } guard Value.self is SourceKitObjectConvertible.Type else { - fatalError("Dictionary confirms to SourceKitObjectConvertible when `Value` is `SourceKitObjectConvertible`!") + fatalError("Dictionary conforms to SourceKitObjectConvertible when `Value` is `SourceKitObjectConvertible`!") } let values: [sourcekitd_object_t?] = self.map { ($0.value as! SourceKitObjectConvertible).sourcekitdObject } return sourcekitd_request_dictionary_create(keys, values, count) From 651cc1609d478588fee87479bb26faa6704fbfb4 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Tue, 6 Mar 2018 23:18:28 -0800 Subject: [PATCH 6/6] Minor fixes --- Source/SourceKittenFramework/Request.swift | 2 +- Source/SourceKittenFramework/SourceKitObject.swift | 2 +- Source/SourceKittenFramework/UID.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/SourceKittenFramework/Request.swift b/Source/SourceKittenFramework/Request.swift index aace9a7f4..72aaf41b6 100644 --- a/Source/SourceKittenFramework/Request.swift +++ b/Source/SourceKittenFramework/Request.swift @@ -195,7 +195,7 @@ public enum Request { case editorOpen(file: File) /// A `cursorinfo` request for an offset in the given file, using the `arguments` given. case cursorInfo(file: String, offset: Int64, arguments: [String]) - /// A custom request by passing in the sourcekitd_object_t directly. + /// A custom request by passing in the `SourceKitObject` directly. case customRequest(request: SourceKitObject) /// A request generated by sourcekit using the yaml representation. case yamlRequest(yaml: String) diff --git a/Source/SourceKittenFramework/SourceKitObject.swift b/Source/SourceKittenFramework/SourceKitObject.swift index 05c024815..b3ea7e9f4 100644 --- a/Source/SourceKittenFramework/SourceKitObject.swift +++ b/Source/SourceKittenFramework/SourceKitObject.swift @@ -8,7 +8,7 @@ import Foundation #if SWIFT_PACKAGE - import SourceKit +import SourceKit #endif // MARK: - SourceKitObjectConvertible diff --git a/Source/SourceKittenFramework/UID.swift b/Source/SourceKittenFramework/UID.swift index e78016e23..4aeb288b2 100644 --- a/Source/SourceKittenFramework/UID.swift +++ b/Source/SourceKittenFramework/UID.swift @@ -8,7 +8,7 @@ import Foundation #if SWIFT_PACKAGE - import SourceKit +import SourceKit #endif /// Swift representation of sourcekitd_uid_t