diff --git a/Source/SourceKittenFramework/Accessibility.swift b/Source/SourceKittenFramework/Accessibility.swift new file mode 100644 index 000000000..181fd4574 --- /dev/null +++ b/Source/SourceKittenFramework/Accessibility.swift @@ -0,0 +1,15 @@ +// +// Accessibility.swift +// SourceKitten +// +// Created by Paul Young on 12/4/15. +// Copyright © 2015 SourceKitten. All rights reserved. +// + +import Foundation + +public enum Accessibility: String { + case Internal = "source.lang.swift.accessibility.internal" + case Private = "source.lang.swift.accessibility.private" + case Public = "source.lang.swift.accessibility.public" +} diff --git a/Source/SourceKittenFramework/DeclarationKindType.swift b/Source/SourceKittenFramework/DeclarationKindType.swift new file mode 100644 index 000000000..0f7dcb570 --- /dev/null +++ b/Source/SourceKittenFramework/DeclarationKindType.swift @@ -0,0 +1,9 @@ +// +// DeclarationKindType.swift +// SourceKitten +// +// Created by Paul Young on 12/4/15. +// Copyright © 2015 SourceKitten. All rights reserved. +// + +public protocol DeclarationKindType {} diff --git a/Source/SourceKittenFramework/DeclarationType.swift b/Source/SourceKittenFramework/DeclarationType.swift new file mode 100644 index 000000000..498344d51 --- /dev/null +++ b/Source/SourceKittenFramework/DeclarationType.swift @@ -0,0 +1,20 @@ +// +// DeclarationType.swift +// SourceKitten +// +// Created by Paul Young on 12/4/15. +// Copyright © 2015 SourceKitten. All rights reserved. +// + +public protocol DeclarationType { + var language: Language { get } + var kind: DeclarationKindType? { get } + var location: SourceLocation { get } + var extent: (start: SourceLocation, end: SourceLocation) { get } + var name: String? { get } + var typeName: String? { get } + var usr: String? { get } + var declaration: String? { get } + var documentationComment: String? { get } + var children: [DeclarationType] { get } +} diff --git a/Source/SourceKittenFramework/ObjCDeclaration.swift b/Source/SourceKittenFramework/ObjCDeclaration.swift new file mode 100644 index 000000000..bec2b09e3 --- /dev/null +++ b/Source/SourceKittenFramework/ObjCDeclaration.swift @@ -0,0 +1,110 @@ +// +// ObjCDeclaration.swift +// SourceKitten +// +// Created by Paul Young on 12/4/15. +// Copyright © 2015 SourceKitten. All rights reserved. +// + +import Foundation + +public struct ObjCDeclaration: DeclarationType { + public let language: Language = .Swift + public let kind: ObjCDeclarationKind? // FIXME: Type 'ObjCDeclaration' does not conform to protocol 'DeclarationType' + public let location: SourceLocation + public let extent: (start: SourceLocation, end: SourceLocation) + public let name: String? + public let typeName: String? + public let usr: String? + public let declaration: String? + public let documentationComment: String? + public let children: [DeclarationType] + + /// Returns the USR for the auto-generated getter for this property. + /// + /// - warning: can only be invoked if `type == .Property`. + var getterUSR: String { + return generateAccessorUSR(getter: true) + } + + /// Returns the USR for the auto-generated setter for this property. + /// + /// - warning: can only be invoked if `type == .Property`. + var setterUSR: String { + return generateAccessorUSR(getter: false) + } + + private func generateAccessorUSR(getter getter: Bool) -> String { + assert(kind == .Property) + guard let usr = usr else { + fatalError("Couldn't extract USR") + } + guard let declaration = declaration else { + fatalError("Couldn't extract declaration") + } + let pyStartIndex = usr.rangeOfString("(py)")!.startIndex + let usrPrefix = usr.substringToIndex(pyStartIndex) + let fullDeclarationRange = NSRange(location: 0, length: (declaration as NSString).length) + let regex = try! NSRegularExpression(pattern: getter ? "getter\\s*=\\s*(\\w+)" : "setter\\s*=\\s*(\\w+:)", options: []) + let matches = regex.matchesInString(declaration, options: [], range: fullDeclarationRange) + if matches.count > 0 { + let accessorName = (declaration as NSString).substringWithRange(matches[0].rangeAtIndex(1)) + return usrPrefix + "(im)\(accessorName)" + } else if getter { + return usr.stringByReplacingOccurrencesOfString("(py)", withString: "(im)") + } + // Setter + let capitalFirstLetter = String(usr.characters[pyStartIndex.advancedBy(4)]).capitalizedString + let restOfSetterName = usr.substringFromIndex(pyStartIndex.advancedBy(5)) + return "\(usrPrefix)(im)set\(capitalFirstLetter)\(restOfSetterName):" + } +} + +extension ObjCDeclaration { + public init?(cursor: CXCursor) { + guard cursor.shouldDocument() else { + return nil + } + kind = cursor.objCKind() + extent = cursor.extent() + name = cursor.name() + //typeName = cursor. // FIXME: no cursor.typeName() + usr = cursor.usr() + declaration = cursor.declaration() + documentationComment = cursor.parsedComment() // FIXME: Cannot assign value of type 'CXComment' to type 'String?' + children = cursor.flatMap(ObjCDeclaration.init).rejectPropertyMethods() // FIXME: Cannot assign value of type '[ObjCDeclaration]' to type '[DeclarationType]' + } +} + +extension SequenceType where Generator.Element == ObjCDeclaration { + /// Removes implicitly generated property getters & setters + func rejectPropertyMethods() -> [ObjCDeclaration] { + let propertyGetterSetterUSRs = filter { + $0.kind == .Property + }.flatMap { + [$0.getterUSR, $0.setterUSR] + } + return filter { !propertyGetterSetterUSRs.contains($0.usr!) } + } +} + +// MARK: Hashable + +extension ObjCDeclaration: Hashable { + public var hashValue: Int { + return usr?.hashValue ?? 0 + } +} + +public func ==(lhs: ObjCDeclaration, rhs: ObjCDeclaration) -> Bool { + return lhs.usr == rhs.usr && + lhs.location == rhs.location +} + +// MARK: Comparable + +/// A [strict total order](http://en.wikipedia.org/wiki/Total_order#Strict_total_order) +/// over instances of `Self`. +public func <(lhs: ObjCDeclaration, rhs: ObjCDeclaration) -> Bool { + return lhs.location < rhs.location +} diff --git a/Source/SourceKittenFramework/ObjCDeclarationKind.swift b/Source/SourceKittenFramework/ObjCDeclarationKind.swift index 0d34c8c70..0e50c74c7 100644 --- a/Source/SourceKittenFramework/ObjCDeclarationKind.swift +++ b/Source/SourceKittenFramework/ObjCDeclarationKind.swift @@ -65,3 +65,5 @@ public enum ObjCDeclarationKind: String { } } } + +extension ObjCDeclarationKind: DeclarationKindType {} diff --git a/Source/SourceKittenFramework/SwiftDeclaration.swift b/Source/SourceKittenFramework/SwiftDeclaration.swift new file mode 100644 index 000000000..a2d5bee6e --- /dev/null +++ b/Source/SourceKittenFramework/SwiftDeclaration.swift @@ -0,0 +1,75 @@ +// +// SwiftDeclaration.swift +// SourceKitten +// +// Created by Paul Young on 12/4/15. +// Copyright © 2015 SourceKitten. All rights reserved. +// + +import SwiftXPC + +public struct SwiftDeclaration: DeclarationType { + public let language: Language = .Swift + public let kind: DeclarationKindType? + public let location: SourceLocation + public let extent: (start: SourceLocation, end: SourceLocation) + public let name: String? + public let typeName: String? + public let usr: String? + public let declaration: String? + public let documentationComment: String? + public let children: [DeclarationType] + public let accessibility: Accessibility? +} + +extension SwiftDeclaration { + public init(dictionary: XPCDictionary) { + kind = SwiftDocKey.getKind(dictionary).flatMap { SwiftDeclarationKind(rawValue: $0) } // FIXME: why doesn't .flatMap(SwiftDeclarationKind.init) work here? + + if let file = SwiftDocKey.getDocFile(dictionary), + line = SwiftDocKey.getDocLine(dictionary).map({ UInt32($0) }), + column = SwiftDocKey.getDocColumn(dictionary).map({ UInt32($0) }) { + + if let offset = SwiftDocKey.getOffset(dictionary).map({ UInt32($0) }) { + location = SourceLocation(file: file, line: line, column: column, offset: offset) + } + + if let parsedScopeStart = SwiftDocKey.getParsedScopeStart(dictionary).map({ UInt32($0) }), + parsedScopeEnd = SwiftDocKey.getParsedScopeEnd(dictionary).map({ UInt32($0) }) { + + let start = SourceLocation.init(file: file, line: line, column: column, offset: parsedScopeStart) + let end = SourceLocation.init(file: file, line: line, column: column, offset: parsedScopeEnd) + extent = (start: start, end: end) + } + } + + name = SwiftDocKey.getName(dictionary) + typeName = SwiftDocKey.getTypeName(dictionary) + usr = SwiftDocKey.getUSR(dictionary) + declaration = SwiftDocKey.getParsedDeclaration(dictionary) + documentationComment = // FIXME + children = SwiftDocKey.getSubstructure(dictionary) // FIXME: Cannot assign value of type 'XPCArray?' to type '[DeclarationType]' + accessibility = // FIXME: Accessibility(rawValue: ...) + } +} + +// MARK: Hashable + +extension SwiftDeclaration: Hashable { + public var hashValue: Int { + return usr?.hashValue ?? 0 + } +} + +public func ==(lhs: SwiftDeclaration, rhs: SwiftDeclaration) -> Bool { + return lhs.usr == rhs.usr && + lhs.location == rhs.location +} + +// MARK: Comparable + +/// A [strict total order](http://en.wikipedia.org/wiki/Total_order#Strict_total_order) +/// over instances of `Self`. +public func <(lhs: SwiftDeclaration, rhs: SwiftDeclaration) -> Bool { + return lhs.location < rhs.location +} diff --git a/Source/SourceKittenFramework/SwiftDeclarationKind.swift b/Source/SourceKittenFramework/SwiftDeclarationKind.swift index ed6e53152..163e84d33 100644 --- a/Source/SourceKittenFramework/SwiftDeclarationKind.swift +++ b/Source/SourceKittenFramework/SwiftDeclarationKind.swift @@ -78,3 +78,5 @@ public enum SwiftDeclarationKind: String { /// `var.static`. case VarStatic = "source.lang.swift.decl.var.static" } + +extension SwiftDeclarationKind: DeclarationKindType {} diff --git a/Source/SourceKittenFramework/SwiftDocKey.swift b/Source/SourceKittenFramework/SwiftDocKey.swift index 9c7a46a97..a5a71df0f 100644 --- a/Source/SourceKittenFramework/SwiftDocKey.swift +++ b/Source/SourceKittenFramework/SwiftDocKey.swift @@ -62,17 +62,17 @@ internal enum SwiftDocKey: String { case DocName = "key.doc.name" /// Parameters of documented token (XPCArray). case DocParameters = "key.doc.parameters" - /// Parsed declaration (String). + /// Result discussion documentation of documented token (XPCArray). case DocResultDiscussion = "key.doc.result_discussion" - /// Parsed scope start (Int64). + /// Type of documented token (String). case DocType = "key.doc.type" - /// Parsed scope start end (Int64). + /// USR of documented token (String). case USR = "key.usr" - /// Result discussion documentation of documented token (XPCArray). + /// Parsed declaration (String). case ParsedDeclaration = "key.parsed_declaration" - /// Type of documented token (String). + /// Parsed scope end (Int64). case ParsedScopeEnd = "key.parsed_scope.end" - /// USR of documented token (String). + /// Parsed scope start (Int64). case ParsedScopeStart = "key.parsed_scope.start" @@ -166,6 +166,17 @@ internal enum SwiftDocKey: String { internal static func getSubstructure(dictionary: XPCDictionary) -> XPCArray? { return get(.Substructure, dictionary) } + + /** + Get name string from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: Name string if successful. + */ + internal static func getName(dictionary: XPCDictionary) -> String? { + return get(.Name, dictionary) + } /** Get name offset int from dictionary. @@ -232,4 +243,81 @@ internal enum SwiftDocKey: String { internal static func getFullXMLDocs(dictionary: XPCDictionary) -> String? { return get(.FullXMLDocs, dictionary) } + + /** + Get USR string from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: USR string if successful. + */ + internal static func getUSR(dictionary: XPCDictionary) -> String? { + return get(.USR, dictionary) + } + + /** + Get parsed declaration string from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: parsed declaration string if successful. + */ + internal static func getParsedDeclaration(dictionary: XPCDictionary) -> String? { + return get(.ParsedDeclaration, dictionary) + } + + /** + Get parsed scope start int from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: parsed scope start int if successful. + */ + internal static func getParsedScopeStart(dictionary: XPCDictionary) -> Int64? { + return get(.ParsedScopeStart, dictionary) + } + + /** + Get parsed scope end int from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: parsed scope end int if successful. + */ + internal static func getParsedScopeEnd(dictionary: XPCDictionary) -> Int64? { + return get(.ParsedScopeEnd, dictionary) + } + + /** + Get doc file string from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: doc file string if successful. + */ + internal static func getDocFile(dictionary: XPCDictionary) -> String? { + return get(.DocFile, dictionary) + } + + /** + Get doc column int from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: doc column int if successful. + */ + internal static func getDocColumn(dictionary: XPCDictionary) -> Int64? { + return get(.DocColumn, dictionary) + } + + /** + Get doc line int from dictionary. + + - parameter dictionary: Dictionary to get value from. + + - returns: doc line int if successful. + */ + internal static func getDocLine(dictionary: XPCDictionary) -> Int64? { + return get(.DocLine, dictionary) + } } diff --git a/sourcekitten.xcodeproj/project.pbxproj b/sourcekitten.xcodeproj/project.pbxproj index eaabf6df3..6e03f0c95 100644 --- a/sourcekitten.xcodeproj/project.pbxproj +++ b/sourcekitten.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 2C0A78691C111E2500C64CB0 /* DeclarationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0A78681C111E2500C64CB0 /* DeclarationType.swift */; }; + 2C0A786B1C111F2500C64CB0 /* SwiftDeclaration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0A786A1C111F2500C64CB0 /* SwiftDeclaration.swift */; }; + 2C0A786D1C111FC100C64CB0 /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0A786C1C111FC100C64CB0 /* Accessibility.swift */; }; + 2C0A786F1C11222700C64CB0 /* DeclarationKindType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0A786E1C11222700C64CB0 /* DeclarationKindType.swift */; }; + 2C184FEB1C121838001AA834 /* ObjCDeclaration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C184FEA1C121838001AA834 /* ObjCDeclaration.swift */; }; 2C55B3321BEB3CA7002E8C6B /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E834D61D1B2D054B002AA1FE /* Result.framework */; }; 2C55B3331BEB3CAB002E8C6B /* Commandant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8EBAA5D1A5D374B002F1B8E /* Commandant.framework */; }; 2C55B3341BEB3D7D002E8C6B /* Commandant.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E8EBAA5D1A5D374B002F1B8E /* Commandant.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -125,6 +130,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2C0A78681C111E2500C64CB0 /* DeclarationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeclarationType.swift; sourceTree = ""; }; + 2C0A786A1C111F2500C64CB0 /* SwiftDeclaration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftDeclaration.swift; sourceTree = ""; }; + 2C0A786C1C111FC100C64CB0 /* Accessibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = ""; }; + 2C0A786E1C11222700C64CB0 /* DeclarationKindType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeclarationKindType.swift; sourceTree = ""; }; + 2C184FEA1C121838001AA834 /* ObjCDeclaration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCDeclaration.swift; sourceTree = ""; }; 3F0CBB401BAAFF160015BBA8 /* Clang+SourceKitten.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Clang+SourceKitten.swift"; sourceTree = ""; }; 3F56EACF1BAB251C006433D0 /* JSONOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONOutput.swift; sourceTree = ""; }; 5499CA961A2394B700783309 /* Components.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = ""; }; @@ -362,10 +372,13 @@ children = ( E8D4742B1A648ED10011A49C /* clang-c */, D0D1216F19E87B05005E4BAA /* Supporting Files */, + 2C0A786C1C111FC100C64CB0 /* Accessibility.swift */, E87212191AD2FFCD00A484F4 /* Array+SourceKitten.swift */, 3F0CBB401BAAFF160015BBA8 /* Clang+SourceKitten.swift */, E8D4743A1A648F290011A49C /* ClangTranslationUnit.swift */, E8EE34BE1B9A502F00947605 /* CodeCompletionItem.swift */, + 2C0A786E1C11222700C64CB0 /* DeclarationKindType.swift */, + 2C0A78681C111E2500C64CB0 /* DeclarationType.swift */, E852418E1A5F4FB3007099FB /* Dictionary+Merge.swift */, E806D2921BE058D600D1BE41 /* Documentation.swift */, E84763691A5A0651000EAE22 /* File.swift */, @@ -382,6 +395,7 @@ E806D28C1BE0589B00D1BE41 /* SourceLocation.swift */, E8AE53C61A5B5FCA0092D24A /* String+SourceKitten.swift */, E834740E1A593B5B00532B9A /* Structure.swift */, + 2C0A786A1C111F2500C64CB0 /* SwiftDeclaration.swift */, E89291A81A5B800300D91568 /* SwiftDeclarationKind.swift */, E89291A61A5B7FF800D91568 /* SwiftDocKey.swift */, E8A18A3E1A592246000362B7 /* SwiftDocs.swift */, @@ -391,6 +405,7 @@ E80F236A1A5CB04100FD2352 /* SyntaxToken.swift */, E806D28E1BE058B100D1BE41 /* Text.swift */, E8A9B88F1B56CB5500CD17D4 /* Xcode.swift */, + 2C184FEA1C121838001AA834 /* ObjCDeclaration.swift */, ); name = SourceKittenFramework; path = Source/SourceKittenFramework; @@ -607,12 +622,15 @@ files = ( E806D2931BE058D600D1BE41 /* Documentation.swift in Sources */, E872121A1AD2FFCD00A484F4 /* Array+SourceKitten.swift in Sources */, + 2C0A786F1C11222700C64CB0 /* DeclarationKindType.swift in Sources */, 3F0CBB411BAAFF160015BBA8 /* Clang+SourceKitten.swift in Sources */, E8D4743B1A648F290011A49C /* ClangTranslationUnit.swift in Sources */, E8EE34BF1B9A502F00947605 /* CodeCompletionItem.swift in Sources */, E852418F1A5F4FB3007099FB /* Dictionary+Merge.swift in Sources */, + 2C0A78691C111E2500C64CB0 /* DeclarationType.swift in Sources */, E847636A1A5A0651000EAE22 /* File.swift in Sources */, 3F56EAD01BAB251C006433D0 /* JSONOutput.swift in Sources */, + 2C184FEB1C121838001AA834 /* ObjCDeclaration.swift in Sources */, E8A18A3B1A58971D000362B7 /* Language.swift in Sources */, E806D28F1BE058B100D1BE41 /* Text.swift in Sources */, E8241CA51A5E01A10047687E /* Module.swift in Sources */, @@ -624,11 +642,13 @@ E806D28D1BE0589B00D1BE41 /* SourceLocation.swift in Sources */, E868473E1A587CCC0043DC65 /* sourcekitd.swift in Sources */, E8AE53C71A5B5FCA0092D24A /* String+SourceKitten.swift in Sources */, + 2C0A786B1C111F2500C64CB0 /* SwiftDeclaration.swift in Sources */, E834740F1A593B5B00532B9A /* Structure.swift in Sources */, E89291A91A5B800300D91568 /* SwiftDeclarationKind.swift in Sources */, E89291A71A5B7FF800D91568 /* SwiftDocKey.swift in Sources */, E8A18A3F1A592246000362B7 /* SwiftDocs.swift in Sources */, E83C8E9A1A5CBADD003A8D35 /* SwiftXPC+JSON.swift in Sources */, + 2C0A786D1C111FC100C64CB0 /* Accessibility.swift in Sources */, E80F23691A5CB01A00FD2352 /* SyntaxKind.swift in Sources */, E8CC8A2D1A587FD300D1FEC7 /* SyntaxMap.swift in Sources */, E80F236B1A5CB04100FD2352 /* SyntaxToken.swift in Sources */,