0001    //
0002    //  File.swift
0003    //  SourceKitten
0004    //
0005    //  Created by JP Simard on 2015-01-03.
0006    //  Copyright (c) 2015 SourceKitten. All rights reserved.
0007    //
0008    
0009    import Foundation
0010    import SWXMLHash
0011    #if SWIFT_PACKAGE
0012    import SourceKit
0013    #endif
0014    
0015    /// Represents a source file.
0016    public final class File
Module.swift:26
            if let file = File(path: $0) {
OffsetMap.swift:13
extension File {
Request.swift:172
    case EditorOpen(File)
SourceDeclaration.swift:16
    guard let path = declarations.first?.location.file, let file = File(path: path) else {
Structure.swift:32
    public init(file: File) {
SwiftDocs.swift:17
    public let file: File
SwiftDocs.swift:28
    public init?(file: File, arguments: [String]) {
SwiftDocs.swift:50
    public init(file: File, dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t?) {
SyntaxMap.swift:51
    public init(file: File) {
{ 0017 /// File path. Nil if initialized directly with `File(contents:)`. 0018 public let path
File.swift:30
        self.path = (path as NSString).absolutePathRepresentation()
File.swift:49
        path = nil
File.swift:292
        return path == SwiftDocKey.getFilePath(dictionary)
Request.swift:185
            if let path = file.path {
SwiftDocs.swift:33
                cursorInfoRequest: Request.cursorInfoRequestForFilePath(file.path, arguments: arguments)
SwiftDocs.swift:74
        return toJSON(toAnyObject([file.path ?? "<No File>": docsDictionary]))
: String? 0019 /// File contents. 0020 public var contents
File.swift:32
            contents = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String
File.swift:33
            lines = contents.lines()
File.swift:37
            contents = ""
File.swift:50
        self.contents = contents
File.swift:69
            return contents.substringLinesWithByteRange(start: start, length: length)?
File.swift:93
            return contents.lineRangeWithByteRange(start: start, length: length)
File.swift:108
        if let fileContentsData = contents.dataUsingEncoding(NSUTF8StringEncoding),
File.swift:321
                let commentEndLine = (contents as NSString).lineAndCharacterForByteOffset(commentByteRange.endIndex)?.line
File.swift:322
                let tokenStartLine = (contents as NSString).lineAndCharacterForByteOffset(Int(offset))?.line
File.swift:326
                return contents.byteRangeToNSRange(start: commentByteRange.startIndex, length: commentByteRange.endIndex - commentByteRange.startIndex).flatMap { nsRange in
File.swift:327
                    return contents.commentBody(nsRange)
Request.swift:194
                    sourcekitd_uid_get_from_cstr("key.name"): sourcekitd_request_string_create(String(file.contents.hash)),
Request.swift:195
                    sourcekitd_uid_get_from_cstr("key.sourcetext"): sourcekitd_request_string_create(file.contents)
SourceDeclaration.swift:19
    let currentMarks = file.contents.pragmaMarks(path, excludeRanges: declarations.map({
SourceDeclaration.swift:20
        file.contents.byteRangeToNSRange(start: $0.range.location, length: $0.range.length) ?? NSRange()
SourceDeclaration.swift:24
        let range = file.contents.byteRangeToNSRange(start: declaration.range.location, length: declaration.range.length)
SwiftDocs.swift:57
            let documentedTokenOffsets = file.contents.documentedTokenOffsets(syntaxMap)
: String 0021 /// File lines. 0022 public var lines
File.swift:33
            lines = contents.lines()
File.swift:38
            lines = []
File.swift:51
        lines = contents.lines()
: [Line] 0023 0024 /** 0025 Failable initializer by path. Fails if file contents could not be read as a UTF8 string. 0026 0027 - parameter path: File path. 0028 */ 0029 public init
Module.swift:26
            if let file = File(path: $0) {
SourceDeclaration.swift:16
    guard let path = declarations.first?.location.file, let file = File(path: path) else {
?(path: String) { 0030 self.path = (path as NSString).absolutePathRepresentation() 0031 do { 0032 contents = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String 0033 lines = contents.lines() 0034 } catch { 0035 fputs("Could not read contents of `\(path)`\n", stderr) 0036 // necessary to set contents & lines because of rdar://21744509 0037 contents = "" 0038 lines = [] 0039 return nil 0040 } 0041 } 0042 0043 /** 0044 Initializer by file contents. File path is nil. 0045 0046 - parameter contents: File contents. 0047 */ 0048 public init(contents: String) { 0049 path = nil 0050 self.contents = contents 0051 lines = contents.lines() 0052 } 0053 0054 /** 0055 Parse source declaration string from SourceKit dictionary. 0056 0057 - parameter dictionary: SourceKit dictionary to extract declaration from. 0058 0059 - returns: Source declaration if successfully parsed. 0060 */ 0061 public func parseDeclaration
File.swift:133
        if let parsedDeclaration = parseDeclaration(dictionary) {
(dictionary: [String: SourceKitRepresentable]) -> String? { 0062 if !shouldParseDeclaration(dictionary) { 0063 return nil 0064 } 0065 return SwiftDocKey.getOffset(dictionary).flatMap { start in 0066 let end = SwiftDocKey.getBodyOffset(dictionary).map { Int($0) } 0067 let start = Int(start) 0068 let length = (end ?? start) - start 0069 return contents.substringLinesWithByteRange(start: start, length: length)? 0070 .stringByTrimmingWhitespaceAndOpeningCurlyBrace() 0071 } 0072 } 0073 0074 /** 0075 Parse line numbers containing the declaration's implementation from SourceKit dictionary. 0076 0077 - parameter dictionary: SourceKit dictionary to extract declaration from. 0078 0079 - returns: Line numbers containing the declaration's implementation. 0080 */ 0081 public func parseScopeRange
File.swift:138
        if let parsedScopeRange = parseScopeRange(dictionary) {
(dictionary: [String: SourceKitRepresentable]) -> (start: Int, end: Int)? { 0082 if !shouldParseDeclaration(dictionary) { 0083 return nil 0084 } 0085 return SwiftDocKey.getOffset(dictionary).flatMap { start in 0086 let start = Int(start) 0087 let end = SwiftDocKey.getBodyOffset(dictionary).flatMap { bodyOffset in 0088 return SwiftDocKey.getBodyLength(dictionary).map { bodyLength in 0089 return Int(bodyOffset + bodyLength) 0090 } 0091 } ?? start 0092 let length = end - start 0093 return contents.lineRangeWithByteRange(start: start, length: length) 0094 } 0095 } 0096 0097 /** 0098 Extract mark-style comment string from doc dictionary. e.g. '// MARK: - The Name' 0099 0100 - parameter dictionary: Doc dictionary to parse. 0101 0102 - returns: Mark name if successfully parsed. 0103 */ 0104 private func markNameFromDictionary
File.swift:214
                if let markName = markNameFromDictionary(dictionary) {
(dictionary: [String: SourceKitRepresentable]) -> String? { 0105 precondition(SwiftDocKey.getKind(dictionary)! == SyntaxKind.CommentMark.rawValue) 0106 let offset = Int(SwiftDocKey.getOffset(dictionary)!) 0107 let length = Int(SwiftDocKey.getLength(dictionary)!) 0108 if let fileContentsData = contents.dataUsingEncoding(NSUTF8StringEncoding), 0109 subdata = Optional(fileContentsData.subdataWithRange(NSRange(location: offset, length: length))), 0110 substring = NSString(data: subdata, encoding: NSUTF8StringEncoding) as String? { 0111 return substring 0112 } 0113 return nil 0114 } 0115 0116 /** 0117 Returns a copy of the input dictionary with comment mark names, cursor.info information and 0118 parsed declarations for the top-level of the input dictionary and its substructures. 0119 0120 - parameter dictionary: Dictionary to process. 0121 - parameter cursorInfoRequest: Cursor.Info request to get declaration information. 0122 */ 0123 public func processDictionary
File.swift:172
            if let response = Request.sendCursorInfoRequest(cursorInfoRequest, atOffset: Int64(offset)).map({ processDictionary($0, cursorInfoRequest: nil, syntaxMap: syntaxMap) }),
File.swift:199
                processDictionary($0, cursorInfoRequest: cursorInfoRequest, syntaxMap: syntaxMap)
SwiftDocs.swift:55
        dictionary = file.processDictionary(dictionary, cursorInfoRequest: cursorInfoRequest, syntaxMap: syntaxMap)
(dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t? = nil, syntaxMap: SyntaxMap? = nil) -> [String: SourceKitRepresentable] { 0124 var dictionary = dictionary 0125 if let cursorInfoRequest = cursorInfoRequest { 0126 dictionary = merge( 0127 dictionary, 0128 dictWithCommentMarkNamesCursorInfo(dictionary, cursorInfoRequest: cursorInfoRequest) 0129 ) 0130 } 0131 0132 // Parse declaration and add to dictionary 0133 if let parsedDeclaration = parseDeclaration(dictionary) { 0134 dictionary[SwiftDocKey.ParsedDeclaration.rawValue] = parsedDeclaration 0135 } 0136 0137 // Parse scope range and add to dictionary 0138 if let parsedScopeRange = parseScopeRange(dictionary) { 0139 dictionary[SwiftDocKey.ParsedScopeStart.rawValue] = Int64(parsedScopeRange.start) 0140 dictionary[SwiftDocKey.ParsedScopeEnd.rawValue] = Int64(parsedScopeRange.end) 0141 } 0142 0143 // Parse `key.doc.full_as_xml` and add to dictionary 0144 if let parsedXMLDocs = (SwiftDocKey.getFullXMLDocs(dictionary).flatMap(parseFullXMLDocs)) { 0145 dictionary = merge(dictionary, parsedXMLDocs) 0146 0147 // Parse documentation comment and add to dictionary 0148 if let commentBody = (syntaxMap.flatMap { getDocumentationCommentBody(dictionary, syntaxMap: $0) }) { 0149 dictionary[SwiftDocKey.DocumentationComment.rawValue] = commentBody 0150 } 0151 } 0152 0153 // Update substructure 0154 if let substructure = newSubstructure(dictionary, cursorInfoRequest: cursorInfoRequest, syntaxMap: syntaxMap) { 0155 dictionary[SwiftDocKey.Substructure.rawValue] = substructure 0156 } 0157 return dictionary 0158 } 0159 0160 /** 0161 Returns a copy of the input dictionary with additional cursorinfo information at the given 0162 `documentationTokenOffsets` that haven't yet been documented. 0163 0164 - parameter dictionary: Dictionary to insert new docs into. 0165 - parameter documentedTokenOffsets: Offsets that are likely documented. 0166 - parameter cursorInfoRequest: Cursor.Info request to get declaration information. 0167 */ 0168 internal func furtherProcessDictionary
SwiftDocs.swift:58
            dictionary = file.furtherProcessDictionary(
(dictionary: [String: SourceKitRepresentable], documentedTokenOffsets: [Int], cursorInfoRequest: sourcekitd_object_t, syntaxMap: SyntaxMap) -> [String: SourceKitRepresentable] { 0169 var dictionary = dictionary 0170 let offsetMap = generateOffsetMap(documentedTokenOffsets, dictionary: dictionary) 0171 for offset in offsetMap.keys.reverse() { // Do this in reverse to insert the doc at the correct offset 0172 if let response = Request.sendCursorInfoRequest(cursorInfoRequest, atOffset: Int64(offset)).map({ processDictionary($0, cursorInfoRequest: nil, syntaxMap: syntaxMap) }), 0173 kind = SwiftDocKey.getKind(response), 0174 _ = SwiftDeclarationKind(rawValue: kind), 0175 parentOffset = offsetMap[offset].flatMap({ Int64($0) }), 0176 inserted = insertDoc(response, parent: dictionary, offset: parentOffset) { 0177 dictionary = inserted 0178 } 0179 } 0180 return dictionary 0181 } 0182 0183 /** 0184 Update input dictionary's substructure by running `processDictionary(_:cursorInfoRequest:syntaxMap:)` on 0185 its elements, only keeping comment marks and declarations. 0186 0187 - parameter dictionary: Input dictionary to process its substructure. 0188 - parameter cursorInfoRequest: Cursor.Info request to get declaration information. 0189 0190 - returns: A copy of the input dictionary's substructure processed by running 0191 `processDictionary(_:cursorInfoRequest:syntaxMap:)` on its elements, only keeping comment marks 0192 and declarations. 0193 */ 0194 private func newSubstructure
File.swift:154
        if let substructure = newSubstructure(dictionary, cursorInfoRequest: cursorInfoRequest, syntaxMap: syntaxMap) {
(dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t?, syntaxMap: SyntaxMap?) -> [SourceKitRepresentable]? { 0195 return SwiftDocKey.getSubstructure(dictionary)? 0196 .map({ $0 as! [String: SourceKitRepresentable] }) 0197 .filter(isDeclarationOrCommentMark) 0198 .map { 0199 processDictionary($0, cursorInfoRequest: cursorInfoRequest, syntaxMap: syntaxMap) 0200 } 0201 } 0202 0203 /** 0204 Returns an updated copy of the input dictionary with comment mark names and cursor.info information. 0205 0206 - parameter dictionary: Dictionary to update. 0207 - parameter cursorInfoRequest: Cursor.Info request to get declaration information. 0208 */ 0209 private func dictWithCommentMarkNamesCursorInfo
File.swift:128
                dictWithCommentMarkNamesCursorInfo(dictionary, cursorInfoRequest: cursorInfoRequest)
(dictionary: [String: SourceKitRepresentable], cursorInfoRequest: sourcekitd_object_t) -> [String: SourceKitRepresentable]? { 0210 if let kind = SwiftDocKey.getKind(dictionary) { 0211 // Only update dictionaries with a 'kind' key 0212 if kind == SyntaxKind.CommentMark.rawValue { 0213 // Update comment marks 0214 if let markName = markNameFromDictionary(dictionary) { 0215 return [SwiftDocKey.Name.rawValue: markName] 0216 } 0217 } else if let decl = SwiftDeclarationKind(rawValue: kind) where decl != .VarParameter { 0218 // Update if kind is a declaration (but not a parameter) 0219 var updateDict = Request.sendCursorInfoRequest(cursorInfoRequest, 0220 atOffset: SwiftDocKey.getNameOffset(dictionary)!) ?? [String: SourceKitRepresentable]() 0221 0222 // Skip kinds, since values from editor.open are more accurate than cursorinfo 0223 updateDict.removeValueForKey(SwiftDocKey.Kind.rawValue) 0224 return updateDict 0225 } 0226 } 0227 return nil 0228 } 0229 0230 /** 0231 Returns whether or not a doc should be inserted into a parent at the provided offset. 0232 0233 - parameter parent: Parent dictionary to evaluate. 0234 - parameter offset: Offset to search for in parent dictionary. 0235 0236 - returns: True if a doc should be inserted in the parent at the provided offset. 0237 */ 0238 private func shouldInsert
File.swift:257
        if shouldInsert(parent, offset: offset) {
(parent: [String: SourceKitRepresentable], offset: Int64) -> Bool { 0239 return SwiftDocKey.getSubstructure(parent) != nil && 0240 ((offset == 0) || 0241 (shouldTreatAsSameFile(parent) && SwiftDocKey.getOffset(parent) == offset)) 0242 } 0243 0244 /** 0245 Inserts a document dictionary at the specified offset. 0246 Parent will be traversed until the offset is found. 0247 Returns nil if offset could not be found. 0248 0249 - parameter doc: Document dictionary to insert. 0250 - parameter parent: Parent to traverse to find insertion point. 0251 - parameter offset: Offset to insert document dictionary. 0252 0253 - returns: Parent with doc inserted if successful. 0254 */ 0255 private func insertDoc
File.swift:176
                inserted = insertDoc(response, parent: dictionary, offset: parentOffset) {
File.swift:274
                    if let subDict = insertDoc(doc, parent: subArray[i] as! [String: SourceKitRepresentable], offset: offset) {
(doc: [String: SourceKitRepresentable], parent: [String: SourceKitRepresentable], offset: Int64) -> [String: SourceKitRepresentable]? { 0256 var parent = parent 0257 if shouldInsert(parent, offset: offset) { 0258 var substructure = SwiftDocKey.getSubstructure(parent)! 0259 var insertIndex = substructure.count 0260 for (index, structure) in substructure.reverse().enumerate() { 0261 if SwiftDocKey.getOffset(structure as! [String: SourceKitRepresentable])! < offset { 0262 break 0263 } 0264 insertIndex = substructure.count - index 0265 } 0266 substructure.insert(doc, atIndex: insertIndex) 0267 parent[SwiftDocKey.Substructure.rawValue] = substructure 0268 return parent 0269 } 0270 for key in parent.keys { 0271 if let subArray = parent[key] as? [SourceKitRepresentable] { 0272 var subArray = subArray 0273 for i in 0..<subArray.count { 0274 if let subDict = insertDoc(doc, parent: subArray[i] as! [String: SourceKitRepresentable], offset: offset) { 0275 subArray[i] = subDict 0276 parent[key] = subArray 0277 return parent 0278 } 0279 } 0280 } 0281 } 0282 return nil 0283 } 0284 0285 /** 0286 Returns true if path is nil or if path has the same last path component as `key.filepath` in the 0287 input dictionary. 0288 0289 - parameter dictionary: Dictionary to parse. 0290 */ 0291 internal func shouldTreatAsSameFile
File.swift:241
            (shouldTreatAsSameFile(parent) && SwiftDocKey.getOffset(parent) == offset))
File.swift:301
        let sameFile                = shouldTreatAsSameFile(dictionary)
OffsetMap.swift:54
            shouldTreatAsSameFile(dictionary) {
(dictionary: [String: SourceKitRepresentable]) -> Bool { 0292 return path == SwiftDocKey.getFilePath(dictionary) 0293 } 0294 0295 /** 0296 Returns true if the input dictionary contains a parseable declaration. 0297 0298 - parameter dictionary: Dictionary to parse. 0299 */ 0300 private func shouldParseDeclaration
File.swift:62
        if !shouldParseDeclaration(dictionary) {
File.swift:82
        if !shouldParseDeclaration(dictionary) {
(dictionary: [String: SourceKitRepresentable]) -> Bool { 0301 let sameFile = shouldTreatAsSameFile(dictionary) 0302 let hasTypeName = SwiftDocKey.getTypeName(dictionary) != nil 0303 let hasAnnotatedDeclaration = SwiftDocKey.getAnnotatedDeclaration(dictionary) != nil 0304 let hasOffset = SwiftDocKey.getOffset(dictionary) != nil 0305 let isntExtension = SwiftDocKey.getKind(dictionary) != SwiftDeclarationKind.Extension.rawValue 0306 return sameFile && hasTypeName && hasAnnotatedDeclaration && hasOffset && isntExtension 0307 } 0308 0309 /** 0310 Parses `dictionary`'s documentation comment body. 0311 0312 - parameter dictionary: Dictionary to parse. 0313 - parameter syntaxMap: SyntaxMap for current file. 0314 0315 - returns: `dictionary`'s documentation comment body as a string, without any documentation 0316 syntax (`/** ... */` or `/// ...`). 0317 */ 0318 public func getDocumentationCommentBody
File.swift:148
            if let commentBody = (syntaxMap.flatMap { getDocumentationCommentBody(dictionary, syntaxMap: $0) }) {
(dictionary: [String: SourceKitRepresentable], syntaxMap: SyntaxMap) -> String? { 0319 return SwiftDocKey.getOffset(dictionary).flatMap { offset in 0320 return syntaxMap.commentRangeBeforeOffset(Int(offset)).flatMap { commentByteRange in 0321 let commentEndLine = (contents as NSString).lineAndCharacterForByteOffset(commentByteRange.endIndex)?.line 0322 let tokenStartLine = (contents as NSString).lineAndCharacterForByteOffset(Int(offset))?.line 0323 guard commentEndLine == tokenStartLine || commentEndLine == tokenStartLine?.predecessor() else { 0324 return nil 0325 } 0326 return contents.byteRangeToNSRange(start: commentByteRange.startIndex, length: commentByteRange.endIndex - commentByteRange.startIndex).flatMap { nsRange in 0327 return contents.commentBody(nsRange) 0328 } 0329 } 0330 } 0331 } 0332 } 0333 0334 /** 0335 Returns true if the dictionary represents a source declaration or a mark-style comment. 0336 0337 - parameter dictionary: Dictionary to parse. 0338 */ 0339 private func isDeclarationOrCommentMark
File.swift:197
            .filter(isDeclarationOrCommentMark)
(dictionary: [String: SourceKitRepresentable]) -> Bool { 0340 if let kind = SwiftDocKey.getKind(dictionary) { 0341 return kind != SwiftDeclarationKind.VarParameter.rawValue && 0342 (kind == SyntaxKind.CommentMark.rawValue || SwiftDeclarationKind(rawValue: kind) != nil) 0343 } 0344 return false 0345 } 0346 0347 /** 0348 Parse XML from `key.doc.full_as_xml` from `cursor.info` request. 0349 0350 - parameter xmlDocs: Contents of `key.doc.full_as_xml` from SourceKit. 0351 0352 - returns: XML parsed as an `[String: SourceKitRepresentable]`. 0353 */ 0354 public func parseFullXMLDocs
File.swift:144
        if let parsedXMLDocs = (SwiftDocKey.getFullXMLDocs(dictionary).flatMap(parseFullXMLDocs)) {
(xmlDocs: String) -> [String: SourceKitRepresentable]? { 0355 let cleanXMLDocs = xmlDocs.stringByReplacingOccurrencesOfString("<rawHTML>", withString: "") 0356 .stringByReplacingOccurrencesOfString("</rawHTML>", withString: "") 0357 .stringByReplacingOccurrencesOfString("<codeVoice>", withString: "`") 0358 .stringByReplacingOccurrencesOfString("</codeVoice>", withString: "`") 0359 return SWXMLHash.parse(cleanXMLDocs).children.first.map { rootXML in 0360 var docs = [String: SourceKitRepresentable]() 0361 docs[SwiftDocKey.DocType.rawValue] = rootXML.element?.name 0362 docs[SwiftDocKey.DocFile.rawValue] = rootXML.element?.attributes["file"] 0363 docs[SwiftDocKey.DocLine.rawValue] = rootXML.element?.attributes["line"].flatMap { 0364 Int64($0) 0365 } 0366 docs[SwiftDocKey.DocColumn.rawValue] = rootXML.element?.attributes["column"].flatMap { 0367 Int64($0) 0368 } 0369 docs[SwiftDocKey.DocName.rawValue] = rootXML["Name"].element?.text 0370 docs[SwiftDocKey.USR.rawValue] = rootXML["USR"].element?.text 0371 docs[SwiftDocKey.DocDeclaration.rawValue] = rootXML["Declaration"].element?.text 0372 let parameters = rootXML["Parameters"].children 0373 if parameters.count > 0 { 0374 docs[SwiftDocKey.DocParameters.rawValue] = parameters.map { 0375 [ 0376 "name": $0["Name"].element?.text ?? "", 0377 "discussion": childrenAsArray($0["Discussion"]) ?? [] 0378 ] as [String: SourceKitRepresentable] 0379 } as [SourceKitRepresentable] 0380 } 0381 docs[SwiftDocKey.DocDiscussion.rawValue] = childrenAsArray(rootXML["Discussion"]) 0382 docs[SwiftDocKey.DocResultDiscussion.rawValue] = childrenAsArray(rootXML["ResultDiscussion"]) 0383 return docs 0384 } 0385 } 0386 0387 /** 0388 Returns an `[SourceKitRepresentable]` of `[String: SourceKitRepresentable]` items from `indexer` children, if any. 0389 0390 - parameter indexer: `XMLIndexer` to traverse. 0391 */ 0392 private func childrenAsArray(indexer: XMLIndexer) -> [SourceKitRepresentable]? { 0393 let children = indexer.children 0394 if children.count > 0 { 0395 return children.flatMap({ $0.element }).map { 0396 [$0.name: $0.text ?? ""] as [String: SourceKitRepresentable] 0397 } as [SourceKitRepresentable] 0398 } 0399 return nil 0400 } 0401