function getCurrentMethodAndTokenPaths()

in public/src/autocomplete.js [602:816]


  function getCurrentMethodAndTokenPaths(pos) {
    var tokenIter = editor.iterForPosition(pos.row, pos.column);
    var startPos = pos;
    var bodyTokenPath = [], last_var = "", first_scope = true, ret = {};

    var 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
    };
    var state = STATES.start;

    // initialization problems -
    var t = tokenIter.getCurrentToken();
    if (t) {
      if (startPos.column == 0) {
        // 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 == 0) {
        // empty lines do no have tokens, move one back
        t = tokenIter.stepBackward();
        state = STATES.start;
      }
    }

    var 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..
          var 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 "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)) {
      // we had some content and still no path -> the cursor is position after a closed body -> no auto complete
      return {};
    }

    if (tokenIter.getCurrentTokenRow() == startPos.row) {
      if (t.type === "url.part" || t.type === "url.param" || t.type === "url.value") {
        // we are on the same line as cursor and dealing with a url. Current token is not part of the context
        t = tokenIter.stepBackward();
      }
      bodyTokenPath = null; // no not on a body line.
    }

    ret.bodyTokenPath = bodyTokenPath;
    ret.urlTokenPath = [];
    ret.urlParamsTokenPath = null;
    ret.requestStartRow = tokenIter.getCurrentTokenRow();
    var curUrlPart;

    while (t && isUrlParamsToken(t)) {
      switch (t.type) {
        case "url.value":
          if (_.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 (!_.isArray(curUrlPart)) {
            curUrlPart = [curUrlPart];
          }
          break;
        case "url.param":
          var 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 (_.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 (!_.isArray(curUrlPart)) {
            curUrlPart = [curUrlPart];
          }
          break;
        case "url.slash":
          ret.urlTokenPath.unshift(curUrlPart);
          curUrlPart = null;
          break;
      }
      t = editor.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.splice(-1)[0];
      }
    }
    else {
      // mark the url as completed.
      ret.urlTokenPath.push(url_pattern_matcher.URL_PATH_END_MARKER);
    }

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