diff --git a/Sources/Errors.swift b/Sources/Errors.swift index ab5eda3..76c7559 100644 --- a/Sources/Errors.swift +++ b/Sources/Errors.swift @@ -32,18 +32,15 @@ public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible { } static func description(reason: String, lexeme: Lexeme?, template: Template?) -> String { - if let template = template, let range = lexeme?.range { - let templateName = template.name.map({ "\($0):" }) ?? "" - let tokenContent = template.templateString.substring(with: range) - let line = template.templateString.rangeLine(range) - let highlight = "\(String(Array(repeating: " ", count: line.offset)))^\(String(Array(repeating: "~", count: max(tokenContent.length - 1, 0))))" - - return "\(templateName)\(line.number):\(line.offset): error: \(reason)\n" - + "\(line.content)\n" - + "\(highlight)\n" - } else { - return reason - } + guard let template = template, let lexeme = lexeme else { return reason } + let templateName = template.name.map({ "\($0):" }) ?? "" + let range = template.templateString.range(of: lexeme.contents, range: lexeme.range) ?? lexeme.range + let line = template.templateString.rangeLine(range) + let highlight = "\(String(Array(repeating: " ", count: line.offset)))^\(String(Array(repeating: "~", count: max(lexeme.contents.length - 1, 0))))" + + return "\(templateName)\(line.number):\(line.offset): error: \(reason)\n" + + "\(line.content)\n" + + "\(highlight)\n" } init(reason: String, lexeme: Lexeme? = nil, template: Template? = nil, parentError: Error? = nil) { diff --git a/Sources/Lexer.swift b/Sources/Lexer.swift index d9f972f..9a7052b 100644 --- a/Sources/Lexer.swift +++ b/Sources/Lexer.swift @@ -77,6 +77,7 @@ struct Lexer { } protocol Lexeme { + var contents: String { get } var range: Range { get } } diff --git a/Sources/Node.swift b/Sources/Node.swift index 10ee16a..5656b79 100644 --- a/Sources/Node.swift +++ b/Sources/Node.swift @@ -15,13 +15,9 @@ public func renderNodes(_ nodes:[NodeType], _ context:Context) throws -> String do { return try $0.render(context) } catch { - if var syntaxError = error as? TemplateSyntaxError, syntaxError.lexeme == nil, let token = $0.token { - if let contentsRange = context.environment.template?.templateString.range(of: token.contents, range: token.range) { - syntaxError.lexeme = Token.block(value: token.contents, at: contentsRange) - } else { - syntaxError.lexeme = token - } - throw syntaxError + if var error = error as? TemplateSyntaxError { + error.lexeme = error.lexeme ?? $0.token + throw error } else { throw error } diff --git a/Tests/StencilTests/EnvironmentSpec.swift b/Tests/StencilTests/EnvironmentSpec.swift index 4d98f7b..67d2152 100644 --- a/Tests/StencilTests/EnvironmentSpec.swift +++ b/Tests/StencilTests/EnvironmentSpec.swift @@ -41,41 +41,25 @@ func testEnvironment() { $0.it("reports syntax error on invalid for tag syntax") { let template: Template = "Hello {% for name in %}{{ name }}, {% endfor %}!" - let error = expectedSyntaxError( - token: "{% for name in %}", - template: template, - description: "'for' statements should use the following syntax 'for x in y where condition'." - ) + let error = expectedSyntaxError(token: "for name in", template: template, description: "'for' statements should use the following syntax 'for x in y where condition'.") try expect(try environment.renderTemplate(string: template.templateString, context:["names": ["Bob", "Alice"]])).toThrow(error) } $0.it("reports syntax error on missing endfor") { let template: Template = "{% for name in names %}{{ name }}" - let error = expectedSyntaxError( - token: "{% for name in names %}", - template: template, - description: "`endfor` was not found." - ) + let error = expectedSyntaxError(token: "for name in names", template: template, description: "`endfor` was not found.") try expect(try environment.renderTemplate(string: template.templateString, context: ["names": ["Bob", "Alice"]])).toThrow(error) } $0.it("reports syntax error on unknown tag") { let template: Template = "{% for name in names %}{{ name }}{% end %}" - let error = expectedSyntaxError( - token: "{% end %}", - template: template, - description: "Unknown template tag 'end'" - ) + let error = expectedSyntaxError(token: "end", template: template, description: "Unknown template tag 'end'") try expect(try environment.renderTemplate(string: template.templateString, context: ["names": ["Bob", "Alice"]])).toThrow(error) } $0.context("given unknown filter") { func expectedFilterError(token: String, template: Template) -> TemplateSyntaxError { - return expectedSyntaxError( - token: token, - template: template, - description: "Unknown filter 'unknown'" - ) + return expectedSyntaxError(token: token, template: template, description: "Unknown filter 'unknown'") } $0.it("reports syntax error in for tag") { @@ -110,7 +94,7 @@ func testEnvironment() { $0.it("reports syntax error in filter tag") { let template: Template = "{% filter unknown %}Text{% endfilter %}" - let error = expectedFilterError(token: "{% filter unknown %}", template: template) + let error = expectedFilterError(token: "filter unknown", template: template) try expect(try environment.renderTemplate(string: template.templateString, context: [:])).toThrow(error) } @@ -228,7 +212,6 @@ func testEnvironment() { var error = expectedSyntaxError(token: "include \"invalid-include.html\"", template: template, description: "Unknown filter 'unknown'") error.parentError = parentError - try expect(environment.renderTemplate(string: template.templateString, context: ["target": "World"])).toThrow(error) } @@ -297,7 +280,7 @@ func testEnvironment() { let template = Template.init(templateString: "{% extends \"base.html\" %}\n" + "{% block body %}Child {{ target|unknown }}{% endblock %}", environment: environment, name: nil) - let error = expectedSyntaxError(token: "{{ target|unknown }}", template: template, description: "filter error") + let error = expectedSyntaxError(token: "target|unknown", template: template, description: "filter error") try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error) }