From 8d2ce961497318b999e524bceb42af0011d9707e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 9 Dec 2015 02:10:24 -0800 Subject: [PATCH 1/3] Fixed offset issues looking for Objective-C marks Fixes #114. --- .../SourceDeclaration.swift | 7 +++- .../String+SourceKitten.swift | 37 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Source/SourceKittenFramework/SourceDeclaration.swift b/Source/SourceKittenFramework/SourceDeclaration.swift index c6d293b88..944eb72ba 100644 --- a/Source/SourceKittenFramework/SourceDeclaration.swift +++ b/Source/SourceKittenFramework/SourceDeclaration.swift @@ -11,10 +11,13 @@ public func insertMarks(declarations: [SourceDeclaration], limitRange: NSRange? guard let path = declarations.first?.location.file, let file = File(path: path) else { fatalError("can't extract marks without a file.") } - let currentMarks = file.contents.pragmaMarks(path, excludeRanges: declarations.map({ $0.range }), limitRange: limitRange) + let currentMarks = file.contents.pragmaMarks(path, excludeRanges: declarations.map({ + file.contents.byteRangeToNSRange(start: $0.range.location, length: $0.range.length) ?? NSRange() + }), limitRange: limitRange) let newDeclarations: [SourceDeclaration] = declarations.map { declaration in var varDeclaration = declaration - varDeclaration.children = insertMarks(declaration.children, limitRange: declaration.range) + let range = file.contents.byteRangeToNSRange(start: declaration.range.location, length: declaration.range.length) + varDeclaration.children = insertMarks(declaration.children, limitRange: range) return varDeclaration } return (newDeclarations + currentMarks).sort() diff --git a/Source/SourceKittenFramework/String+SourceKitten.swift b/Source/SourceKittenFramework/String+SourceKitten.swift index 21a696de4..fee7e410a 100644 --- a/Source/SourceKittenFramework/String+SourceKitten.swift +++ b/Source/SourceKittenFramework/String+SourceKitten.swift @@ -87,6 +87,24 @@ extension NSString { } } + /** + Converts an `NSRange` suitable for filtering `self` as an + `NSString` to a range of byte offsets in `self`. + + - parameter start: Starting character index in the string. + - parameter length: Number of characters to include in range. + + - returns: An equivalent `NSRange`. + */ + public func NSRangeToByteRange(start start: Int, length: Int) -> NSRange? { + let string = self as String + return string.byteOffsetAtIndex(start).flatMap { stringStart in + return string.byteOffsetAtIndex(start + length).map { stringEnd in + return NSRange(location: stringStart, length: stringEnd - stringStart) + } + } + } + /** Returns a substring with the provided byte range. @@ -181,6 +199,17 @@ extension String { return utf8.startIndex.advancedBy(offset).samePositionIn(utf16).map(utf16.startIndex.distanceTo) } + /** + Byte offset equivalent to UTF16 index. + + - parameter index: UTF16 index. + + - returns: Byte offset, if any. + */ + private func byteOffsetAtIndex(index: Int) -> Int? { + return utf16.startIndex.advancedBy(index).samePositionIn(utf8).map(utf8.startIndex.distanceTo) + } + /// Returns the `#pragma mark`s in the string. /// Just the content; no leading dashes or leading `#pragma mark`. public func pragmaMarks(filename: String, excludeRanges: [NSRange], limitRange: NSRange?) -> [SourceDeclaration] { @@ -205,9 +234,13 @@ extension String { if markString.isEmpty { return nil } + let markByteRange = self.NSRangeToByteRange(start: markRange.location, length: markRange.length) + if markByteRange == nil { + return nil + } let location = SourceLocation(file: filename, - line: UInt32((self as NSString).lineRangeWithByteRange(start: markRange.location, length: 0)!.start), - column: 1, offset: UInt32(markRange.location)) + line: UInt32((self as NSString).lineRangeWithByteRange(start: markByteRange!.location, length: 0)!.start), + column: 1, offset: UInt32(markByteRange!.location)) return SourceDeclaration(type: .Mark, location: location, extent: (location, location), name: markString, usr: nil, declaration: nil, documentation: nil, commentBody: nil, children: []) } From 4646514f85e83aa06b86f63f9a3b92ad1de2f663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 9 Dec 2015 02:37:03 -0800 Subject: [PATCH 2/3] Added test for Unicode in Objective-C --- .../ClangTranslationUnitTests.swift | 9 ++++ .../Fixtures/SuperScript.h | 15 ++++++ .../Fixtures/SuperScript.json | 49 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 Source/SourceKittenFrameworkTests/Fixtures/SuperScript.h create mode 100644 Source/SourceKittenFrameworkTests/Fixtures/SuperScript.json diff --git a/Source/SourceKittenFrameworkTests/ClangTranslationUnitTests.swift b/Source/SourceKittenFrameworkTests/ClangTranslationUnitTests.swift index ba316d27c..e67a72205 100644 --- a/Source/SourceKittenFrameworkTests/ClangTranslationUnitTests.swift +++ b/Source/SourceKittenFrameworkTests/ClangTranslationUnitTests.swift @@ -35,6 +35,15 @@ class ClangTranslationUnitTests: XCTestCase { let comparisonString = (tu.description + "\n").stringByReplacingOccurrencesOfString(escapedFixturesDirectory, withString: "") compareJSONStringWithFixturesName("Musician", jsonString: comparisonString) } + + func testUnicodeInObjectiveCDocs() { + let headerFiles = [fixturesDirectory + "SuperScript.h"] + let compilerArguments = ["-x", "objective-c", "-isysroot", sdkPath(), "-I", fixturesDirectory] + let tu = ClangTranslationUnit(headerFiles: headerFiles, compilerArguments: compilerArguments) + let escapedFixturesDirectory = fixturesDirectory.stringByReplacingOccurrencesOfString("/", withString: "\\/") + let comparisonString = (tu.description + "\n").stringByReplacingOccurrencesOfString(escapedFixturesDirectory, withString: "") + compareJSONStringWithFixturesName("SuperScript", jsonString: comparisonString) + } func testRealmObjectiveCDocs() { let headerFiles = [fixturesDirectory + "/Realm/Realm.h"] diff --git a/Source/SourceKittenFrameworkTests/Fixtures/SuperScript.h b/Source/SourceKittenFrameworkTests/Fixtures/SuperScript.h new file mode 100644 index 000000000..77f108eab --- /dev/null +++ b/Source/SourceKittenFrameworkTests/Fixtures/SuperScript.h @@ -0,0 +1,15 @@ +#import + +/** + ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ + */ +@interface SuperScript : NSObject + +#pragma mark ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ + +/** + ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶹ ᵠ ᵡ + */ +- (IBAction)superSize:(id)sender; + +@end diff --git a/Source/SourceKittenFrameworkTests/Fixtures/SuperScript.json b/Source/SourceKittenFrameworkTests/Fixtures/SuperScript.json new file mode 100644 index 000000000..d9d9705f8 --- /dev/null +++ b/Source/SourceKittenFrameworkTests/Fixtures/SuperScript.json @@ -0,0 +1,49 @@ +[ + { + "SuperScript.h" : { + "key.substructure" : [ + { + "key.kind" : "sourcekitten.source.lang.objc.decl.class", + "key.parsed_declaration" : "@interface SuperScript : NSObject", + "key.doc.comment" : "ᴬ \tᴮ \t\tᴰ \tᴱ \t\tᴳ \tᴴ \tᴵ \tᴶ \tᴷ \tᴸ \tᴹ \tᴺ \tᴼ \tᴾ \t\tᴿ \t\tᵀ \tᵁ \tⱽ \tᵂ", + "key.doc.line" : 6, + "key.name" : "SuperScript", + "key.doc.column" : 12, + "key.parsed_scope.end" : 15, + "key.usr" : "c:objc(cs)SuperScript", + "key.doc.file" : "SuperScript.h", + "key.doc.full_as_xml" : "", + "key.substructure" : [ + { + "key.kind" : "sourcekitten.source.lang.objc.mark", + "key.doc.file" : "SuperScript.h", + "key.doc.line" : 8, + "key.name" : "ᵃ\tᵇ\tᶜ\tᵈ\tᵉ\tᶠ\tᵍ\tʰ\tⁱ\tʲ\tᵏ\tˡ\tᵐ\tⁿ\tᵒ\tᵖ\t\tʳ\tˢ\tᵗ\tᵘ\tᵛ\tʷ\tˣ\tʸ\tᶻ", + "key.parsed_scope.start" : 8, + "key.doc.column" : 1, + "key.parsed_scope.end" : 8, + "key.filepath" : "SuperScript.h" + }, + { + "key.kind" : "sourcekitten.source.lang.objc.decl.method.instance", + "key.doc.file" : "SuperScript.h", + "key.parsed_declaration" : "- (void)superSize:(id)sender;", + "key.doc.comment" : "ᵝ \tᵞ \tᵟ \tᵋ \t\t\tᶿ \tᶥ \t\t\t\t\t\t\t\t\t\t\tᶹ \tᵠ \tᵡ", + "key.doc.full_as_xml" : "", + "key.doc.line" : 13, + "key.name" : "-superSize:", + "key.parsed_scope.start" : 13, + "key.doc.column" : 13, + "key.parsed_scope.end" : 13, + "key.filepath" : "SuperScript.h", + "key.usr" : "c:objc(cs)SuperScript(im)superSize:" + } + ], + "key.parsed_scope.start" : 6, + "key.filepath" : "SuperScript.h" + } + ], + "key.diagnostic_stage" : "" + } + } +] From 4243ed807f4e0f251b413dced7e1e30fedab6095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 9 Dec 2015 10:16:07 -0800 Subject: [PATCH 3/3] Wrapped let in guard --- Source/SourceKittenFramework/String+SourceKitten.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/SourceKittenFramework/String+SourceKitten.swift b/Source/SourceKittenFramework/String+SourceKitten.swift index fee7e410a..161bb0e4c 100644 --- a/Source/SourceKittenFramework/String+SourceKitten.swift +++ b/Source/SourceKittenFramework/String+SourceKitten.swift @@ -234,13 +234,12 @@ extension String { if markString.isEmpty { return nil } - let markByteRange = self.NSRangeToByteRange(start: markRange.location, length: markRange.length) - if markByteRange == nil { + guard let markByteRange = self.NSRangeToByteRange(start: markRange.location, length: markRange.length) else { return nil } let location = SourceLocation(file: filename, - line: UInt32((self as NSString).lineRangeWithByteRange(start: markByteRange!.location, length: 0)!.start), - column: 1, offset: UInt32(markByteRange!.location)) + line: UInt32((self as NSString).lineRangeWithByteRange(start: markByteRange.location, length: 0)!.start), + column: 1, offset: UInt32(markByteRange.location)) return SourceDeclaration(type: .Mark, location: location, extent: (location, location), name: markString, usr: nil, declaration: nil, documentation: nil, commentBody: nil, children: []) }