You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
5.3 KiB

  1. 'use strict';
  2. const Mixin = require('../../utils/mixin');
  3. const Tokenizer = require('../../tokenizer');
  4. const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin');
  5. class LocationInfoTokenizerMixin extends Mixin {
  6. constructor(tokenizer) {
  7. super(tokenizer);
  8. this.tokenizer = tokenizer;
  9. this.posTracker = Mixin.install(tokenizer.preprocessor, PositionTrackingPreprocessorMixin);
  10. this.currentAttrLocation = null;
  11. this.ctLoc = null;
  12. }
  13. _getCurrentLocation() {
  14. return {
  15. startLine: this.posTracker.line,
  16. startCol: this.posTracker.col,
  17. startOffset: this.posTracker.offset,
  18. endLine: -1,
  19. endCol: -1,
  20. endOffset: -1
  21. };
  22. }
  23. _attachCurrentAttrLocationInfo() {
  24. this.currentAttrLocation.endLine = this.posTracker.line;
  25. this.currentAttrLocation.endCol = this.posTracker.col;
  26. this.currentAttrLocation.endOffset = this.posTracker.offset;
  27. const currentToken = this.tokenizer.currentToken;
  28. const currentAttr = this.tokenizer.currentAttr;
  29. if (!currentToken.location.attrs) {
  30. currentToken.location.attrs = Object.create(null);
  31. }
  32. currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation;
  33. }
  34. _getOverriddenMethods(mxn, orig) {
  35. const methods = {
  36. _createStartTagToken() {
  37. orig._createStartTagToken.call(this);
  38. this.currentToken.location = mxn.ctLoc;
  39. },
  40. _createEndTagToken() {
  41. orig._createEndTagToken.call(this);
  42. this.currentToken.location = mxn.ctLoc;
  43. },
  44. _createCommentToken() {
  45. orig._createCommentToken.call(this);
  46. this.currentToken.location = mxn.ctLoc;
  47. },
  48. _createDoctypeToken(initialName) {
  49. orig._createDoctypeToken.call(this, initialName);
  50. this.currentToken.location = mxn.ctLoc;
  51. },
  52. _createCharacterToken(type, ch) {
  53. orig._createCharacterToken.call(this, type, ch);
  54. this.currentCharacterToken.location = mxn.ctLoc;
  55. },
  56. _createEOFToken() {
  57. orig._createEOFToken.call(this);
  58. this.currentToken.location = mxn._getCurrentLocation();
  59. },
  60. _createAttr(attrNameFirstCh) {
  61. orig._createAttr.call(this, attrNameFirstCh);
  62. mxn.currentAttrLocation = mxn._getCurrentLocation();
  63. },
  64. _leaveAttrName(toState) {
  65. orig._leaveAttrName.call(this, toState);
  66. mxn._attachCurrentAttrLocationInfo();
  67. },
  68. _leaveAttrValue(toState) {
  69. orig._leaveAttrValue.call(this, toState);
  70. mxn._attachCurrentAttrLocationInfo();
  71. },
  72. _emitCurrentToken() {
  73. const ctLoc = this.currentToken.location;
  74. //NOTE: if we have pending character token make it's end location equal to the
  75. //current token's start location.
  76. if (this.currentCharacterToken) {
  77. this.currentCharacterToken.location.endLine = ctLoc.startLine;
  78. this.currentCharacterToken.location.endCol = ctLoc.startCol;
  79. this.currentCharacterToken.location.endOffset = ctLoc.startOffset;
  80. }
  81. if (this.currentToken.type === Tokenizer.EOF_TOKEN) {
  82. ctLoc.endLine = ctLoc.startLine;
  83. ctLoc.endCol = ctLoc.startCol;
  84. ctLoc.endOffset = ctLoc.startOffset;
  85. } else {
  86. ctLoc.endLine = mxn.posTracker.line;
  87. ctLoc.endCol = mxn.posTracker.col + 1;
  88. ctLoc.endOffset = mxn.posTracker.offset + 1;
  89. }
  90. orig._emitCurrentToken.call(this);
  91. },
  92. _emitCurrentCharacterToken() {
  93. const ctLoc = this.currentCharacterToken && this.currentCharacterToken.location;
  94. //NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
  95. //then set it's location at the current preprocessor position.
  96. //We don't need to increment preprocessor position, since character token
  97. //emission is always forced by the start of the next character token here.
  98. //So, we already have advanced position.
  99. if (ctLoc && ctLoc.endOffset === -1) {
  100. ctLoc.endLine = mxn.posTracker.line;
  101. ctLoc.endCol = mxn.posTracker.col;
  102. ctLoc.endOffset = mxn.posTracker.offset;
  103. }
  104. orig._emitCurrentCharacterToken.call(this);
  105. }
  106. };
  107. //NOTE: patch initial states for each mode to obtain token start position
  108. Object.keys(Tokenizer.MODE).forEach(modeName => {
  109. const state = Tokenizer.MODE[modeName];
  110. methods[state] = function(cp) {
  111. mxn.ctLoc = mxn._getCurrentLocation();
  112. orig[state].call(this, cp);
  113. };
  114. });
  115. return methods;
  116. }
  117. }
  118. module.exports = LocationInfoTokenizerMixin;