unified setting higlighting range for errors

This commit is contained in:
Ilya Puchka
2017-12-25 01:10:58 +01:00
parent c486617854
commit bb3f33724b
4 changed files with 19 additions and 42 deletions

View File

@@ -32,18 +32,15 @@ public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible {
} }
static func description(reason: String, lexeme: Lexeme?, template: Template?) -> String { static func description(reason: String, lexeme: Lexeme?, template: Template?) -> String {
if let template = template, let range = lexeme?.range { guard let template = template, let lexeme = lexeme else { return reason }
let templateName = template.name.map({ "\($0):" }) ?? "" let templateName = template.name.map({ "\($0):" }) ?? ""
let tokenContent = template.templateString.substring(with: range) let range = template.templateString.range(of: lexeme.contents, range: lexeme.range) ?? lexeme.range
let line = template.templateString.rangeLine(range) let line = template.templateString.rangeLine(range)
let highlight = "\(String(Array(repeating: " ", count: line.offset)))^\(String(Array(repeating: "~", count: max(tokenContent.length - 1, 0))))" 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" return "\(templateName)\(line.number):\(line.offset): error: \(reason)\n"
+ "\(line.content)\n" + "\(line.content)\n"
+ "\(highlight)\n" + "\(highlight)\n"
} else {
return reason
}
} }
init(reason: String, lexeme: Lexeme? = nil, template: Template? = nil, parentError: Error? = nil) { init(reason: String, lexeme: Lexeme? = nil, template: Template? = nil, parentError: Error? = nil) {

View File

@@ -77,6 +77,7 @@ struct Lexer {
} }
protocol Lexeme { protocol Lexeme {
var contents: String { get }
var range: Range<String.Index> { get } var range: Range<String.Index> { get }
} }

View File

@@ -15,13 +15,9 @@ public func renderNodes(_ nodes:[NodeType], _ context:Context) throws -> String
do { do {
return try $0.render(context) return try $0.render(context)
} catch { } catch {
if var syntaxError = error as? TemplateSyntaxError, syntaxError.lexeme == nil, let token = $0.token { if var error = error as? TemplateSyntaxError {
if let contentsRange = context.environment.template?.templateString.range(of: token.contents, range: token.range) { error.lexeme = error.lexeme ?? $0.token
syntaxError.lexeme = Token.block(value: token.contents, at: contentsRange) throw error
} else {
syntaxError.lexeme = token
}
throw syntaxError
} else { } else {
throw error throw error
} }

View File

@@ -41,41 +41,25 @@ func testEnvironment() {
$0.it("reports syntax error on invalid for tag syntax") { $0.it("reports syntax error on invalid for tag syntax") {
let template: Template = "Hello {% for name in %}{{ name }}, {% endfor %}!" let template: Template = "Hello {% for name in %}{{ name }}, {% endfor %}!"
let error = expectedSyntaxError( let error = expectedSyntaxError(token: "for name in", template: template, description: "'for' statements should use the following syntax 'for x in y where condition'.")
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) try expect(try environment.renderTemplate(string: template.templateString, context:["names": ["Bob", "Alice"]])).toThrow(error)
} }
$0.it("reports syntax error on missing endfor") { $0.it("reports syntax error on missing endfor") {
let template: Template = "{% for name in names %}{{ name }}" let template: Template = "{% for name in names %}{{ name }}"
let error = expectedSyntaxError( let error = expectedSyntaxError(token: "for name in names", template: template, description: "`endfor` was not found.")
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) try expect(try environment.renderTemplate(string: template.templateString, context: ["names": ["Bob", "Alice"]])).toThrow(error)
} }
$0.it("reports syntax error on unknown tag") { $0.it("reports syntax error on unknown tag") {
let template: Template = "{% for name in names %}{{ name }}{% end %}" let template: Template = "{% for name in names %}{{ name }}{% end %}"
let error = expectedSyntaxError( let error = expectedSyntaxError(token: "end", template: template, description: "Unknown template tag 'end'")
token: "{% end %}",
template: template,
description: "Unknown template tag 'end'"
)
try expect(try environment.renderTemplate(string: template.templateString, context: ["names": ["Bob", "Alice"]])).toThrow(error) try expect(try environment.renderTemplate(string: template.templateString, context: ["names": ["Bob", "Alice"]])).toThrow(error)
} }
$0.context("given unknown filter") { $0.context("given unknown filter") {
func expectedFilterError(token: String, template: Template) -> TemplateSyntaxError { func expectedFilterError(token: String, template: Template) -> TemplateSyntaxError {
return expectedSyntaxError( return expectedSyntaxError(token: token, template: template, description: "Unknown filter 'unknown'")
token: token,
template: template,
description: "Unknown filter 'unknown'"
)
} }
$0.it("reports syntax error in for tag") { $0.it("reports syntax error in for tag") {
@@ -110,7 +94,7 @@ func testEnvironment() {
$0.it("reports syntax error in filter tag") { $0.it("reports syntax error in filter tag") {
let template: Template = "{% filter unknown %}Text{% endfilter %}" 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) 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'") var error = expectedSyntaxError(token: "include \"invalid-include.html\"", template: template, description: "Unknown filter 'unknown'")
error.parentError = parentError error.parentError = parentError
try expect(environment.renderTemplate(string: template.templateString, context: ["target": "World"])).toThrow(error) 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" + let template = Template.init(templateString: "{% extends \"base.html\" %}\n" +
"{% block body %}Child {{ target|unknown }}{% endblock %}", environment: environment, name: nil) "{% 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) try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
} }