--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/processors/xbl-bindings.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/processors/xbl-bindings.js
@@ -104,22 +104,16 @@ XMLParser.prototype = {
this._currentNode.comments.push(text);
}
};
// -----------------------------------------------------------------------------
// Processor Definition
// -----------------------------------------------------------------------------
-const INDENT_LEVEL = 2;
-
-function indent(count) {
- return " ".repeat(count * INDENT_LEVEL);
-}
-
// Stores any XML parse error
let xmlParseError = null;
// Stores the lines of JS code generated from the XBL
let scriptLines = [];
// Stores a map from the synthetic line number to the real line number
// and column offset.
let lineMap = [];
@@ -145,51 +139,62 @@ function addNodeLines(node, reindent) {
// Strip off any preceeding whitespace only lines. These are often used to
// format the XML and CDATA blocks.
while (lines.length && lines[0].trim() == "") {
indentFirst = true;
startLine++;
lines.shift();
}
+ // Remember the indent of the last blank line, which is the closing part of
+ // the CDATA block.
+ let lastIndent = 0;
+ if (lines.length && lines[lines.length - 1].trim() == "") {
+ lastIndent = lines[lines.length - 1].length;
+ }
+
+ // If the second to last line is also blank and has a higher indent than the
+ // last one, then the CDATA block doesn't close with the closing tag.
+ if (lines.length > 2 && lines[lines.length - 2].trim() == "" &&
+ lines[lines.length - 2].length > lastIndent) {
+ lastIndent = lines[lines.length - 2].length;
+ }
+
// Strip off any whitespace lines at the end. These are often used to line
// up the closing tags
while (lines.length && lines[lines.length - 1].trim() == "") {
lines.pop();
}
if (!indentFirst) {
let firstLine = lines.shift();
- firstLine = " ".repeat(reindent * INDENT_LEVEL) + firstLine;
// ESLint counts columns starting at 1 rather than 0
- lineMap[scriptLines.length] = {
- line: startLine,
- offset: reindent * INDENT_LEVEL - (startColumn - 1)
- };
+ lineMap[scriptLines.length] = { line: startLine, offset: startColumn - 1 };
scriptLines.push(firstLine);
startLine++;
}
// Find the preceeding whitespace for all lines that aren't entirely
// whitespace.
let indents = lines.filter(s => s.trim().length > 0)
.map(s => s.length - s.trimLeft().length);
// Find the smallest indent level in use
let minIndent = Math.min.apply(null, indents);
+ let indent = Math.max(2, minIndent - lastIndent);
for (let line of lines) {
if (line.trim().length == 0) {
// Don't offset lines that are only whitespace, the only possible JS error
// is trailing whitespace and we want it to point at the right place
lineMap[scriptLines.length] = { line: startLine, offset: 0 };
} else {
- line = " ".repeat(reindent * INDENT_LEVEL) + line.substring(minIndent);
+ line = " ".repeat(indent) + line.substring(minIndent);
lineMap[scriptLines.length] = {
line: startLine,
- offset: reindent * INDENT_LEVEL - (minIndent - 1)
+ offset: 1 + indent - minIndent
};
}
scriptLines.push(line);
startLine++;
}
}
@@ -235,28 +240,27 @@ module.exports = {
addSyntheticLine(`this.bindings = {`, bindings.textLine);
for (let binding of bindings.children) {
if (binding.local != "binding" || binding.namespace != NS_XBL) {
continue;
}
- addSyntheticLine(indent(1) +
- `"${binding.attributes.id}": {`, binding.textLine);
+ addSyntheticLine(`"${binding.attributes.id}": {`, binding.textLine);
for (let part of binding.children) {
if (part.namespace != NS_XBL) {
continue;
}
if (part.local == "implementation") {
- addSyntheticLine(indent(2) + `implementation: {`, part.textLine);
+ addSyntheticLine(`implementation: {`, part.textLine);
} else if (part.local == "handlers") {
- addSyntheticLine(indent(2) + `handlers: [`, part.textLine);
+ addSyntheticLine(`handlers: [`, part.textLine);
} else {
continue;
}
for (let item of part.children) {
if (item.namespace != NS_XBL) {
continue;
}
@@ -265,94 +269,88 @@ module.exports = {
case "field": {
// Fields are something like lazy getter functions
// Ignore empty fields
if (item.textContent.trim().length == 0) {
continue;
}
- addSyntheticLine(indent(3) +
- `get ${item.attributes.name}() {`, item.textLine);
- addSyntheticLine(indent(4) +
- `return (`, item.textLine);
+ addSyntheticLine(`get ${item.attributes.name}() {`, item.textLine);
+ addSyntheticLine(`return (`, item.textLine);
// Remove trailing semicolons, as we are adding our own
item.textContent = item.textContent.replace(/;(?=\s*$)/, "");
addNodeLines(item, 5);
- addSyntheticLine(indent(4) + `);`, item.textLine);
- addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ addSyntheticLine(`);`, item.textLine);
+ addSyntheticLine(`},`, item.textEndLine);
break;
}
case "constructor":
case "destructor": {
// Constructors and destructors become function declarations
- addSyntheticLine(indent(3) + `${item.local}() {`, item.textLine);
+ addSyntheticLine(`${item.local}() {`, item.textLine);
addNodeLines(item, 4);
- addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ addSyntheticLine(`},`, item.textEndLine);
break;
}
case "method": {
// Methods become function declarations with the appropriate
// params.
let params = item.children.filter(n => {
return n.local == "parameter" && n.namespace == NS_XBL;
})
.map(n => n.attributes.name)
.join(", ");
let body = item.children.filter(n => {
return n.local == "body" && n.namespace == NS_XBL;
})[0];
- addSyntheticLine(indent(3) +
- `${item.attributes.name}(${params}) {`, item.textLine);
+ addSyntheticLine(`${item.attributes.name}(${params}) {`, item.textLine);
addNodeLines(body, 4);
- addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ addSyntheticLine(`},`, item.textEndLine);
break;
}
case "property": {
// Properties become one or two function declarations
for (let propdef of item.children) {
if (propdef.namespace != NS_XBL) {
continue;
}
if (propdef.local == "setter") {
- addSyntheticLine(indent(3) +
- `set ${item.attributes.name}(val) {`, propdef.textLine);
+ addSyntheticLine(`set ${item.attributes.name}(val) {`, propdef.textLine);
} else if (propdef.local == "getter") {
- addSyntheticLine(indent(3) +
- `get ${item.attributes.name}() {`, propdef.textLine);
+ addSyntheticLine(`get ${item.attributes.name}() {`, propdef.textLine);
} else {
continue;
}
addNodeLines(propdef, 4);
- addSyntheticLine(indent(3) + `},`, propdef.textEndLine);
+ addSyntheticLine(`},`, propdef.textEndLine);
}
break;
}
case "handler": {
// Handlers become a function declaration with an `event`
// parameter.
- addSyntheticLine(indent(3) + `function(event) {`, item.textLine);
+ addSyntheticLine(`function(event) {`, item.textLine);
addNodeLines(item, 4);
- addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ addSyntheticLine(`},`, item.textEndLine);
break;
}
default:
continue;
}
}
- addSyntheticLine(indent(2) +
- (part.local == "implementation" ? `},` : `],`), part.textEndLine);
+ addSyntheticLine((part.local == "implementation" ? `},` : `],`), part.textEndLine);
}
- addSyntheticLine(indent(1) + `},`, binding.textEndLine);
+ addSyntheticLine(`},`, binding.textEndLine);
}
addSyntheticLine(`};`, bindings.textEndLine);
let script = scriptLines.join("\n") + "\n";
return [script];
},
postprocess(messages, filename) {