|
|
'use strict';
const Mixin = require('../../utils/mixin'); const Tokenizer = require('../../tokenizer'); const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin');
class LocationInfoTokenizerMixin extends Mixin { constructor(tokenizer) { super(tokenizer);
this.tokenizer = tokenizer; this.posTracker = Mixin.install(tokenizer.preprocessor, PositionTrackingPreprocessorMixin); this.currentAttrLocation = null; this.ctLoc = null; }
_getCurrentLocation() { return { startLine: this.posTracker.line, startCol: this.posTracker.col, startOffset: this.posTracker.offset, endLine: -1, endCol: -1, endOffset: -1 }; }
_attachCurrentAttrLocationInfo() { this.currentAttrLocation.endLine = this.posTracker.line; this.currentAttrLocation.endCol = this.posTracker.col; this.currentAttrLocation.endOffset = this.posTracker.offset;
const currentToken = this.tokenizer.currentToken; const currentAttr = this.tokenizer.currentAttr;
if (!currentToken.location.attrs) { currentToken.location.attrs = Object.create(null); }
currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation; }
_getOverriddenMethods(mxn, orig) { const methods = { _createStartTagToken() { orig._createStartTagToken.call(this); this.currentToken.location = mxn.ctLoc; },
_createEndTagToken() { orig._createEndTagToken.call(this); this.currentToken.location = mxn.ctLoc; },
_createCommentToken() { orig._createCommentToken.call(this); this.currentToken.location = mxn.ctLoc; },
_createDoctypeToken(initialName) { orig._createDoctypeToken.call(this, initialName); this.currentToken.location = mxn.ctLoc; },
_createCharacterToken(type, ch) { orig._createCharacterToken.call(this, type, ch); this.currentCharacterToken.location = mxn.ctLoc; },
_createEOFToken() { orig._createEOFToken.call(this); this.currentToken.location = mxn._getCurrentLocation(); },
_createAttr(attrNameFirstCh) { orig._createAttr.call(this, attrNameFirstCh); mxn.currentAttrLocation = mxn._getCurrentLocation(); },
_leaveAttrName(toState) { orig._leaveAttrName.call(this, toState); mxn._attachCurrentAttrLocationInfo(); },
_leaveAttrValue(toState) { orig._leaveAttrValue.call(this, toState); mxn._attachCurrentAttrLocationInfo(); },
_emitCurrentToken() { const ctLoc = this.currentToken.location;
//NOTE: if we have pending character token make it's end location equal to the
//current token's start location.
if (this.currentCharacterToken) { this.currentCharacterToken.location.endLine = ctLoc.startLine; this.currentCharacterToken.location.endCol = ctLoc.startCol; this.currentCharacterToken.location.endOffset = ctLoc.startOffset; }
if (this.currentToken.type === Tokenizer.EOF_TOKEN) { ctLoc.endLine = ctLoc.startLine; ctLoc.endCol = ctLoc.startCol; ctLoc.endOffset = ctLoc.startOffset; } else { ctLoc.endLine = mxn.posTracker.line; ctLoc.endCol = mxn.posTracker.col + 1; ctLoc.endOffset = mxn.posTracker.offset + 1; }
orig._emitCurrentToken.call(this); },
_emitCurrentCharacterToken() { const ctLoc = this.currentCharacterToken && this.currentCharacterToken.location;
//NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
//then set it's location at the current preprocessor position.
//We don't need to increment preprocessor position, since character token
//emission is always forced by the start of the next character token here.
//So, we already have advanced position.
if (ctLoc && ctLoc.endOffset === -1) { ctLoc.endLine = mxn.posTracker.line; ctLoc.endCol = mxn.posTracker.col; ctLoc.endOffset = mxn.posTracker.offset; }
orig._emitCurrentCharacterToken.call(this); } };
//NOTE: patch initial states for each mode to obtain token start position
Object.keys(Tokenizer.MODE).forEach(modeName => { const state = Tokenizer.MODE[modeName];
methods[state] = function(cp) { mxn.ctLoc = mxn._getCurrentLocation(); orig[state].call(this, cp); }; });
return methods; } }
module.exports = LocationInfoTokenizerMixin;
|