Skip to content

Commit

Permalink
Do not cut out generated content when can't find location for inline …
Browse files Browse the repository at this point in the history
…annotation (#692)

* insert back into generated content when can't find location for inline annotation

* updated CHANGELOG

* same for inline:auto annotations

* refactored extracting annotated ranges and cutting them out into separate methods to avoid re-inserting

* fixed wrong annotation
  • Loading branch information
ilyapuchka committed Oct 9, 2018
1 parent baedabe commit 2ecb4c3
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@
- Updated Stencil to 0.13.1 and SwiftStencilKit to 2.7.0
- In Swift templates CLI arguments should now be accessed via `argument` instead of `arguments`, to be consistent with Stencil and JS templates.
- Now in swift templates you can define types, extensions and use other Swift features that require file scope, without using separate files. All templates code is now placed at the top level of the template executable code, instead of being placed inside an extension of `TemplateContext` type.
- Fixed missing generated code annotated with `inline` annotation when corresponding annotation in sources are missing. This generated code will be now present in `*.generated.swift` file.

## 0.15.0

Expand Down
24 changes: 15 additions & 9 deletions Sourcery/Parsing/Utils/InlineParser.swift
Expand Up @@ -17,10 +17,22 @@ internal enum TemplateAnnotationsParser {
}

static func parseAnnotations(_ annotation: String, contents: String, aggregate: Bool = false) -> (contents: String, annotatedRanges: [String: [NSRange]]) {
let (annotatedRanges, rangesToReplace) = annotationRanges(annotation, contents: contents, aggregate: aggregate)

var bridged = contents.bridge()
rangesToReplace
.sorted(by: { $0.location > $1.location })
.forEach {
bridged = bridged.replacingCharacters(in: $0, with: "") as NSString
}
return (bridged as String, annotatedRanges)
}

static func annotationRanges(_ annotation: String, contents: String, aggregate: Bool = false) -> (annotatedRanges: [String: [NSRange]], rangesToReplace: Set<NSRange>) {
let bridged = contents.bridge()
let regex = try? self.regex(annotation: annotation)

var rangesToReplace = [NSRange]()
var rangesToReplace = Set<NSRange>()
var annotatedRanges = [String: [NSRange]]()

regex?.enumerateMatches(in: contents, options: [], range: bridged.entireRange) { result, _, _ in
Expand All @@ -44,16 +56,10 @@ internal enum TemplateAnnotationsParser {
} else {
annotatedRanges[name] = [range]
}
rangesToReplace.append(range)
rangesToReplace.insert(range)
}

rangesToReplace
.reversed()
.forEach {
bridged = bridged.replacingCharacters(in: $0, with: "") as NSString
}

return (bridged as String, annotatedRanges)
return (annotatedRanges, rangesToReplace)
}

static func removingEmptyAnnotations(from content: String) -> String {
Expand Down
41 changes: 26 additions & 15 deletions Sourcery/Sourcery.swift
Expand Up @@ -471,7 +471,7 @@ extension Sourcery {
}

private func processInlineRanges(`for` parsingResult: ParsingResult, in contents: String) throws -> String {
let inline = TemplateAnnotationsParser.parseAnnotations("inline", contents: contents)
var (annotatedRanges, rangesToReplace) = TemplateAnnotationsParser.annotationRanges("inline", contents: contents)

typealias MappedInlineAnnotations = (
range: NSRange,
Expand All @@ -480,26 +480,31 @@ extension Sourcery {
toInsert: String
)

try inline.annotatedRanges
try annotatedRanges
.map { (key: $0, range: $1) }
.compactMap { (key, ranges) -> MappedInlineAnnotations? in
let range = ranges[0]
let generatedBody = contents.bridge().substring(with: range)

guard let (filePath, ranges) = parsingResult.inlineRanges.first(where: { $0.ranges[key] != nil }) else {
guard key.hasPrefix("auto:") else { return nil }
let autoTypeName = key.trimmingPrefix("auto:").components(separatedBy: ".").dropLast().joined(separator: ".")
let toInsert = "\n// sourcery:inline:\(key)\n\(generatedBody)// sourcery:end\n"
if let (filePath, inlineRanges) = parsingResult.inlineRanges.first(where: { $0.ranges[key] != nil }) {
// swiftlint:disable:next force_unwrapping
return MappedInlineAnnotations(range, Path(filePath), inlineRanges[key]!, generatedBody)
}

guard let definition = parsingResult.types.types.first(where: { $0.name == autoTypeName }),
let path = definition.path,
let rangeInFile = try definition.rangeToAppendBody() else {
return nil
}
return MappedInlineAnnotations(range, path, rangeInFile, toInsert)
guard key.hasPrefix("auto:") else {
rangesToReplace.remove(range)
return nil
}
// swiftlint:disable:next force_unwrapping
return MappedInlineAnnotations(range, Path(filePath), ranges[key]!, generatedBody)
let autoTypeName = key.trimmingPrefix("auto:").components(separatedBy: ".").dropLast().joined(separator: ".")
let toInsert = "\n// sourcery:inline:\(key)\n\(generatedBody)// sourcery:end\n"

guard let definition = parsingResult.types.types.first(where: { $0.name == autoTypeName }),
let path = definition.path,
let rangeInFile = try definition.rangeToAppendBody() else {
rangesToReplace.remove(range)
return nil
}
return MappedInlineAnnotations(range, path, rangeInFile, toInsert)
}
.sorted { lhs, rhs in
return lhs.rangeInFile.location > rhs.rangeInFile.location
Expand All @@ -509,7 +514,13 @@ extension Sourcery {
try writeIfChanged(updated, to: path)
}

return inline.contents
var bridged = contents.bridge()
rangesToReplace
.sorted(by: { $0.location > $1.location })
.forEach {
bridged = bridged.replacingCharacters(in: $0, with: "") as NSString
}
return bridged as String
}

private func processFileRanges(`for` parsingResult: ParsingResult, in contents: String, outputPath: Path) -> String {
Expand Down
5 changes: 1 addition & 4 deletions SourceryRuntime/Sources/Attribute.swift
Expand Up @@ -141,22 +141,19 @@ import Foundation
}
}

// sourcery:inline:sourcery:.AutoCoding
// sourcery:inline:Attribute.AutoCoding
/// :nodoc:
required public init?(coder aDecoder: NSCoder) {
guard let name: String = aDecoder.decode(forKey: "name") else { NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: getVaList(["name"])); fatalError() }; self.name = name
guard let arguments: [String: NSObject] = aDecoder.decode(forKey: "arguments") else { NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: getVaList(["arguments"])); fatalError() }; self.arguments = arguments
guard let _description: String = aDecoder.decode(forKey: "_description") else { NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: getVaList(["_description"])); fatalError() }; self._description = _description

}

/// :nodoc:
public func encode(with aCoder: NSCoder) {

aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.arguments, forKey: "arguments")
aCoder.encode(self._description, forKey: "_description")

}
// sourcery:end

