export function getCurrentMethodAndTokenPaths()

in src/plugins/console/public/lib/autocomplete/autocomplete.ts [78:321]


export function getCurrentMethodAndTokenPaths(
  editor: CoreEditor,
  pos: Position,
  parser: any,
  forceEndOfUrl?: boolean /* Flag for indicating whether we want to avoid early escape optimization. */
) {
  const tokenIter = createTokenIterator({
    editor,
    position: pos,
  });
  const startPos = pos;
  let bodyTokenPath: any = [];
  const ret: any = {};

  const STATES = {
    looking_for_key: 0, // looking for a key but without jumping over anything but white space and colon.
    looking_for_scope_start: 1, // skip everything until scope start
    start: 3,
  };
  let state = STATES.start;

  // initialization problems -
  let t = tokenIter.getCurrentToken();
  if (t) {
    if (startPos.column === 1) {
      // if we are at the beginning of the line, the current token is the one after cursor, not before which
      // deviates from the standard.
      t = tokenIter.stepBackward();
      state = STATES.looking_for_scope_start;
    }
  } else {
    if (startPos.column === 1) {
      // empty lines do no have tokens, move one back
      t = tokenIter.stepBackward();
      state = STATES.start;
    }
  }

  let walkedSomeBody = false;

  // climb one scope at a time and get the scope key
  for (; t && t.type.indexOf('url') === -1 && t.type !== 'method'; t = tokenIter.stepBackward()) {
    if (t.type !== 'whitespace') {
      walkedSomeBody = true;
    } // marks we saw something

    switch (t.type) {
      case 'variable':
        if (state === STATES.looking_for_key) {
          bodyTokenPath.unshift(t.value.trim().replace(/"/g, ''));
        }
        state = STATES.looking_for_scope_start; // skip everything until the beginning of this scope
        break;

      case 'paren.lparen':
        bodyTokenPath.unshift(t.value);
        if (state === STATES.looking_for_scope_start) {
          // found it. go look for the relevant key
          state = STATES.looking_for_key;
        }
        break;
      case 'paren.rparen':
        // reset he search for key
        state = STATES.looking_for_scope_start;
        // and ignore this sub scope..
        let parenCount = 1;
        t = tokenIter.stepBackward();
        while (t && parenCount > 0) {
          switch (t.type) {
            case 'paren.lparen':
              parenCount--;
              break;
            case 'paren.rparen':
              parenCount++;
              break;
          }
          if (parenCount > 0) {
            t = tokenIter.stepBackward();
          }
        }
        if (!t) {
          // oops we run out.. we don't know what's up return null;
          return {};
        }
        continue;
      case 'punctuation.end_triple_quote':
        // reset the search for key
        state = STATES.looking_for_scope_start;
        for (t = tokenIter.stepBackward(); t; t = tokenIter.stepBackward()) {
          if (t.type === 'punctuation.start_triple_quote') {
            t = tokenIter.stepBackward();
            break;
          }
        }
        if (!t) {
          // oops we run out.. we don't know what's up return null;
          return {};
        }
        continue;
      case 'punctuation.start_triple_quote':
        if (state === STATES.start) {
          state = STATES.looking_for_key;
        } else if (state === STATES.looking_for_key) {
          state = STATES.looking_for_scope_start;
        }
        bodyTokenPath.unshift('"""');
        continue;
      case 'string':
      case 'constant.numeric':
      case 'constant.language.boolean':
      case 'text':
        if (state === STATES.start) {
          state = STATES.looking_for_key;
        } else if (state === STATES.looking_for_key) {
          state = STATES.looking_for_scope_start;
        }

        break;
      case 'punctuation.comma':
        if (state === STATES.start) {
          state = STATES.looking_for_scope_start;
        }
        break;
      case 'punctuation.colon':
      case 'whitespace':
        if (state === STATES.start) {
          state = STATES.looking_for_key;
        }
        break; // skip white space
    }
  }

  if (walkedSomeBody && (!bodyTokenPath || bodyTokenPath.length === 0) && !forceEndOfUrl) {
    // we had some content and still no path -> the cursor is position after a closed body -> no auto complete
    return {};
  }

  ret.urlTokenPath = [];
  if (tokenIter.getCurrentPosition().lineNumber === startPos.lineNumber) {
    if (t && (t.type === 'url.part' || t.type === 'url.param' || t.type === 'url.value')) {
      // we are forcing the end of the url for the purposes of determining an endpoint
      if (forceEndOfUrl && t.type === 'url.part') {
        ret.urlTokenPath.push(t.value);
        ret.urlTokenPath.push(URL_PATH_END_MARKER);
      }
      // we are on the same line as cursor and dealing with a url. Current token is not part of the context
      t = tokenIter.stepBackward();
      // This will force method parsing
      while (t!.type === 'whitespace') {
        t = tokenIter.stepBackward();
      }
    }
    bodyTokenPath = null; // no not on a body line.
  }

  ret.bodyTokenPath = bodyTokenPath;

  ret.urlParamsTokenPath = null;
  ret.requestStartRow = tokenIter.getCurrentPosition().lineNumber;
  let curUrlPart: any;

  while (t && isUrlParamsToken(t)) {
    switch (t.type) {
      case 'url.value':
        if (Array.isArray(curUrlPart)) {
          curUrlPart.unshift(t.value);
        } else if (curUrlPart) {
          curUrlPart = [t.value, curUrlPart];
        } else {
          curUrlPart = t.value;
        }
        break;
      case 'url.comma':
        if (!curUrlPart) {
          curUrlPart = [];
        } else if (!Array.isArray(curUrlPart)) {
          curUrlPart = [curUrlPart];
        }
        break;
      case 'url.param':
        const v = curUrlPart;
        curUrlPart = {};
        curUrlPart[t.value] = v;
        break;
      case 'url.amp':
      case 'url.questionmark':
        if (!ret.urlParamsTokenPath) {
          ret.urlParamsTokenPath = [];
        }
        ret.urlParamsTokenPath.unshift(curUrlPart || {});
        curUrlPart = null;
        break;
    }
    t = tokenIter.stepBackward();
  }

  curUrlPart = null;
  while (t && t.type.indexOf('url') !== -1) {
    switch (t.type) {
      case 'url.part':
        if (Array.isArray(curUrlPart)) {
          curUrlPart.unshift(t.value);
        } else if (curUrlPart) {
          curUrlPart = [t.value, curUrlPart];
        } else {
          curUrlPart = t.value;
        }
        break;
      case 'url.comma':
        if (!curUrlPart) {
          curUrlPart = [];
        } else if (!Array.isArray(curUrlPart)) {
          curUrlPart = [curUrlPart];
        }
        break;
      case 'url.slash':
        if (curUrlPart) {
          ret.urlTokenPath.unshift(curUrlPart);
          curUrlPart = null;
        }
        break;
    }
    t = parser.prevNonEmptyToken(tokenIter);
  }

  if (curUrlPart) {
    ret.urlTokenPath.unshift(curUrlPart);
  }

  if (!ret.bodyTokenPath && !ret.urlParamsTokenPath) {
    if (ret.urlTokenPath.length > 0) {
      //   // started on the url, first token is current token
      ret.otherTokenValues = ret.urlTokenPath[0];
    }
  } else {
    // mark the url as completed.
    ret.urlTokenPath.push(URL_PATH_END_MARKER);
  }

  if (t && t.type === 'method') {
    ret.method = t.value;
  }
  return ret;
}