docs: function getEncodedLexicalClassifications(text, lexState, syntacticClassifierAbsent) {
              var offset = 0;
              var token = 0 /* Unknown */;
              var lastNonTriviaToken = 0 /* Unknown */;
              // Empty out the template stack for reuse.
              while (templateStack.length > 0) {
                  templateStack.pop();
              }
              // If we're in a string literal, then prepend: "\
              // (and a newline).  That way when we lex we'll think we're still in a string literal.
              //
              // If we're in a multiline comment, then prepend: /*
              // (and a newline).  That way when we lex we'll think we're still in a multiline comment.
              switch (lexState) {
                  case 3 /* InDoubleQuoteStringLiteral */:
                      text = "\"\\\n" + text;
                      offset = 3;
                      break;
                  case 2 /* InSingleQuoteStringLiteral */:
                      text = "'\\\n" + text;
                      offset = 3;
                      break;
                  case 1 /* InMultiLineCommentTrivia */:
                      text = "/*\n" + text;
                      offset = 3;
                      break;
                  case 4 /* InTemplateHeadOrNoSubstitutionTemplate */:
                      text = "`\n" + text;
                      offset = 2;
                      break;
                  case 5 /* InTemplateMiddleOrTail */:
                      text = "}\n" + text;
                      offset = 2;
                  // fallthrough
                  case 6 /* InTemplateSubstitutionPosition */:
                      templateStack.push(12 /* TemplateHead */);
                      break;
              }
              scanner.setText(text);
              var result = {
                  endOfLineState: 0 /* None */,
                  spans: []
              };
              // We can run into an unfortunate interaction between the lexical and syntactic classifier
              // when the user is typing something generic.  Consider the case where the user types:
              //
              //      Foo<number
              //
              // From the lexical classifier's perspective, 'number' is a keyword, and so the word will
              // be classified as such.  However, from the syntactic classifier's tree-based perspective
              // this is simply an expression with the identifier 'number' on the RHS of the less than
              // token.  So the classification will go back to being an identifier.  The moment the user
              // types again, number will become a keyword, then an identifier, etc. etc.
              //
              // To try to avoid this problem, we avoid classifying contextual keywords as keywords
              // when the user is potentially typing something generic.  We just can't do a good enough
              // job at the lexical level, and so well leave it up to the syntactic classifier to make
              // the determination.
              //
              // In order to determine if the user is potentially typing something generic, we use a
              // weak heuristic where we track < and > tokens.  It's a weak heuristic, but should
              // work well enough in practice.
              var angleBracketStack = 0;
              do {
                  token = scanner.scan();
                  if (!ts.isTrivia(token)) {
                      if ((token === 39 /* SlashToken */ || token === 61 /* SlashEqualsToken */) && !noRegexTable[lastNonTriviaToken]) {
                          if (scanner.reScanSlashToken() === 10 /* RegularExpressionLiteral */) {
                              token = 10 /* RegularExpressionLiteral */;
                          }
                      }
                      else if (lastNonTriviaToken === 21 /* DotToken */ && isKeyword(token)) {
                          token = 69 /* Identifier */;
                      }
                      else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
                          // We have two keywords in a row.  Only treat the second as a keyword if
                          // it's a sequence that could legally occur in the language.  Otherwise
                          // treat it as an identifier.  This way, if someone writes "private var"
                          // we recognize that 'var' is actually an identifier here.
                          token = 69 /* Identifier */;
                      }
                      else if (lastNonTriviaToken === 69 /* Identifier */ &&
                          token === 25 /* LessThanToken */) {
                          // Could be the start of something generic.  Keep track of that by bumping
                          // up the current count of generic contexts we may be in.
                          angleBracketStack++;
                      }
                      else if (token === 27 /* GreaterThanToken */ && angleBracketStack > 0) {
                          // If we think we're currently in something generic, then mark that that
                          // generic entity is complete.
                          angleBracketStack--;
                      }
                      else if (token === 117 /* AnyKeyword */ ||
                          token === 132 /* StringKeyword */ ||
                          token === 130 /* NumberKeyword */ ||
                          token === 120 /* BooleanKeyword */ ||
                          token === 133 /* SymbolKeyword */) {
                          if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
                              // If it looks like we're could be in something generic, don't classify this
                              // as a keyword.  We may just get overwritten by the syntactic classifier,
                              // causing a noisy experience for the user.
                              token = 69 /* Identifier */;
                          }
                      }
                      else if (token === 12 /* TemplateHead */) {
                          templateStack.push(token);
                      }
                      else if (token === 15 /* OpenBraceToken */) {
                          // If we don't have anything on the template stack,
                          // then we aren't trying to keep track of a previously scanned template head.
                          if (templateStack.length > 0) {
                              templateStack.push(token);
                          }
                      }
                      else if (token === 16 /* CloseBraceToken */) {
                          // If we don't have anything on the template stack,
                          // then we aren't trying to keep track of a previously scanned template head.
                          if (templateStack.length > 0) {
                              var lastTemplateStackToken = ts.lastOrUndefined(templateStack);
                              if (lastTemplateStackToken === 12 /* TemplateHead */) {
                                  token = scanner.reScanTemplateToken();
                                  // Only pop on a TemplateTail; a TemplateMiddle indicates there is more for us.
                                  if (token === 14 /* TemplateTail */) {
                                      templateStack.pop();
                                  }
                                  else {
                                      ts.Debug.assert(token === 13 /* TemplateMiddle */, "Should have been a template middle. Was " + token);
                                  }
                              }
                              else {
                                  ts.Debug.assert(lastTemplateStackToken === 15 /* OpenBraceToken */, "Should have been an open brace. Was: " + token);
                                  templateStack.pop();
                              }
                          }
                      }
                      lastNonTriviaToken = token;
                  }
                  processToken();
              } while (token !== 1 /* EndOfFileToken */);
              return result;
              function processToken() {
                  var start = scanner.getTokenPos();
                  var end = scanner.getTextPos();
                  addResult(start, end, classFromKind(token));
                  if (end >= text.length) {
                      if (token === 9 /* StringLiteral */) {
                          // Check to see if we finished up on a multiline string literal.
                          var tokenText = scanner.getTokenText();
                          if (scanner.isUnterminated()) {
                              var lastCharIndex = tokenText.length - 1;
                              var numBackslashes = 0;
                              while (tokenText.charCodeAt(lastCharIndex - numBackslashes) === 92 /* backslash */) {
                                  numBackslashes++;
                              }
                              // If we have an odd number of backslashes, then the multiline string is unclosed
                              if (numBackslashes & 1) {
                                  var quoteChar = tokenText.charCodeAt(0);
                                  result.endOfLineState = quoteChar === 34 /* doubleQuote */
                                      ? 3 /* InDoubleQuoteStringLiteral */
                                      : 2 /* InSingleQuoteStringLiteral */;
                              }
                          }
                      }
                      else if (token === 3 /* MultiLineCommentTrivia */) {
                          // Check to see if the multiline comment was unclosed.
                          if (scanner.isUnterminated()) {
                              result.endOfLineState = 1 /* InMultiLineCommentTrivia */;
                          }
                      }
                      else if (ts.isTemplateLiteralKind(token)) {
                          if (scanner.isUnterminated()) {
                              if (token === 14 /* TemplateTail */) {
                                  result.endOfLineState = 5 /* InTemplateMiddleOrTail */;
                              }
                              else if (token === 11 /* NoSubstitutionTemplateLiteral */) {
                                  result.endOfLineState = 4 /* InTemplateHeadOrNoSubstitutionTemplate */;
                              }
                              else {
                                  ts.Debug.fail("Only 'NoSubstitutionTemplateLiteral's and 'TemplateTail's can be unterminated; got SyntaxKind #" + token);
                              }
                          }
                      }
                      else if (templateStack.length > 0 && ts.lastOrUndefined(templateStack) === 12 /* TemplateHead */) {
                          result.endOfLineState = 6 /* InTemplateSubstitutionPosition */;
                      }
                  }
              }
              function addResult(start, end, classification) {
                  if (classification === 8 /* whiteSpace */) {
                      // Don't bother with whitespace classifications.  They're not needed.
                      return;
                  }
                  if (start === 0 && offset > 0) {
                      // We're classifying the first token, and this was a case where we prepended
                      // text.  We should consider the start of this token to be at the start of
                      // the original text.
                      start += offset;
                  }
                  // All our tokens are in relation to the augmented text.  Move them back to be
                  // relative to the original text.
                  start -= offset;
                  end -= offset;
                  var length = end - start;
                  if (length > 0) {
                      result.spans.push(start);
                      result.spans.push(length);
                      result.spans.push(classification);
                  }
              }
          }