Expand Down
30 changes: 30 additions & 0 deletions SourceryTests/SourcerySpec.swift
Expand Up @@ -134,6 +134,36 @@ class SourcerySpecTests: QuickSpec {
expect(result?.withoutWhitespaces).to(equal(expectedResult.withoutWhitespaces))
}

it("does not remove code from within generated template when missing origin") {
update(code: """
class Foo {
// sourcery:inline:Bar.Inlined
// This will be replaced
Last line
// sourcery:end
}
""", in: sourcePath)

expect { try Sourcery(watcherEnabled: false, cacheDisabled: true).processFiles(.sources(Paths(include: [sourcePath])), usingTemplates: Paths(include: [templatePath]), output: output) }.toNot(throwError())

let expectedResult = """
// Generated using Sourcery Major.Minor.Patch — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
// Line One
// sourcery:inline:Foo.Inlined
var property = 2
// Line Three
// sourcery:end
"""

let generatedPath = outputDir + Sourcery().generatedPath(for: templatePath)

let result = try? generatedPath.read(.utf8)
expect(result?.withoutWhitespaces).to(equal(expectedResult.withoutWhitespaces))
}

it("does not create generated file with empty content") {
update(code: """
// sourcery:inline:Foo.Inlined
Expand Down

0 comments on commit 2ecb4c3

Please sign in to comment.