private void parseKeywordAndArgs() {

    if (!keyword_parsed) {
      keyword_parsed = true;
      ParsedString keywordOrSetting = line.arguments.get(argOff);
      keywordSequence_isSetting = keywordOrSetting.getValue().startsWith("[");
      if (keywordSequence_isSetting) {
        keywordSequence_keywordCallState = KeywordCallState.UNDETERMINED_NOT_FOR_NOINDENT; // TODO
        // possibly
        // should
        // be
        // KEYWORD_NOT_FOR_NOINDENT
        keywordSequence_settingType = keywordSequenceSettingTypes.get(keywordOrSetting.getValue());
        if (keywordSequence_settingType == null) {
          keywordSequence_settingType = SettingType.UNKNOWN;
        }
        keywordOrSetting.setType(ArgumentType.SETTING_KEY);
        prepareNextToken();
      } else {
        keywordSequence_keywordCallState = KeywordCallState.UNDETERMINED;
        parseKeywordCall(lastRealType.isTestCaseLine());
      }
    } else {
      if (keywordSequence_isSetting) {
        parseKeywordSequenceSetting();
      } else {
        parseKeywordCall(lastRealType.isTestCaseLine());
      }
    }
  }
 private void parseTableBeginToken() {
   assert argOff == 0;
   ParsedString table = line.arguments.get(0);
   table.setType(ArgumentType.TABLE);
   prepareNextLine();
   return;
 }
 void prepareNextLine() {
   assert argOff >= 0;
   assert argOff <= argLen;
   // if previous line ended with comment, add it to queue now
   if (lineEndsWithComment) {
     ParsedString comment = line.arguments.get(argLen);
     if (comment.getValue().startsWith("#")) {
       comment.setType(ArgumentType.COMMENT);
     }
   }
   // next line
   line = getNextNonemptyLine();
   if (line != null) {
     argLen = line.arguments.size();
     lineEndsWithComment = line.arguments.get(argLen - 1).getValue().startsWith("#");
     if (lineEndsWithComment) {
       --argLen; // exclude now, deal with it later (see top of method)
     }
   } else {
     // for (RobotLine line : lines) {
     // System.out.println(line);
     // }
     lines = null;
     lineIterator = null;
     argLen = 0;
     lineEndsWithComment = false;
   }
   argOff = 0;
 }
 private void setUpKeywordArgPos(ParsedString keyword) {
   keywordSequence_keywordArgPos = KEYWORD_ARG_POS_NONE;
   if (keywordsTakingKeywords.containsKey(keyword.getValue())) {
     int keywordArgumentPosition = keywordsTakingKeywords.get(keyword.getValue());
     keywordSequence_keywordArgPos = keywordArgumentPosition;
     if (keywordArgumentPosition != KEYWORD_ARG_POS_ALL) {
       keywordSequence_currentArgPos = 1;
     }
   }
 }
 private void parseVariableTableLineToken() {
   switch (argOff) {
     case 0:
       ParsedString variable = line.arguments.get(0);
       variable.setType(ArgumentType.VARIABLE_KEY);
       prepareNextToken();
       return;
     default:
       parseVariableArgs();
       return;
   }
 }
 private void parseTestCaseOrKeywordTableBegin(LineType type) {
   if (argOff == 0) {
     keyword_parsed = false;
     lookForLocalTestTemplate();
     ParsedString newName = line.arguments.get(0);
     if (!newName.isEmpty()) {
       boolean isTestCase = type == LineType.TESTCASE_TABLE_TESTCASE_BEGIN;
       newName.setType(isTestCase ? ArgumentType.NEW_TESTCASE : ArgumentType.NEW_KEYWORD);
     }
     prepareNextToken();
     return;
   }
   parseTestCaseOrKeywordLineToken();
   return;
 }
 @Override
 protected void addProposal(ParsedString proposal, FileWithType proposalLocation) {
   String proposalString = proposal.getValue().toLowerCase();
   boolean proposalExisted = addedProposals.contains(proposalString);
   super.addProposal(proposal, proposalLocation);
   if (proposalExisted) {
     for (RobotCompletionProposal robotCompletionProposal : proposals) {
       setPrefixRequiredIfNeeded(proposalString, robotCompletionProposal);
     }
   }
 }
  /**
   * Splits a line from a robot TXT file into arguments. Only supports the
   * tab-or-multiple-whitespace separator right now.
   *
   * @param line
   * @param charPos the file character position of the first character of the line
   * @return
   */
  static List<ParsedString> splitLineIntoArguments(String line, int charPos) {
    String origLine = line;
    // remove trailing empty cells and whitespace
    line = rtrim(line);
    if (line == null) {
      return Collections.emptyList();
    }

    // split line by tab-or-multiwhitespace
    Matcher m = SEPARATOR_RE.matcher(line);
    List<ParsedString> arguments = new ArrayList<ParsedString>();
    int lastEnd = 0;
    while (true) {
      // if next cell starts with #, the rest of the line is a comment
      boolean isComment = lastEnd < line.length() && line.charAt(lastEnd) == '#';
      boolean isLastArgument = isComment || !m.find();
      int nextStart = !isLastArgument ? m.start() : line.length();
      if (lastEnd == 0 && nextStart > 0 && line.charAt(0) == ' ') {
        /*
         * spec says all arguments are trimmed - this is the only case when additional trimming is needed.
         */
        ++lastEnd;
      }
      ParsedString parsedString =
          new ParsedString(line.substring(lastEnd, nextStart), charPos + lastEnd);
      int nextLen = (!isLastArgument ? m.end() : origLine.length()) - nextStart;
      if (nextLen >= 1 && origLine.charAt(nextStart) == ' ') {
        parsedString.setHasSpaceAfter(true);
      }
      arguments.add(parsedString);
      if (isLastArgument) {
        // last argument
        break;
      }
      lastEnd = m.end();
    }
    return arguments;
  }
 private void parseSettingTableLineToken() {
   switch (argOff) {
     case 0:
       {
         ParsedString setting = line.arguments.get(0);
         setting.setType(ArgumentType.SETTING_KEY);
         setting_type = settingTypes.get(setting.getValue());
         if (setting_type == null) {
           setting_type = SettingType.UNKNOWN;
         }
         setting_gotFirstArg = false;
         // TODO possibly should be KEYWORD_NOT_FOR_NOINDENT:
         keywordSequence_keywordCallState = KeywordCallState.UNDETERMINED_NOT_FOR_NOINDENT;
         prepareNextToken();
         return;
       }
     default:
       {
         parseSettingArgs();
         return;
       }
   }
 }
 private KeywordCallState scanLine(
     KeywordCallState initialKeywordCallState, RobotLine scanLine, int scanOff) {
   assert initialKeywordCallState.isUndetermined();
   for (; scanOff < scanLine.arguments.size(); ++scanOff) {
     ParsedString parsedString = scanLine.arguments.get(scanOff);
     if (parsedString.isEmpty()) {
       if (initialKeywordCallState == KeywordCallState.UNDETERMINED) {
         // no variables yet
         continue;
       } else {
         // no equal sign found before first non-variable parameter
         return initialKeywordCallState == KeywordCallState.UNDETERMINED_NOT_FOR_NOINDENT
             ? KeywordCallState.KEYWORD_NOT_FOR_NOINDENT
             : KeywordCallState.KEYWORD;
       }
     }
     String arg = parsedString.getValue();
     switch (arg.charAt(0)) {
       case '$':
       case '@':
         // TODO ensure it's a proper lvalue
         initialKeywordCallState = KeywordCallState.UNDETERMINED_GOTVARIABLE;
         break;
       default:
         // non-variable and no prior lvalue indication, so..
         return initialKeywordCallState == KeywordCallState.UNDETERMINED_NOT_FOR_NOINDENT
             ? KeywordCallState.KEYWORD_NOT_FOR_NOINDENT
             : KeywordCallState.KEYWORD;
     }
     if (arg.endsWith("=")) {
       return initialKeywordCallState == KeywordCallState.UNDETERMINED_NOT_FOR_NOINDENT
           ? KeywordCallState.LVALUE_NOINDENT
           : KeywordCallState.LVALUE;
     }
   }
   return initialKeywordCallState;
 }
 @Override
 public VisitorInterest visitMatch(ParsedString proposal, FileWithType proposalLocation) {
   if (userInput == null) {
     addProposal(proposal, proposalLocation);
   } else {
     String userInputString = userInput.getValue().toLowerCase();
     String proposalString = proposal.getValue().toLowerCase();
     if (proposalString.contains(userInputString)
         || matchesWithoutPrefix(userInputString, proposalString, proposalLocation)) {
       addProposal(proposal, proposalLocation);
     }
     // if (KeywordMatchResult.DIFFERENT == match(proposalString, lookFor(userInputString))) {
     // if (!prefixesMatch(userInputString, proposalLocation)) {
     // return VisitorInterest.CONTINUE;
     // }
     // if (KeywordMatchResult.DIFFERENT == match(proposalString,
     // lookFor(valueWithoutPrefix(userInputString))))
     // {
     // return VisitorInterest.CONTINUE;
     // }
     // }
   }
   return VisitorInterest.CONTINUE;
 }
 /**
  * Before this is called the first time, keywordSequence_keywordCallState must be initialized to
  * either UNDETERMINED, UNDETERMINED_NOINDENT, KEYWORD_NOINDENT, KEYWORD_NOT_FOR_NOINDENT
  *
  * @param templatesEnabled whether the template flags {@link #globalTemplateAtLine} and {@link
  *     #localTemplateAtLine} affect keyword calls during this invocation
  */
 private void parseKeywordCall(boolean templatesEnabled) {
   if (keywordSequence_keywordCallState.isUndetermined()) {
     keywordSequence_keywordCallState =
         determineInitialKeywordCallState(keywordSequence_keywordCallState);
   }
   switch (keywordSequence_keywordCallState) {
     case LVALUE_NOINDENT:
     case LVALUE:
       {
         ParsedString variable = line.arguments.get(argOff);
         if (!variable.isEmpty()
             || keywordSequence_keywordCallState == KeywordCallState.LVALUE_NOINDENT) {
           variable.setType(ArgumentType.KEYWORD_LVALUE);
           if (variable.getValue().endsWith("=")) {
             keywordSequence_keywordCallState = KeywordCallState.KEYWORD_NOT_FOR_NOINDENT;
           }
         }
         prepareNextToken();
         return;
       }
     case KEYWORD_NOT_FOR_NOINDENT:
     case KEYWORD:
       {
         ParsedString keyword = line.arguments.get(argOff);
         if (!keyword.isEmpty()
             || keywordSequence_keywordCallState == KeywordCallState.KEYWORD_NOT_FOR_NOINDENT) {
           if (keyword.getValue().equals(":FOR")
               && keywordSequence_keywordCallState != KeywordCallState.KEYWORD_NOT_FOR_NOINDENT) {
             keyword.setType(ArgumentType.FOR_PART);
             keywordSequence_keywordCallState = KeywordCallState.FOR_VARS;
           } else {
             if (templatesEnabled && isTemplateActive()) {
               keyword.setType(ArgumentType.KEYWORD_ARG);
               // TODO support "Run Keyword If" etc as template keyword
               keywordSequence_keywordArgPos = KEYWORD_ARG_POS_NONE;
             } else if (NONE_STR.equals(keyword.getValue())) {
               // TODO verify what is this branch about
               keyword.setType(ArgumentType.SETTING_VAL);
               keywordSequence_keywordArgPos = KEYWORD_ARG_POS_NONE;
             } else {
               keyword.setType(ArgumentType.KEYWORD_CALL);
               setUpKeywordArgPos(keyword);
             }
             keywordSequence_keywordCallState = KeywordCallState.KEYWORD_ARGS;
           }
         }
         prepareNextToken();
         return;
       }
     case FOR_VARS:
       {
         ParsedString arg = line.arguments.get(argOff);
         String argVal = arg.getValue();
         if (argVal.equals("IN") || argVal.equals("IN RANGE")) {
           arg.setType(ArgumentType.FOR_PART);
           keywordSequence_keywordCallState = KeywordCallState.FOR_ARGS;
           prepareNextToken();
           return;
         }
         arg.setType(ArgumentType.KEYWORD_LVALUE);
         prepareNextToken();
         return;
       }
     case FOR_ARGS:
       {
         setArgTypesToEol(ArgumentType.KEYWORD_ARG);
         prepareNextLine();
         return;
       }
     case KEYWORD_ARGS:
       {
         ParsedString arg = line.arguments.get(argOff);
         boolean isKeyword;
         switch (keywordSequence_keywordArgPos) {
           case KEYWORD_ARG_POS_NONE:
             isKeyword = false;
             break;
           case KEYWORD_ARG_POS_ALL:
             isKeyword = true;
             break;
           default:
             isKeyword = keywordSequence_currentArgPos++ == keywordSequence_keywordArgPos;
             if (isKeyword) {
               setUpKeywordArgPos(arg);
             }
             break;
         }
         if (isKeyword) {
           arg.setType(ArgumentType.KEYWORD_CALL_DYNAMIC);
         } else {
           arg.setType(ArgumentType.KEYWORD_ARG);
         }
         prepareNextToken();
         return;
       }
     default:
       throw new RuntimeException();
   }
 }
 private void parseSettingArgs() {
   switch (setting_type) {
     case UNKNOWN:
       {
         prepareNextLine();
         return;
       }
     case STRING:
       {
         setArgTypesToEol(ArgumentType.SETTING_VAL);
         prepareNextLine();
         return;
       }
     case FILE:
       {
         ParsedString file = line.arguments.get(argOff);
         file.setType(ArgumentType.SETTING_FILE);
         prepareNextLine();
         return;
       }
     case FILE_ARGS:
       {
         if (!setting_gotFirstArg) {
           ParsedString file = line.arguments.get(argOff);
           file.setType(ArgumentType.SETTING_FILE);
           prepareNextToken();
           setting_gotFirstArg = true;
           setting_withNameState = WithNameState.NONE;
           return;
         } else {
           switch (setting_withNameState) {
             case NONE:
               ParsedString arg = line.arguments.get(argOff);
               if (arg.getValue().equals("WITH NAME")) {
                 setting_withNameState = WithNameState.GOT_KEY;
                 arg.setType(ArgumentType.SETTING_FILE_WITH_NAME_KEY);
               } else {
                 arg.setType(ArgumentType.SETTING_FILE_ARG);
               }
               prepareNextToken();
               return;
             case GOT_KEY:
               ParsedString name = line.arguments.get(argOff);
               name.setType(ArgumentType.SETTING_FILE_WITH_NAME_VALUE);
               setting_withNameState = WithNameState.GOT_VALUE;
               prepareNextLine();
               return;
             case GOT_VALUE:
               prepareNextLine();
               return;
           }
         }
         throw new RuntimeException();
       }
     case KEYWORD_ARGS:
       {
         parseKeywordCall(false);
         return;
       }
   }
   throw new RuntimeException();
 }
 static int determineContinuationLineArgOff(RobotLine theLine) {
   List<ParsedString> arguments = theLine.arguments;
   ParsedString parsedString = arguments.get(0);
   String value = parsedString.getValue();
   return value.equals(PreParser.CONTINUATION_STR) ? 1 : 2;
 }
 @Override
 public String toString() {
   return parsedString.getValue();
 }