diff --git a/lib/carto/tree/call.js b/lib/carto/tree/call.js index 75b2377..8ba29d6 100644 --- a/lib/carto/tree/call.js +++ b/lib/carto/tree/call.js @@ -22,7 +22,7 @@ tree.Call.prototype = { // we try to pass a variable to a function, like: `saturate(@color)`. // The function should receive the value, not the variable. // - eval: function(env) { + 'eval': function(env) { var args = this.args.map(function(a) { return a.eval(env); }); for (var i = 0; i < args.length; i++) { @@ -55,11 +55,11 @@ tree.Call.prototype = { if (!fn) { var functions = tree.Reference.mapnikFunctions(); // cheap closest, needs improvement. - var name_array = this.name.split(''); + var name = this.name; var mean = functions.map(function(f) { - return [f[0], _.intersection(name_array, f[0].split('')).length, f[1]]; + return [f[0], tree.Reference.editDistance(name, f[0]), f[1]]; }).sort(function(a, b) { - return b[1] - a[1]; + return a[1] - b[1]; }); env.error({ message: 'unknown function ' + this.name + '(), did you mean ' + diff --git a/lib/carto/tree/reference.js b/lib/carto/tree/reference.js index e90a545..8db1777 100644 --- a/lib/carto/tree/reference.js +++ b/lib/carto/tree/reference.js @@ -138,6 +138,31 @@ tree.Reference.isFont = function(selector) { return tree.Reference.selector(selector).validate == 'font'; }; +// https://gist.github.com/982927 +tree.Reference.editDistance = function(a, b){ + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + + var matrix = []; + + for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; } + for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; } + + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i-1) == a.charAt(j-1)) { + matrix[i][j] = matrix[i-1][j-1]; + } else { + matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution + Math.min(matrix[i][j-1] + 1, // insertion + matrix[i-1][j] + 1)); // deletion + } + } + } + + return matrix[b.length][a.length]; +}; + tree.Reference.validValue = function(env, selector, value) { var i, j; // TODO: handle in reusable way diff --git a/lib/carto/tree/rule.js b/lib/carto/tree/rule.js index bf883d9..6f50e61 100644 --- a/lib/carto/tree/rule.js +++ b/lib/carto/tree/rule.js @@ -39,8 +39,18 @@ tree.Rule.prototype.toString = function() { // properties in reference.json which specify serialization=content tree.Rule.prototype.toXML = function(env, content, sep, format) { if (!tree.Reference.validSelector(this.name)) { + var name = this.name; + var mean = tree.Reference.selectors.map(function(f) { + return [f, tree.Reference.editDistance(name, f)]; + }).sort(function(a, b) { + return a[1] - b[1]; + }); + var mean_message = ''; + if (mean[0][1] < 3) { + mean_message = '. Did you mean ' + mean[0][0] + '?'; + } return env.error({ - message: "Unrecognized rule: " + this.name, + message: "Unrecognized rule: " + this.name + mean_message, index: this.index, type: 'syntax', filename: this.filename diff --git a/test/errorhandling/invalid_property.result b/test/errorhandling/invalid_property.result index 81a7cb9..6daf2ef 100644 --- a/test/errorhandling/invalid_property.result +++ b/test/errorhandling/invalid_property.result @@ -1 +1 @@ -invalid_property.mss:3:2 Unrecognized rule: polygonopacity +invalid_property.mss:3:2 Unrecognized rule: polygonopacity. Did you mean polygon-opacity?