Skip to content

Commit

Permalink
[[FIX]] Improve handling of tab indentation.
Browse files Browse the repository at this point in the history
By tracking the column number both with and without soft tabs,
we enable better messages for errors and warnings. This is especially
useful for editor linting integrations.

Fixed jshint#3151
  • Loading branch information
TzviPM authored and jugglinmike committed Aug 5, 2018
1 parent e1936af commit 470b37d
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 96 deletions.
95 changes: 56 additions & 39 deletions src/lex.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,10 @@ function Lexer(source) {

this.line = 0;
this.char = 1;
this.originalChar = 1;
this.from = 1;
this.input = "";
this.originalInput = "";
this.inComment = false;
this.context = [];
this.templateStarts = [];
Expand Down Expand Up @@ -166,17 +168,24 @@ Lexer.prototype = {
* Return the next i character without actually moving the
* char pointer.
*/
peek: function(i) {
return this.input.charAt(i || 0);
peek: function(i, str) {
str = str || this.input;
return str.charAt(i || 0);
},

/*
* Move the char pointer forward i times.
*/
skip: function(i) {
i = i || 1;
this.char += i;
this.input = this.input.slice(i);
var tabExpandedI = i;
if (i === 1 && this.peek(0, this.originalInput) === '\t') {
tabExpandedI = state.tab.length;
}
this.char += tabExpandedI;
this.originalChar += i;
this.input = this.input.slice(tabExpandedI);
this.originalInput = this.originalInput.slice(i);
},

/*
Expand Down Expand Up @@ -393,7 +402,7 @@ Lexer.prototype = {
var ch2 = this.peek(1);
var rest = this.input.substr(2);
var startLine = this.line;
var startChar = this.char;
var startChar = this.originalChar;
var self = this;

// Create a comment token object and make sure it
Expand Down Expand Up @@ -802,7 +811,7 @@ Lexer.prototype = {
{
code: "W119",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "Octal integer literal", "6" ]
},
checks,
Expand All @@ -825,7 +834,7 @@ Lexer.prototype = {
{
code: "W119",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "Binary integer literal", "6" ]
},
checks,
Expand Down Expand Up @@ -968,7 +977,7 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W114",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "\\'" ]
}, checks, function() {return state.jsonMode; });
break;
Expand Down Expand Up @@ -996,7 +1005,7 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W115",
line: this.line,
character: this.char
character: this.originalChar
}, checks,
function() { return n >= 0 && n <= 7 && state.isStrict(); });
break;
Expand All @@ -1011,7 +1020,7 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W115",
line: this.line,
character: this.char
character: this.originalChar
}, checks,
function() { return state.isStrict(); });
break;
Expand All @@ -1024,7 +1033,7 @@ Lexer.prototype = {
this.trigger("warning", {
code: "W052",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "u" + sequence ]
});
}
Expand All @@ -1035,7 +1044,7 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W114",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "\\v" ]
}, checks, function() { return state.jsonMode; });

Expand All @@ -1047,7 +1056,7 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W114",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "\\x-" ]
}, checks, function() { return state.jsonMode; });

Expand Down Expand Up @@ -1082,7 +1091,7 @@ Lexer.prototype = {
var value = "";
var ch;
var startLine = this.line;
var startChar = this.char;
var startChar = this.originalChar;
var depth = this.templateStarts.length;

if (this.peek() === "`") {
Expand All @@ -1092,7 +1101,7 @@ Lexer.prototype = {
{
code: "W119",
line: this.line,
character: this.char,
character: this.originalChar,
data: ["template literal syntax", "6"]
},
checks,
Expand All @@ -1101,7 +1110,11 @@ Lexer.prototype = {
}
// Template must start with a backtick.
tokenType = Token.TemplateHead;
this.templateStarts.push({ line: this.line, char: this.char });
this.templateStarts.push({
line: this.line,
char: this.char,
originalChar: this.originalChar
});
depth = this.templateStarts.length;
this.skip(1);
this.pushContext(Context.Template);
Expand All @@ -1122,7 +1135,7 @@ Lexer.prototype = {
this.trigger("error", {
code: "E052",
line: startPos.line,
character: startPos.char
character: startPos.originalChar
});
return {
type: tokenType,
Expand Down Expand Up @@ -1199,12 +1212,12 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W108",
line: this.line,
character: this.char // +1?
character: this.originalChar // +1?
}, checks, function() { return state.jsonMode && quote !== "\""; });

var value = "";
var startLine = this.line;
var startChar = this.char;
var startChar = this.originalChar;
var allowNewLine = false;

this.skip();
Expand All @@ -1225,7 +1238,7 @@ Lexer.prototype = {
this.trigger("warning", {
code: "W112",
line: this.line,
character: this.char
character: this.originalChar
});
} else {
allowNewLine = false;
Expand All @@ -1236,13 +1249,13 @@ Lexer.prototype = {
this.triggerAsync("warning", {
code: "W043",
line: this.line,
character: this.char
character: this.originalChar
}, checks, function() { return !state.option.multistr; });

this.triggerAsync("warning", {
code: "W042",
line: this.line,
character: this.char
character: this.originalChar
}, checks, function() { return state.jsonMode && state.option.multistr; });
}

Expand Down Expand Up @@ -1274,7 +1287,7 @@ Lexer.prototype = {
{
code: "W113",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "<non-printable>" ]
},
checks,
Expand Down Expand Up @@ -1342,7 +1355,7 @@ Lexer.prototype = {
{
code: "W048",
line: this.line,
character: this.char
character: this.originalChar
},
checks,
function() { return true; }
Expand All @@ -1357,7 +1370,7 @@ Lexer.prototype = {
{
code: "W049",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ char ]
},
checks,
Expand Down Expand Up @@ -1469,7 +1482,7 @@ Lexer.prototype = {
{
code: "W119",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "Sticky RegExp flag", "6" ]
},
checks,
Expand Down Expand Up @@ -1504,7 +1517,7 @@ Lexer.prototype = {
this.trigger("error", {
code: "E016",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ malformedDesc ]
});
}
Expand All @@ -1521,17 +1534,18 @@ Lexer.prototype = {
* can be mistakenly typed on OS X with option-space. Non UTF-8 web
* pages with non-breaking pages produce syntax errors.
*/
scanNonBreakingSpaces: function() {
scanNonBreakingSpaces: function(str) {
str = str || this.input;
return state.option.nonbsp ?
this.input.search(/(\u00A0)/) : -1;
str.search(/(\u00A0)/) : -1;
},

/*
* Produce the next raw token or return 'null' if no tokens can be matched.
* This method skips over all space characters.
*/
next: function(checks) {
this.from = this.char;
this.from = this.originalChar;

// Move to the next non-space character.
while (reg.whitespace.test(this.peek())) {
Expand Down Expand Up @@ -1574,15 +1588,17 @@ Lexer.prototype = {
* switched, this method also checks for other minor warnings.
*/
nextLine: function(checks) {
var char;
var char, originalChar;

if (this.line >= this.getLines().length) {
return false;
}

this.input = this.getLines()[this.line];
this.originalInput = this.getLines()[this.line];
this.line += 1;
this.char = 1;
this.originalChar = 1;
this.from = 1;

var inputTrimmed = this.input.trim();
Expand All @@ -1608,10 +1624,11 @@ Lexer.prototype = {
}

char = this.scanNonBreakingSpaces();
originalChar = this.scanNonBreakingSpaces(this.originalInput);
if (char >= 0) {
this.triggerAsync(
"warning",
{ code: "W125", line: this.line, character: char + 1 },
{ code: "W125", line: this.line, character: originalChar + 1 },
checks,
function() { return true; }
);
Expand All @@ -1633,7 +1650,7 @@ Lexer.prototype = {
if (shouldTriggerError) {
this.triggerAsync(
"warning",
{ code: "W101", line: this.line, character: this.input.length },
{ code: "W101", line: this.line, character: this.originalInput.length },
checks,
function() { return true; }
);
Expand Down Expand Up @@ -1703,7 +1720,7 @@ Lexer.prototype = {
obj.type = obj.type || type;
obj.value = value;
obj.line = this.line;
obj.character = this.char;
obj.character = this.originalChar;
obj.from = this.from;
if (obj.identifier && token) obj.raw_text = token.text || token.value;
if (token && token.startLine && token.startLine !== this.line) {
Expand Down Expand Up @@ -1753,7 +1770,7 @@ Lexer.prototype = {
this.trigger("error", {
code: "E024",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ this.peek() ]
});

Expand Down Expand Up @@ -1842,22 +1859,22 @@ Lexer.prototype = {
this.trigger("warning", {
code: "W045",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ token.value ]
});
}

this.triggerAsync("warning", {
code: "W114",
line: this.line,
character: this.char,
character: this.originalChar,
data: [ "0x-" ]
}, checks, function() { return token.base === 16 && state.jsonMode; });

this.triggerAsync("warning", {
code: "W115",
line: this.line,
character: this.char
character: this.originalChar
}, checks, function() {
return state.isStrict() && token.base === 8 && token.isLegacy;
});
Expand Down Expand Up @@ -1885,7 +1902,7 @@ Lexer.prototype = {
type: token.commentType,
isSpecial: token.isSpecial,
line: this.line,
character: this.char,
character: this.originalChar,
from: this.from
};
}
Expand Down

0 comments on commit 470b37d

Please sign in to comment.