diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ddd642f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+# use glob syntax.
+syntax: glob
+*.ser
+*.class
+*~
+*.bak
+*.off
+*.old
+.DS_Store
+
+# lib'n misc
+lib/
+file/
+
+# eclipse conf file
+.settings
+.classpath
+.project
+
+
+# building
+target/
+bin/
+test-output/
+out/
+
+# Exported plugin for deployment
+IntelliJBehave.zip
+
+# other scm
+.svn
+.CVS
+.hg*
+
+# switch to regexp syntax.
+# syntax: regexp
+# ^\.pc/
+
+# IntelliJ
+*.iml
+*.ipr
+*.iws
+.idea
diff --git a/IntelliJBehave.iml b/IntelliJBehave.iml
index f72ee21..634f32d 100644
--- a/IntelliJBehave.iml
+++ b/IntelliJBehave.iml
@@ -8,18 +8,33 @@
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml
index 90070a4..1c08b47 100644
--- a/META-INF/plugin.xml
+++ b/META-INF/plugin.xml
@@ -31,7 +31,7 @@
Run *.story files
]]>
- 1.2
+ 1.3.SNAPSHOT
Aman Kumar
@@ -70,6 +70,8 @@
+
+
diff --git a/lib/jbehave-core-3.5.4.jar b/lib/jbehave-core-3.5.4.jar
deleted file mode 100644
index e9bb049..0000000
Binary files a/lib/jbehave-core-3.5.4.jar and /dev/null differ
diff --git a/lib/jbehave-core-3.6.jar b/lib/jbehave-core-3.6.jar
new file mode 100644
index 0000000..ad796c4
Binary files /dev/null and b/lib/jbehave-core-3.6.jar differ
diff --git a/notes.md b/notes.md
new file mode 100644
index 0000000..5a555c8
--- /dev/null
+++ b/notes.md
@@ -0,0 +1,144 @@
+http://confluence.jetbrains.net/display/IDEADEV/Developing+Custom+Language+Plugins+for+IntelliJ+IDEA#DevelopingCustomLanguagePluginsforIntelliJIDEA-ImplementingaParserandPSI
+
+Blue Forest theme:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepsInspection.java b/src/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepsInspection.java
index 3ab5316..22d65cf 100644
--- a/src/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepsInspection.java
+++ b/src/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepsInspection.java
@@ -15,65 +15,114 @@
*/
package com.github.kumaraman21.intellijbehave.codeInspector;
+import com.github.kumaraman21.intellijbehave.highlighter.StorySyntaxHighlighter;
import com.github.kumaraman21.intellijbehave.parser.StepPsiElement;
+import com.github.kumaraman21.intellijbehave.resolver.StepDefinitionAnnotation;
+import com.github.kumaraman21.intellijbehave.utility.ParametrizedString;
import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.codeInspection.ex.ProblemDescriptorImpl;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
+
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public class UndefinedStepsInspection extends BaseJavaLocalInspectionTool {
- @Nls
- @NotNull
- @Override
- public String getGroupDisplayName() {
- return "JBehave";
- }
-
- @Nls
- @NotNull
- @Override
- public String getDisplayName() {
- return "Undefined step";
- }
-
- @NotNull
- @Override
- public String getShortName() {
- return "UndefinedStep";
- }
-
- @Override
- public String getStaticDescription() {
- return super.getStaticDescription();
- }
-
- @Override
- public boolean isEnabledByDefault() {
- return true;
- }
-
- @NotNull
- @Override
- public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
- return new PsiElementVisitor() {
-
- @Override
- public void visitElement(PsiElement psiElement) {
- super.visitElement(psiElement);
-
- if(! (psiElement instanceof StepPsiElement)) {
- return;
- }
+ @Nls
+ @NotNull
+ @Override
+ public String getGroupDisplayName() {
+ return "JBehave";
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "Undefined step";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "UndefinedStep";
+ }
+
+ @Override
+ public String getStaticDescription() {
+ return super.getStaticDescription();
+ }
+
+ @Override
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
+ return new PsiElementVisitor() {
- StepPsiElement stepPsiElement = (StepPsiElement) psiElement;
- if(stepPsiElement.getReference().resolve() == null) {
- holder.registerProblem(stepPsiElement, "Step #ref
is not defined");
+ @Override
+ public void visitElement(PsiElement psiElement) {
+ super.visitElement(psiElement);
+
+ if (!(psiElement instanceof StepPsiElement)) {
+ return;
+ }
+
+ StepPsiElement stepPsiElement = (StepPsiElement) psiElement;
+ StepDefinitionAnnotation annotationDef = stepPsiElement.getReference().stepDefinitionAnnotation();
+ if (annotationDef == null) {
+ holder.registerProblem(stepPsiElement, "Step #ref
is not defined");
+ }
+ else {
+ highlightParameters(stepPsiElement, annotationDef, holder);
+ }
+ }
+ };
+ }
+
+
+ private void highlightParameters(StepPsiElement stepPsiElement,
+ StepDefinitionAnnotation annotation,
+ ProblemsHolder holder)
+ {
+ String stepText = stepPsiElement.getStepText();
+ String annotationText = annotation.getAnnotationText();
+ ParametrizedString pString = new ParametrizedString(annotationText);
+
+ int offset = stepPsiElement.getStepTextOffset();
+ for (ParametrizedString.StringToken token : pString.tokenize(stepText)) {
+ int length = token.getValue().length();
+ if (token.isIdentifier()) {
+ registerHiglighting(StorySyntaxHighlighter.TABLE_CELL,
+ stepPsiElement,
+ TextRange.from(offset, length),
+ holder);
+ }
+ offset += length;
}
- }
- };
- }
+ }
+
+ private static void registerHiglighting(TextAttributesKey attributesKey,
+ StepPsiElement step,
+ TextRange range,
+ ProblemsHolder holder)
+ {
+ final ProblemDescriptor descriptor = new ProblemDescriptorImpl(
+ step, step, "", LocalQuickFix.EMPTY_ARRAY,
+ ProblemHighlightType.INFORMATION, false, range, false, null,
+ holder.isOnTheFly());
+ descriptor.setTextAttributes(attributesKey);
+ holder.registerProblem(descriptor);
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java b/src/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java
new file mode 100644
index 0000000..059bc54
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java
@@ -0,0 +1,194 @@
+package com.github.kumaraman21.intellijbehave.completion;
+
+import static com.github.kumaraman21.intellijbehave.utility.ProjectUtils.getProjectFileIndex;
+
+import com.github.kumaraman21.intellijbehave.highlighter.StoryTokenType;
+import com.github.kumaraman21.intellijbehave.parser.StepPsiElement;
+import com.github.kumaraman21.intellijbehave.resolver.StepDefinitionAnnotation;
+import com.github.kumaraman21.intellijbehave.resolver.StepDefinitionIterator;
+import com.github.kumaraman21.intellijbehave.resolver.StepPsiReference;
+import com.github.kumaraman21.intellijbehave.utility.LocalizedStorySupport;
+import com.github.kumaraman21.intellijbehave.utility.ParametrizedString;
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionParameters;
+import com.intellij.codeInsight.completion.CompletionResultSet;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.codeInsight.completion.CompletionUtil;
+import com.intellij.codeInsight.completion.PrefixMatcher;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.Consumer;
+
+import org.jbehave.core.i18n.LocalizedKeywords;
+import org.jbehave.core.steps.StepType;
+
+/**
+ * @author @aloyer
+ */
+public class StoryCompletionContributor extends CompletionContributor {
+ public StoryCompletionContributor() {
+ }
+
+ @Override
+ public void fillCompletionVariants(CompletionParameters parameters, final CompletionResultSet _result) {
+ if (parameters.getCompletionType() == CompletionType.BASIC) {
+ String prefix = CompletionUtil.findReferenceOrAlphanumericPrefix(parameters);
+ CompletionResultSet result = _result.withPrefixMatcher(prefix);
+
+ LocalizedKeywords keywords = lookupLocalizedKeywords(parameters);
+ Consumer consumer = newConsumer(_result);
+
+ addAllKeywords(result.getPrefixMatcher(), consumer, keywords);
+ addAllSteps(parameters,
+ parameters.getInvocationCount() <= 1,
+ result.getPrefixMatcher(),
+ consumer,
+ keywords);
+ }
+ }
+
+ private LocalizedKeywords lookupLocalizedKeywords(CompletionParameters parameters) {
+ String locale = "en";
+ ASTNode localeNode = parameters.getOriginalFile().getNode().findChildByType(StoryTokenType.COMMENT_WITH_LOCALE);
+ if (localeNode != null) {
+ String localeFound = LocalizedStorySupport.checkForLanguageDefinition(localeNode.getText());
+ if (localeFound != null) {
+ locale = localeFound;
+ }
+ }
+ return new LocalizedStorySupport().getKeywords(locale);
+ }
+
+ private static Consumer newConsumer(final CompletionResultSet result) {
+ return new Consumer() {
+ @Override
+ public void consume(LookupElement element) {
+ result.addElement(element);
+ }
+ };
+ }
+
+ private static void addAllKeywords(PrefixMatcher prefixMatcher,
+ Consumer consumer,
+ LocalizedKeywords keywords)
+ {
+ addIfMatches(consumer, prefixMatcher, keywords.narrative());
+ addIfMatches(consumer, prefixMatcher, keywords.asA());
+ addIfMatches(consumer, prefixMatcher, keywords.inOrderTo());
+ addIfMatches(consumer, prefixMatcher, keywords.iWantTo());
+ //
+ addIfMatches(consumer, prefixMatcher, keywords.givenStories());
+ addIfMatches(consumer, prefixMatcher, keywords.ignorable());
+ addIfMatches(consumer, prefixMatcher, keywords.scenario());
+ addIfMatches(consumer, prefixMatcher, keywords.examplesTable());
+ //
+ addIfMatches(consumer, prefixMatcher, keywords.given());
+ addIfMatches(consumer, prefixMatcher, keywords.when());
+ addIfMatches(consumer, prefixMatcher, keywords.then());
+ addIfMatches(consumer, prefixMatcher, keywords.and());
+ }
+
+ private static void addIfMatches(Consumer consumer, PrefixMatcher prefixMatchers, String input) {
+ if (prefixMatchers.prefixMatches(input)) {
+ consumer.consume(LookupElementBuilder.create(input));
+ }
+ }
+
+ private static void addAllSteps(CompletionParameters parameters,
+ boolean filterByScope,
+ PrefixMatcher prefixMatcher,
+ Consumer consumer, LocalizedKeywords keywords)
+ {
+ StepPsiElement stepPsiElement = getStepPsiElement(parameters);
+ if (stepPsiElement == null) {
+ return;
+ }
+
+ StepType stepType = stepPsiElement.getStepType();
+ String actualStepPrefix = stepPsiElement.getActualStepPrefix();
+ //
+ String textBeforeCaret = CompletionUtil.findReferenceOrAlphanumericPrefix(parameters);
+
+ // suggest only if at least the actualStepPrefix is complete
+ if (isStepTypeComplete(keywords, textBeforeCaret)) {
+ StepSuggester stepAnnotationFinder = new StepSuggester(prefixMatcher,
+ stepType,
+ actualStepPrefix,
+ textBeforeCaret,
+ consumer);
+ getProjectFileIndex().iterateContent(stepAnnotationFinder);
+ }
+ }
+
+ private static boolean isStepTypeComplete(LocalizedKeywords keywords, String input) {
+ return input.startsWith(keywords.given())
+ || input.startsWith(keywords.when())
+ || input.startsWith(keywords.then())
+ || input.startsWith(keywords.and());
+ }
+
+ private static StepPsiElement getStepPsiElement(CompletionParameters parameters) {
+ PsiElement position = parameters.getPosition();
+ PsiElement positionParent = position.getParent();
+ if (positionParent instanceof StepPsiElement) {
+ return (StepPsiElement) positionParent;
+ }
+ else if (position instanceof StepPsiReference) {
+ return (StepPsiElement) ((StepPsiReference) position).getElement();
+ }
+ else if (position instanceof StepPsiElement) {
+ return (StepPsiElement) position;
+ }
+ else {
+ return null;
+ }
+ }
+
+ private static class StepSuggester extends StepDefinitionIterator {
+
+ private final PrefixMatcher prefixMatcher;
+ private final StepType stepType;
+ private final String actualStepPrefix;
+ private final String textBeforeCaret;
+ private final Consumer consumer;
+
+ private StepSuggester(PrefixMatcher prefixMatcher,
+ StepType stepType,
+ String actualStepPrefix,
+ String textBeforeCaret, Consumer consumer)
+ {
+ super(null);
+ this.prefixMatcher = prefixMatcher;
+ this.stepType = stepType;
+ this.actualStepPrefix = actualStepPrefix;
+ this.textBeforeCaret = textBeforeCaret;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) {
+ StepType annotationStepType = stepDefinitionAnnotation.getStepType();
+ if (annotationStepType != stepType) {
+ return true;
+ }
+ String annotationText = stepDefinitionAnnotation.getAnnotationText();
+ String adjustedAnnotationText = actualStepPrefix + " " + annotationText;
+
+ ParametrizedString pString = new ParametrizedString(adjustedAnnotationText);
+ String complete = pString.complete(textBeforeCaret);
+ if (StringUtil.isNotEmpty(complete)) {
+ PsiAnnotation matchingAnnotation = stepDefinitionAnnotation.getAnnotation();
+ consumer.consume(LookupElementBuilder.create(matchingAnnotation, textBeforeCaret + complete));
+ }
+ else if (prefixMatcher.prefixMatches(adjustedAnnotationText)) {
+ PsiAnnotation matchingAnnotation = stepDefinitionAnnotation.getAnnotation();
+ consumer.consume(LookupElementBuilder.create(matchingAnnotation, adjustedAnnotationText));
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/LexicalState.java b/src/com/github/kumaraman21/intellijbehave/highlighter/LexicalState.java
new file mode 100644
index 0000000..e0840c5
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/LexicalState.java
@@ -0,0 +1,28 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+/**
+ * @author @aloyer
+ */
+public enum LexicalState {
+ YYINITIAL(_StoryLexer.YYINITIAL)
+ ,IN_DIRECTIVE(_StoryLexer.IN_DIRECTIVE)
+ ,IN_STORY(_StoryLexer.IN_STORY)
+ ,IN_SCENARIO(_StoryLexer.IN_SCENARIO)
+ ,IN_STEP(_StoryLexer.IN_STEP)
+ ,IN_META(_StoryLexer.IN_META)
+ ,IN_TABLE(_StoryLexer.IN_TABLE)
+ ,IN_EXAMPLES(_StoryLexer.IN_EXAMPLES)
+ ;
+
+ private final int lexerId;
+ LexicalState(int lexerId) {
+ this.lexerId = lexerId;
+ }
+
+ public static LexicalState fromLexer(int lexerId) {
+ for(LexicalState state : values())
+ if(state.lexerId==lexerId)
+ return state;
+ throw new IllegalArgumentException("Unsupported lexer id: " + lexerId);
+ }
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/Story.flex b/src/com/github/kumaraman21/intellijbehave/highlighter/Story.flex
index 91b82b0..41caddb 100644
--- a/src/com/github/kumaraman21/intellijbehave/highlighter/Story.flex
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/Story.flex
@@ -2,42 +2,174 @@ package com.github.kumaraman21.intellijbehave.highlighter;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
+import java.util.Stack;
%%
+%{
+ private Stack yystates = new Stack () {{ push(YYINITIAL); }};
+ public boolean trace = false;
+
+ public void yystatePush(int yystate) {
+ if(trace) System.out.println(">>>> PUSH: " + LexicalState.fromLexer(yystate) + " [" + reverseAndMap(yystates) + "]");
+ yybegin(yystate);
+ yystates.push(yystate);
+ }
+
+ private String reverseAndMap(Stack yystates) {
+ StringBuilder builder = new StringBuilder();
+ for(int i=yystates.size()-1; i>=0; i--) {
+ if(builder.length()>0)
+ builder.append(", ");
+ builder.append(LexicalState.fromLexer(yystates.get(i)));
+ }
+ return builder.toString();
+ }
+
+ public void yystatePopNPush(int yystate) {
+ yystatePopNPush(1, yystate);
+ }
+
+ public void yystatePopNPush(int nb, int yystate) {
+ if(trace) System.out.println(">>>> POP'n PUSH : #" + nb + ", " + LexicalState.fromLexer(yystate) + " [" + reverseAndMap(yystates) + "]");
+ for (int i = 0; i < nb; i++) {
+ yystatePop();
+ }
+ yystatePush(yystate);
+ }
+
+ public int yystatePop() {
+ int popped = yystates.pop();
+ if(trace) System.out.println(">>>> POP : " + LexicalState.fromLexer(popped) + " [" + reverseAndMap(yystates) + "]");
+ if(!yystates.isEmpty()) {
+ yybegin(yystates.peek());
+ }// otherwise hopes a push will follow right after
+ return popped;
+ }
+
+ public boolean checkAhead(char c) {
+ if (zzMarkedPos >= zzBuffer.length())
+ return false;
+ return zzBuffer.charAt(zzMarkedPos) == c;
+ }
+%}
+
%class _StoryLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType
-%eof{ return;
-%eof}
-CRLF= \n | \r | \r\n
-WHITE_SPACE_CHAR=[\ \n\r\t\f]
-TEXT_CHAR=[^\n\r]
-COMMENT=("!--")[^\r\n]*
+CRLF = \r|\n|\r\n
+BlankChar = [ \t\f]
+InputChar = [^\r\n]
+WhiteSpace = {CRLF} | {BlankChar}
+NonWhiteSpace = [^ \n\r\t\f]
+TableCellChar = [^\r\n\|]
+NonMetaKey = [^@\r\n]
+%state IN_DIRECTIVE
+%state IN_STORY
%state IN_SCENARIO
%state IN_STEP
+%state IN_META
+%state IN_TABLE
+%state IN_EXAMPLES
+%eof{
+ return;
+%eof}
%%
- {CRLF}+"Scenario:" {TEXT_CHAR}+ { yybegin(IN_SCENARIO); return StoryTokenType.SCENARIO_TEXT; }
- {CRLF}+"Scenario:" {TEXT_CHAR}+ { yybegin(IN_SCENARIO); return StoryTokenType.SCENARIO_TEXT; }
- {CRLF}+"Scenario:" {TEXT_CHAR}+ { yybegin(IN_SCENARIO); return StoryTokenType.SCENARIO_TEXT; }
+ {
+ ( "Scenario: "
+ | "Meta:"
+ | "Examples:"
+ | "Given " | "When " | "Then " | "And "
+ | "!--"
+ | "|" ) {InputChar}+ { yystatePush(IN_DIRECTIVE); yypushback(yytext().length()); }
+ {CRLF} { yystatePush(IN_STORY); yypushback(yytext().length()); }
+ {InputChar}+ { return StoryTokenType.STORY_DESCRIPTION; }
+}
+
+ {
+ "Scenario: " { yystatePopNPush(2, IN_SCENARIO); return StoryTokenType.SCENARIO_TYPE; }
+ "Meta:" { yystatePopNPush(2, IN_META); return StoryTokenType.META; }
+ "Examples:" { yystatePopNPush(2, IN_EXAMPLES); return StoryTokenType.EXAMPLE_TYPE; }
+ ("Given " | "When " | "Then " | "And ") { yystatePopNPush(2, IN_STEP); return StoryTokenType.STEP_TYPE; }
+ "!--" {InputChar}* { yystatePop(); return StoryTokenType.COMMENT; }
+ "|" { yystatePopNPush(1, IN_TABLE); return StoryTokenType.TABLE_DELIM; }
+ {WhiteSpace}+ { return StoryTokenType.WHITE_SPACE; }
+}
+
+ {
+ {CRLF}
+ ( "Scenario: "
+ | "Meta:"
+ | "Examples:"
+ | "Given " | "When " | "Then " | "And "
+ | "!--"
+ | "|" ) { yystatePush(IN_DIRECTIVE); yypushback(yytext().length()); }
+ {InputChar}+ { return StoryTokenType.STORY_DESCRIPTION; }
+ {CRLF} { return StoryTokenType.WHITE_SPACE; }
+}
+
+
+ {
+ {CRLF}
+ ( "Scenario: "
+ | "Meta:"
+ | "Examples:"
+ | "Given " | "When " | "Then " | "And "
+ | "!--"
+ | "|" ) { yystatePush(IN_DIRECTIVE); yypushback(yytext().length()); }
+ {InputChar}+ { return StoryTokenType.SCENARIO_TEXT; }
+ {CRLF} { return StoryTokenType.WHITE_SPACE; }
+}
+
+
+ {
+ "@" {NonWhiteSpace}* { return StoryTokenType.META_KEY; }
+ {CRLF}
+ ( "Scenario: "
+ | "Meta:"
+ | "Examples:"
+ | "Given " | "When " | "Then " | "And "
+ | "!--"
+ | "|" ) { yystatePush(IN_DIRECTIVE); yypushback(yytext().length()); }
+ {NonMetaKey}+ { return StoryTokenType.META_TEXT; }
+ {CRLF} { return StoryTokenType.WHITE_SPACE; }
+}
+
+
+ {
+ {CRLF}
+ ( "Scenario: "
+ | "Meta:"
+ | "Examples:"
+ | "Given " | "When " | "Then " | "And "
+ | "!--"
+ | "|" ) { yystatePush(IN_DIRECTIVE); yypushback(yytext().length()); }
+ {InputChar}+ { return StoryTokenType.STEP_TEXT; }
+ {CRLF} { return StoryTokenType.WHITE_SPACE; }
+}
- {CRLF}+"Given" {WHITE_SPACE_CHAR} { yybegin(IN_STEP); return StoryTokenType.STEP_TYPE; }
- {CRLF}+"When" {WHITE_SPACE_CHAR} { yybegin(IN_STEP); return StoryTokenType.STEP_TYPE; }
- {CRLF}+"Then" {WHITE_SPACE_CHAR} { yybegin(IN_STEP); return StoryTokenType.STEP_TYPE; }
- {CRLF}+"And" {WHITE_SPACE_CHAR} { yybegin(IN_STEP); return StoryTokenType.STEP_TYPE; }
- {CRLF}+{WHITE_SPACE_CHAR}* "|" {TEXT_CHAR}* { yybegin(IN_SCENARIO); return StoryTokenType.TABLE_ROW; }
+ {
+ {CRLF}
+ ( "Scenario: "
+ | "Meta:"
+ | "Examples:"
+ | "Given " | "When " | "Then " | "And "
+ | "!--"
+ | "|" ) { yystatePush(IN_DIRECTIVE); yypushback(yytext().length()); }
+ {WhiteSpace} { return StoryTokenType.WHITE_SPACE; }
+}
- {COMMENT} { return StoryTokenType.COMMENT; }
- .* { return StoryTokenType.STORY_DESCRIPTION; }
- .* { return StoryTokenType.STORY_DESCRIPTION; }
+ {
+ {TableCellChar}+ { return StoryTokenType.TABLE_CELL; }
+ "|" { return StoryTokenType.TABLE_DELIM; }
+ {CRLF} { yystatePop(); yypushback(1); }
+}
- {TEXT_CHAR}* { yybegin(IN_SCENARIO); return StoryTokenType.STEP_TEXT; }
+. { return StoryTokenType.BAD_CHARACTER; }
-{WHITE_SPACE_CHAR}+ { return StoryTokenType.WHITE_SPACE; }
-. { return StoryTokenType.BAD_CHARACTER; }
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryColorsAndFontsPage.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryColorsAndFontsPage.java
new file mode 100644
index 0000000..28642d9
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryColorsAndFontsPage.java
@@ -0,0 +1,93 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.fileTypes.SyntaxHighlighter;
+import com.intellij.openapi.options.colors.AttributesDescriptor;
+import com.intellij.openapi.options.colors.ColorDescriptor;
+import com.intellij.openapi.options.colors.ColorSettingsPage;
+
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
+import java.util.Map;
+
+/**
+ * @author @aloyer
+ */
+public class StoryColorsAndFontsPage implements ColorSettingsPage {
+
+ @NotNull
+ public String getDisplayName() {
+ return "JBehave";
+ }
+
+ @Nullable
+ public Icon getIcon() {
+ return null;//IntelliJBehaveIcons.ICON_16x16;
+ }
+
+ @NotNull
+ public AttributesDescriptor[] getAttributeDescriptors() {
+ return ATTRS;
+ }
+
+ private static final AttributesDescriptor[] ATTRS = new AttributesDescriptor[]{
+ new AttributesDescriptor("Story description", StorySyntaxHighlighter.STORY_DESCRIPTION),//
+ new AttributesDescriptor("Scenario keyword", StorySyntaxHighlighter.SCENARIO_TYPE),//
+ new AttributesDescriptor("Scenario text", StorySyntaxHighlighter.SCENARIO_TEXT),//
+ new AttributesDescriptor("Step keyword", StorySyntaxHighlighter.STEP_TYPE),//
+ new AttributesDescriptor("Step text", StorySyntaxHighlighter.STEP_TEXT), //
+ new AttributesDescriptor("Table delimiter", StorySyntaxHighlighter.TABLE_DELIM),
+ new AttributesDescriptor("Table cell", StorySyntaxHighlighter.TABLE_CELL),//
+ new AttributesDescriptor("Meta keyword", StorySyntaxHighlighter.META_TYPE),//
+ new AttributesDescriptor("Meta key", StorySyntaxHighlighter.META_KEY),//
+ new AttributesDescriptor("Meta text", StorySyntaxHighlighter.META_TEXT), //
+ new AttributesDescriptor("Line comment", StorySyntaxHighlighter.LINE_COMMENT),//
+ new AttributesDescriptor("Bad Character", StorySyntaxHighlighter.BAD_CHARACTER)
+ };
+
+ @NotNull
+ public ColorDescriptor[] getColorDescriptors() {
+ return new ColorDescriptor[0];
+ }
+
+ @NotNull
+ public SyntaxHighlighter getHighlighter() {
+ return new StorySyntaxHighlighter();
+ }
+
+ @NonNls
+ @NotNull
+ public String getDemoText() {
+ return "Narrative: \n" + //
+ "In order to play a game\n" + //
+ "As a player\n" + //
+ "I want to be able to create and manage my account\n" + //
+ "\n" + //
+ "Scenario: An unknown user cannot be logged\n" + //
+ "\n" + //
+ "Meta:\n" + //
+ "@author mccallum\n" + //
+ "@skip\n" + //
+ "\n" + //
+ "Given i am the user with nickname: \"weird\"\n" + //
+ "When i try to login using the password \"soweird\"\n" + //
+ "!-- TODO: Then i get an error message of type \"Wrong Credentials\"\n" + //
+ "\n" + //
+ "\n" + //
+ "Scenario: A known user cannot be logged using a wrong password\n" + //
+ "\n" + //
+ "Given the following existing users:\n" + //
+ "| nickname | password |\n" + //
+ "| Travis | PacMan |\n" + //
+ "Given i am the user with nickname: \"Travis\"\n" + //
+ "When i try to login using the password \"McCallum\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"";
+ }
+
+ @Nullable
+ public Map getAdditionalHighlightingTagToDescriptorMap() {
+ return null;
+ }
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexer.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexer.java
index 9354d6e..740913f 100644
--- a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexer.java
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexer.java
@@ -23,4 +23,8 @@ public class StoryLexer extends FlexAdapter {
public StoryLexer() {
super(new _StoryLexer((Reader)null));
}
+
+ public LexicalState lexerState() {
+ return LexicalState.fromLexer(getFlex().yystate());
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexerFactory.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexerFactory.java
new file mode 100644
index 0000000..4b784d4
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLexerFactory.java
@@ -0,0 +1,17 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+import com.intellij.lexer.Lexer;
+
+/**
+ * @author @aloyer
+ */
+public class StoryLexerFactory {
+ public static final boolean USE_LOCALIZED = true;
+
+ public Lexer createLexer() {
+ if(USE_LOCALIZED)
+ return new StoryLocalizedLexer();
+ else
+ return new StoryLexer();
+ }
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer.java
new file mode 100644
index 0000000..0a9f981
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer.java
@@ -0,0 +1,473 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+import com.github.kumaraman21.intellijbehave.utility.CharTree;
+import com.github.kumaraman21.intellijbehave.utility.JBKeyword;
+import com.github.kumaraman21.intellijbehave.utility.LocalizedStorySupport;
+import com.intellij.lexer.LexerBase;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.util.ArrayUtil;
+
+import org.jbehave.core.i18n.LocalizedKeywords;
+
+/**
+ * @author @aloyer
+ */
+public class StoryLocalizedLexer extends LexerBase {
+
+ /**
+ * lexical states
+ */
+ public enum State {
+ YYINITIAL,
+ IN_DISPATCH,
+ IN_STORY,
+ IN_SCENARIO,
+ IN_STEP,
+ IN_TABLE,
+ IN_STEP_TABLE,
+ IN_META,
+ IN_EXAMPLES, IN_OTHER_TABLE
+ }
+
+ private final LocalizedStorySupport kwSupport;
+ //
+ private LocalizedKeywords keywords;
+ private CharTree charTree;
+ private CharSequence buffer = ArrayUtil.EMPTY_CHAR_SEQUENCE;
+ //private int startOffset;
+ private int endOffset;
+ private State state = State.YYINITIAL;
+ private int position;
+ private IElementType tokenType;
+ private int currentTokenStart;
+
+ public StoryLocalizedLexer() {
+ this(new LocalizedStorySupport());
+ }
+
+ public StoryLocalizedLexer(LocalizedStorySupport kwSupport) {
+ this.kwSupport = kwSupport;
+ changeLocale("en");
+ }
+
+ public void changeLocale(String locale) {
+ keywords = kwSupport.getKeywords(locale);
+ charTree = new CharTree('/', null);
+ for (JBKeyword kw : JBKeyword.values()) {
+ String asString = kw.asString(keywords);
+ charTree.push(asString, kw);
+ }
+ }
+
+ @Override
+ public void start(CharSequence buffer, int startOffset, int endOffset, int initialState) {
+ this.buffer = buffer;
+ //this.startOffset = startOffset;
+ this.position = startOffset;
+ this.endOffset = endOffset;
+ this.state = State.values()[initialState];
+ this.tokenType = null;
+ }
+
+ @Override
+ public int getState() {
+ advanceIfRequired();
+ return state.ordinal();
+ }
+
+ @Override
+ public IElementType getTokenType() {
+ advanceIfRequired();
+ return tokenType;
+ }
+
+ @Override
+ public int getTokenStart() {
+ advanceIfRequired();
+ return currentTokenStart;
+ }
+
+ @Override
+ public int getTokenEnd() {
+ advanceIfRequired();
+ return position;
+ }
+
+ @Override
+ public CharSequence getBufferSequence() {
+ return buffer;
+ }
+
+ @Override
+ public int getBufferEnd() {
+ return endOffset;
+ }
+
+ @Override
+ public void advance() {
+ advanceIfRequired();
+ tokenType = null;
+ }
+
+ private void advanceIfRequired() {
+ if (tokenType == null) {
+ locateToken();
+ }
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ private void locateToken() {
+ if (tokenType != null || position >= endOffset) {
+ return;
+ }
+
+ this.currentTokenStart = position;
+
+ if (consume(keywords.ignorable())) {
+ consume(INPUT_CHAR);
+ if (state == State.YYINITIAL) {
+ String locale = LocalizedStorySupport.checkForLanguageDefinition(tokenText());
+ if(locale!=null) {
+ changeLocale(locale);
+ tokenType = StoryTokenType.COMMENT_WITH_LOCALE;
+ return;
+ }
+ }
+ tokenType = StoryTokenType.COMMENT;
+ return;
+ }
+
+ switch (state) {
+ case YYINITIAL:
+ case IN_STORY: {
+ CharTree.Entry entry = charTree.lookup(buffer, position);
+ if (entry.hasValue()) {
+ tokenType = tokenType(entry.value);
+ position += entry.length;
+ return;
+ }
+ else if (consume(CRLF)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+ return;
+ }
+ else {
+ consume(INPUT_CHAR);
+ tokenType = StoryTokenType.STORY_DESCRIPTION;
+ return;
+ }
+ }
+ case IN_DISPATCH: {
+ if (consume(CRLF) || consume(SPACES)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+ return;
+ }
+ CharTree.Entry entry = charTree.lookup(buffer, position);
+ tokenType = tokenType(entry.value);
+ position += entry.length;
+ return;
+ }
+ case IN_SCENARIO: {
+ if (consume(CRLF)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+ //
+ CharTree.Entry entry = charTree.lookup(buffer, position);
+ if (entry.hasValue()) {
+ switch (entry.value) {
+ case Given:
+ case When:
+ case Then:
+ case And:
+ case Meta:
+ case ExamplesTable:
+ case Narrative:
+ case AsA:
+ case IWantTo:
+ case InOrderTo:
+ case Scenario:
+ state = State.IN_DISPATCH;
+ return;
+ case ExamplesTableHeaderSeparator:
+ case ExamplesTableValueSeparator:
+ state = State.IN_OTHER_TABLE;
+ return;
+ }
+ }
+ return;
+ }
+ else {
+ consume(INPUT_CHAR);
+ tokenType = StoryTokenType.SCENARIO_TEXT;
+ return;
+ }
+ }
+ case IN_META: {
+ CharTree.Entry entry = charTree.lookup(buffer, position);
+ if (entry.hasValue()) {
+ switch (entry.value) {
+ case MetaProperty:
+ position += entry.length;
+ consume(META_PROPERTY_CHARS);
+ tokenType = StoryTokenType.META_KEY;
+ return;
+ default:
+ tokenType = tokenType(entry.value);
+ position += entry.length;
+ return;
+ }
+ }
+ else if (consume(SPACES)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+ return;
+ }
+ else if (consume(CRLF)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+
+ //
+ entry = charTree.lookup(buffer, position);
+ if (entry.hasValue()) {
+ switch (entry.value) {
+ case Given:
+ case When:
+ case Then:
+ case And:
+ case Meta:
+ case ExamplesTable:
+ case Narrative:
+ case AsA:
+ case IWantTo:
+ case InOrderTo:
+ case Scenario:
+ state = State.IN_DISPATCH;
+ return;
+ case ExamplesTableHeaderSeparator:
+ case ExamplesTableValueSeparator:
+ state = State.IN_OTHER_TABLE;
+ return;
+
+ }
+ }
+ return;
+ }
+ else {
+ consumeUntil(META_PROPERTY_CHARS, keywords.metaProperty());
+ tokenType = StoryTokenType.META_TEXT;
+ return;
+ }
+ }
+ case IN_STEP: {
+ if (consume(CRLF)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+
+ //
+ CharTree.Entry entry = charTree.lookup(buffer, position);
+ if (entry.hasValue()) {
+ switch (entry.value) {
+ case Given:
+ case When:
+ case Then:
+ case And:
+ case Meta:
+ case ExamplesTable:
+ case Narrative:
+ case AsA:
+ case IWantTo:
+ case InOrderTo:
+ case Scenario:
+ state = State.IN_DISPATCH;
+ return;
+ case ExamplesTableHeaderSeparator:
+ case ExamplesTableValueSeparator:
+ state = State.IN_STEP_TABLE;
+ return;
+ }
+ }
+ return;
+ }
+ else {
+ consume(INPUT_CHAR);
+ tokenType = StoryTokenType.STEP_TEXT;
+ return;
+ }
+ }
+ case IN_OTHER_TABLE:
+ case IN_STEP_TABLE:
+ case IN_TABLE: {
+ if (consume(CRLF)) {
+ tokenType = StoryTokenType.WHITE_SPACE;
+ //
+ CharTree.Entry entry = charTree.lookup(buffer, position);
+ if (entry.hasValue()) {
+ switch (entry.value) {
+ case Given:
+ case When:
+ case Then:
+ case And:
+ case Meta:
+ case ExamplesTable:
+ case Narrative:
+ case AsA:
+ case IWantTo:
+ case InOrderTo:
+ case Scenario:
+ state = State.IN_DISPATCH;
+ return;
+ }
+ }
+ return;
+ }
+ else if (consume(keywords.examplesTableHeaderSeparator())) {
+ tokenType = StoryTokenType.TABLE_DELIM;
+ return;
+ }
+ else if (consume(keywords.examplesTableValueSeparator())) {
+ tokenType = StoryTokenType.TABLE_DELIM;
+ return;
+ }
+ else if (consumeUntil(INPUT_CHAR, keywords.examplesTableHeaderSeparator(), keywords.examplesTableValueSeparator())) {
+ tokenType = StoryTokenType.TABLE_CELL;
+ return;
+ }
+ }
+ case IN_EXAMPLES:
+ default:
+ throw new UnsupportedOperationException("State: " + state.name());
+ }
+ }
+
+ private CharSequence tokenText() {
+ return buffer.subSequence(this.currentTokenStart, this.position);
+ }
+
+ private boolean matchesAhead(String text) {
+ if (position + text.length() > endOffset) {
+ return false;
+ }
+ for (int i = 0; i < text.length(); i++) {
+ if (text.charAt(i) != buffer.charAt(position + i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean consume(String data) {
+ if (matchesAhead(data)) {
+ position += data.length();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean consumeUntil(CharFilter filter, String stopWord) {
+ int previousPosition = position;
+ while (position < endOffset && !matchesAhead(stopWord) && filter.accept(buffer.charAt(position))) {
+ position++;
+ }
+ return position != previousPosition;
+ }
+
+ private boolean consumeUntil(CharFilter filter, String stopWord1, String stopWord2) {
+ int previousPosition = position;
+ while (position < endOffset && !(matchesAhead(stopWord1) || matchesAhead(stopWord2))
+ && filter.accept(buffer.charAt(position)))
+ {
+ position++;
+ }
+ return position != previousPosition;
+ }
+
+ private boolean consume(CharFilter filter) {
+ int previousPosition = position;
+ while (position < endOffset && filter.accept(buffer.charAt(position))) {
+ position++;
+ }
+ return position != previousPosition;
+ }
+
+ public State lexerState() {
+ return state;
+ }
+
+ public interface CharFilter {
+ boolean accept(char c);
+ }
+
+ private static CharFilter SPACES = new CharFilter() {
+ @Override
+ public boolean accept(char c) {
+ return c == ' ' || c == '\t';
+ }
+ };
+
+ private static CharFilter CRLF = new CharFilter() {
+ @Override
+ public boolean accept(char c) {
+ return c == '\r' || c == '\n';
+ }
+ };
+
+ private static CharFilter INPUT_CHAR = new CharFilter() {
+ @Override
+ public boolean accept(char c) {
+ return c != '\r' && c != '\n';
+ }
+ };
+
+ private static CharFilter META_PROPERTY_CHARS = new CharFilter() {
+ @Override
+ public boolean accept(char c) {
+ return !SPACES.accept(c) && !CRLF.accept(c);
+ }
+ };
+
+ private IElementType tokenType(JBKeyword value) {
+ switch (value) {
+ case Given:
+ state = State.IN_STEP;
+ return StoryTokenType.STEP_TYPE_GIVEN;
+ case When:
+ state = State.IN_STEP;
+ return StoryTokenType.STEP_TYPE_WHEN;
+ case Then:
+ state = State.IN_STEP;
+ return StoryTokenType.STEP_TYPE_THEN;
+ case And:
+ state = State.IN_STEP;
+ return StoryTokenType.STEP_TYPE_AND;
+ case Ignorable:
+ case ExamplesTableIgnorableSeparator:
+ return StoryTokenType.COMMENT;
+ case Narrative:
+ case AsA:
+ case InOrderTo:
+ case IWantTo:
+ state = State.IN_STORY;
+ return StoryTokenType.NARRATIVE_TYPE;
+ case ExamplesTable:
+ return StoryTokenType.EXAMPLE_TYPE;
+ case ExamplesTableHeaderSeparator:
+ case ExamplesTableValueSeparator:
+ state = State.IN_TABLE;
+ return StoryTokenType.TABLE_DELIM;
+ case GivenStories:
+ return StoryTokenType.GIVEN_STORIES;
+ case Meta:
+ state = State.IN_META;
+ return StoryTokenType.META;
+ case Scenario:
+ state = State.IN_SCENARIO;
+ return StoryTokenType.SCENARIO_TYPE;
+
+ case MetaProperty:
+ break;
+ case ExamplesTableRow:
+ break;
+ }
+ return StoryTokenType.BAD_CHARACTER;
+ }
+
+
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlighter.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlighter.java
index ff5d8ec..2108814 100644
--- a/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlighter.java
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlighter.java
@@ -16,60 +16,114 @@
package com.github.kumaraman21.intellijbehave.highlighter;
import com.intellij.lexer.Lexer;
+import com.intellij.openapi.editor.HighlighterColors;
import com.intellij.openapi.editor.SyntaxHighlighterColors;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
import com.intellij.psi.tree.IElementType;
import gnu.trove.THashMap;
-import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
public class StorySyntaxHighlighter extends SyntaxHighlighterBase {
- private static final Map KEYS;
+ private static final Map ATTRIBUTES = new THashMap();
+
+ @NotNull
+ @Override
+ public Lexer getHighlightingLexer() {
+ return new StorySyntaxHighlightingLexer();
+ }
+
+ @NotNull
+ @Override
+ public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
+ return pack(ATTRIBUTES.get(tokenType));
+ }
- private static final TextAttributesKey STORY_DESCRIPTION_ATTRIBUTES
- = TextAttributesKey.createTextAttributesKey("STORY_DESCRIPTION_ATTRIBUTES",
- SyntaxHighlighterColors.NUMBER.getDefaultAttributes());
- private static final TextAttributesKey SCENARIO_TEXT_ATTRIBUTES
- = TextAttributesKey.createTextAttributesKey("SCENARIO_TEXT_ATTRIBUTES",
- CodeInsightColors.STATIC_FIELD_ATTRIBUTES.getDefaultAttributes());
- private static final TextAttributesKey STEP_TYPE_ATTRIBUTES
- = TextAttributesKey.createTextAttributesKey("STEP_TYPE_ATTRIBUTES",
- SyntaxHighlighterColors.KEYWORD.getDefaultAttributes());
- private static final TextAttributesKey STEP_TEXT_ATTRIBUTES
- = TextAttributesKey.createTextAttributesKey("STEP_TEXT_ATTRIBUTES",
- SyntaxHighlighterColors.STRING.getDefaultAttributes());
- private static final TextAttributesKey COMMENT_ATTRIBUTES
- = TextAttributesKey.createTextAttributesKey("COMMENT_ATTRIBUTES",
- SyntaxHighlighterColors.LINE_COMMENT.getDefaultAttributes());
- private static final TextAttributesKey BAD_CHARACTER_ATTRIBUTES
- = TextAttributesKey.createTextAttributesKey("BAD_CHARACTER_ATTRIBUTES",
- SyntaxHighlighterColors.INVALID_STRING_ESCAPE.getDefaultAttributes());
+ @NonNls
+ public static final String STORY_DESCRIPTION_ID = "JBEHAVE.STORY_DESCRIPTION";
+ @NonNls
+ public static final String SCENARIO_TYPE_ID = "JBEHAVE.SCENARIO_TYPE";
+ @NonNls
+ public static final String SCENARIO_TEXT_ID = "JBEHAVE.SCENARIO_TEXT";
+ @NonNls
+ public static final String STEP_TYPE_ID = "JBEHAVE.STEP_TYPE";
+ @NonNls
+ public static final String STEP_TEXT_ID = "JBEHAVE.STEP_TEXT";
+ @NonNls
+ public static final String TABLE_DELIM_ID = "JBEHAVE.TABLE_DELIM";
+ @NonNls
+ public static final String TABLE_CELL_ID = "JBEHAVE.TABLE_CELL";
+ @NonNls
+ public static final String META_TYPE_ID = "JBEHAVE.META_TYPE";
+ @NonNls
+ public static final String META_KEY_ID = "JBEHAVE.META_KEY";
+ @NonNls
+ public static final String META_TEXT_ID = "JBEHAVE.META_TEXT";
+ @NonNls
+ public static final String LINE_COMMENT_ID = "JBEHAVE.COMMENT";
+ @NonNls
+ public static final String BAD_CHARACTER_ID = "JBEHAVE.BAD_CHARACTER";
- static {
- KEYS = new THashMap();
+ // Registering TextAttributes
+ static {
+ createKey(STORY_DESCRIPTION_ID, SyntaxHighlighterColors.NUMBER);
+ createKey(SCENARIO_TYPE_ID, CodeInsightColors.STATIC_FIELD_ATTRIBUTES);
+ createKey(SCENARIO_TEXT_ID, CodeInsightColors.STATIC_FIELD_ATTRIBUTES);
+ createKey(STEP_TYPE_ID, SyntaxHighlighterColors.KEYWORD);
+ createKey(STEP_TEXT_ID, HighlighterColors.TEXT);
+ createKey(TABLE_DELIM_ID, SyntaxHighlighterColors.BRACES);
+ createKey(TABLE_CELL_ID, SyntaxHighlighterColors.STRING);
+ createKey(META_TYPE_ID, SyntaxHighlighterColors.KEYWORD);
+ createKey(META_KEY_ID, SyntaxHighlighterColors.STRING);
+ createKey(META_TEXT_ID, SyntaxHighlighterColors.STRING);
+ createKey(LINE_COMMENT_ID, SyntaxHighlighterColors.LINE_COMMENT);
+ createKey(BAD_CHARACTER_ID, SyntaxHighlighterColors.INVALID_STRING_ESCAPE);
+ }
- KEYS.put(StoryTokenType.STORY_DESCRIPTION, STORY_DESCRIPTION_ATTRIBUTES);
- KEYS.put(StoryTokenType.SCENARIO_TEXT, SCENARIO_TEXT_ATTRIBUTES);
- KEYS.put(StoryTokenType.STEP_TYPE, STEP_TYPE_ATTRIBUTES);
- KEYS.put(StoryTokenType.STEP_TEXT, STEP_TEXT_ATTRIBUTES);
- KEYS.put(StoryTokenType.TABLE_ROW, STEP_TEXT_ATTRIBUTES);
- KEYS.put(StoryTokenType.COMMENT, COMMENT_ATTRIBUTES);
- KEYS.put(StoryTokenType.BAD_CHARACTER, BAD_CHARACTER_ATTRIBUTES);
- }
+ public static TextAttributesKey STORY_DESCRIPTION = TextAttributesKey.createTextAttributesKey(STORY_DESCRIPTION_ID);
+ public static TextAttributesKey SCENARIO_TYPE = TextAttributesKey.createTextAttributesKey(SCENARIO_TYPE_ID);
+ public static TextAttributesKey SCENARIO_TEXT = TextAttributesKey.createTextAttributesKey(SCENARIO_TEXT_ID);
+ public static TextAttributesKey STEP_TYPE = TextAttributesKey.createTextAttributesKey(STEP_TYPE_ID);
+ public static TextAttributesKey STEP_TEXT = TextAttributesKey.createTextAttributesKey(STEP_TEXT_ID);
+ public static TextAttributesKey TABLE_DELIM = TextAttributesKey.createTextAttributesKey(TABLE_DELIM_ID);
+ public static TextAttributesKey TABLE_CELL = TextAttributesKey.createTextAttributesKey(TABLE_CELL_ID);
+ public static TextAttributesKey META_TYPE = TextAttributesKey.createTextAttributesKey(META_TYPE_ID);
+ public static TextAttributesKey META_KEY = TextAttributesKey.createTextAttributesKey(META_KEY_ID);
+ public static TextAttributesKey META_TEXT = TextAttributesKey.createTextAttributesKey(META_TEXT_ID);
+ public static TextAttributesKey LINE_COMMENT = TextAttributesKey.createTextAttributesKey(LINE_COMMENT_ID);
+ public static TextAttributesKey BAD_CHARACTER = TextAttributesKey.createTextAttributesKey(BAD_CHARACTER_ID);
- @NotNull
- @Override
- public Lexer getHighlightingLexer() {
- return new StorySyntaxHighlightingLexer();
- }
+ static {
+ ATTRIBUTES.put(StoryTokenType.STORY_DESCRIPTION, STORY_DESCRIPTION);
+ ATTRIBUTES.put(StoryTokenType.NARRATIVE_TYPE, STORY_DESCRIPTION);
+ ATTRIBUTES.put(StoryTokenType.NARRATIVE_TEXT, STORY_DESCRIPTION);
+ ATTRIBUTES.put(StoryTokenType.SCENARIO_TYPE, SCENARIO_TYPE);
+ ATTRIBUTES.put(StoryTokenType.SCENARIO_TEXT, SCENARIO_TEXT);
+ ATTRIBUTES.put(StoryTokenType.STEP_TYPE, STEP_TYPE);
+ ATTRIBUTES.put(StoryTokenType.STEP_TYPE_GIVEN, STEP_TYPE);
+ ATTRIBUTES.put(StoryTokenType.STEP_TYPE_WHEN, STEP_TYPE);
+ ATTRIBUTES.put(StoryTokenType.STEP_TYPE_THEN, STEP_TYPE);
+ ATTRIBUTES.put(StoryTokenType.STEP_TYPE_AND, STEP_TYPE);
+ ATTRIBUTES.put(StoryTokenType.STEP_TEXT, STEP_TEXT);
+ ATTRIBUTES.put(StoryTokenType.TABLE_DELIM, TABLE_DELIM);
+ ATTRIBUTES.put(StoryTokenType.TABLE_CELL, TABLE_CELL);
+ ATTRIBUTES.put(StoryTokenType.META, META_TYPE);
+ ATTRIBUTES.put(StoryTokenType.META_KEY, META_KEY);
+ ATTRIBUTES.put(StoryTokenType.META_TEXT, META_TEXT);
+ ATTRIBUTES.put(StoryTokenType.COMMENT, LINE_COMMENT);
+ ATTRIBUTES.put(StoryTokenType.COMMENT_WITH_LOCALE, LINE_COMMENT);
+ ATTRIBUTES.put(StoryTokenType.BAD_CHARACTER, BAD_CHARACTER);
+ }
- @NotNull
- @Override
- public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
- return new TextAttributesKey[]{KEYS.get(tokenType)};
- }
+ private static TextAttributesKey createKey(String externalName, TextAttributesKey textAttributesKey) {
+ return TextAttributesKey.createTextAttributesKey(externalName, textAttributesKey.getDefaultAttributes());
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlightingLexer.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlightingLexer.java
index 0a98ab0..92b8252 100644
--- a/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlightingLexer.java
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StorySyntaxHighlightingLexer.java
@@ -19,6 +19,6 @@
public class StorySyntaxHighlightingLexer extends LayeredLexer {
public StorySyntaxHighlightingLexer() {
- super(new StoryLexer());
+ super(new StoryLexerFactory().createLexer());
}
}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryTokenType.java b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryTokenType.java
index ef27b64..0cd7377 100644
--- a/src/com/github/kumaraman21/intellijbehave/highlighter/StoryTokenType.java
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/StoryTokenType.java
@@ -15,28 +15,71 @@
*/
package com.github.kumaraman21.intellijbehave.highlighter;
+import static com.github.kumaraman21.intellijbehave.language.StoryFileType.STORY_FILE_TYPE;
+
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
-import org.jetbrains.annotations.NonNls;
+import com.intellij.psi.tree.TokenSet;
-import static com.github.kumaraman21.intellijbehave.language.StoryFileType.STORY_FILE_TYPE;
+import org.jetbrains.annotations.NonNls;
public class StoryTokenType extends IElementType {
- public StoryTokenType(@NonNls String debugName) {
- super(debugName, STORY_FILE_TYPE.getLanguage());
- }
+ public static final IElementType WHITE_SPACE = TokenType.WHITE_SPACE;
+ public static final IElementType BAD_CHARACTER = TokenType.BAD_CHARACTER;
+
+ public static final IElementType STORY_DESCRIPTION = new StoryTokenType("STORY_DESCRIPTION");
+ public static final IElementType SCENARIO_TYPE = new StoryTokenType("SCENARIO_TYPE");
+ public static final IElementType SCENARIO_TEXT = new StoryTokenType("SCENARIO_TEXT");
+
+ public static final IElementType STEP_TYPE = new StoryTokenType("STEP_TYPE");
+ public static final IElementType STEP_TYPE_GIVEN = new StoryTokenType("STEP_TYPE_GIVEN");
+ public static final IElementType STEP_TYPE_WHEN = new StoryTokenType("STEP_TYPE_WHEN");
+ public static final IElementType STEP_TYPE_THEN = new StoryTokenType("STEP_TYPE_THEN");
+ public static final IElementType STEP_TYPE_AND = new StoryTokenType("STEP_TYPE_AND");
+
+ public static final TokenSet STEP_TYPES = TokenSet.create(STEP_TYPE, STEP_TYPE_GIVEN, STEP_TYPE_WHEN, STEP_TYPE_THEN, STEP_TYPE_AND);
+
+ public static final IElementType STEP_TEXT = new StoryTokenType("STEP_TEXT");
+
+ public static final IElementType TABLE_DELIM = new StoryTokenType("TABLE_DELIM");
+ public static final IElementType TABLE_CELL = new StoryTokenType("TABLE_CELL");
+
+ public static final IElementType COMMENT = new StoryTokenType("COMMENT");
+ public static final IElementType COMMENT_WITH_LOCALE = new StoryTokenType("COMMENT_WITH_LOCALE");
+
+ public static final IElementType META = new StoryTokenType("META");
+ public static final IElementType META_KEY = new StoryTokenType("META_KEY");
+ public static final IElementType META_TEXT = new StoryTokenType("META_TEXT");
+ public static final IElementType EXAMPLE_TYPE = new StoryTokenType("EXAMPLE_TYPE");
+ public static final IElementType GIVEN_STORIES = new StoryTokenType("GIVEN_STORIES");
+
+ public static final IElementType NARRATIVE_TYPE = new StoryTokenType("NARRATIVE_TYPE");
+ public static final IElementType NARRATIVE_TEXT = new StoryTokenType("NARRATIVE_TEXT");
- public static final IElementType WHITE_SPACE = TokenType.WHITE_SPACE;
- public static final IElementType BAD_CHARACTER = TokenType.BAD_CHARACTER;
+ private final String key;
+ public StoryTokenType(@NonNls String debugName) {
+ super(debugName, STORY_FILE_TYPE.getLanguage());
+ this.key = debugName;
+ }
- public static final IElementType STORY_DESCRIPTION = new StoryTokenType("STORY_DESCRIPTION");
- public static final IElementType SCENARIO_TEXT = new StoryTokenType("SCENARIO_TEXT");
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof StoryTokenType)
+ && ((StoryTokenType)other).key.equals(key);
+ }
- public static final IElementType STEP_TYPE = new StoryTokenType("STEP_TYPE");
- public static final IElementType STEP_TEXT = new StoryTokenType("STEP_TEXT");
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
- public static final IElementType TABLE_ROW = new StoryTokenType("TABLE_ROW");
+ public static boolean isStepType(IElementType tokenType) {
+ return tokenType == STEP_TYPE_GIVEN
+ || tokenType == STEP_TYPE_WHEN
+ || tokenType == STEP_TYPE_THEN
+ || tokenType == STEP_TYPE_AND
+ || tokenType == STEP_TYPE;
+ }
- public static final IElementType COMMENT = new StoryTokenType("COMMENT");
}
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/_StoryLexer.java b/src/com/github/kumaraman21/intellijbehave/highlighter/_StoryLexer.java
index a2d1c06..4c9f97c 100644
--- a/src/com/github/kumaraman21/intellijbehave/highlighter/_StoryLexer.java
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/_StoryLexer.java
@@ -1,15 +1,16 @@
-/* The following code was generated by JFlex 1.4.3 on 12/21/11 8:00 PM */
+/* The following code was generated by JFlex 1.4.3 on 27/08/12 18:33 */
package com.github.kumaraman21.intellijbehave.highlighter;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
+import java.util.Stack;
/**
* This class is a scanner generated by
* JFlex 1.4.3
- * on 12/21/11 8:00 PM from the specification file
+ * on 27/08/12 18:33 from the specification file
* Story.flex
*/
class _StoryLexer implements FlexLexer {
@@ -17,9 +18,14 @@ class _StoryLexer implements FlexLexer {
private static final int ZZ_BUFFERSIZE = 16384;
/** lexical states */
- public static final int IN_SCENARIO = 2;
+ public static final int IN_SCENARIO = 6;
+ public static final int IN_STORY = 4;
public static final int YYINITIAL = 0;
- public static final int IN_STEP = 4;
+ public static final int IN_TABLE = 12;
+ public static final int IN_DIRECTIVE = 2;
+ public static final int IN_STEP = 8;
+ public static final int IN_META = 10;
+ public static final int IN_EXAMPLES = 14;
/**
* ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
@@ -28,18 +34,19 @@ class _StoryLexer implements FlexLexer {
* l is of the form l = 2*k, k a non negative integer
*/
private static final int ZZ_LEXSTATE[] = {
- 0, 0, 1, 1, 2, 2
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7
};
/**
* Translates characters to character classes
*/
private static final String ZZ_CMAP_PACKED =
- "\11\0\1\3\1\1\1\0\1\3\1\2\22\0\1\3\1\4\13\0"+
- "\1\5\14\0\1\16\6\0\1\23\5\0\1\17\13\0\1\6\1\21"+
- "\2\0\1\21\11\0\1\12\1\0\1\7\1\24\1\10\2\0\1\22"+
- "\1\14\4\0\1\11\1\15\2\0\1\13\3\0\1\20\5\0\1\25"+
- "\uff83\0";
+ "\11\0\1\3\1\2\1\0\1\3\1\1\22\0\1\17\1\36\13\0"+
+ "\1\37\14\0\1\16\5\0\1\5\1\34\3\0\1\22\1\0\1\30"+
+ "\5\0\1\20\5\0\1\6\1\32\2\0\1\32\11\0\1\12\1\0"+
+ "\1\7\1\35\1\10\2\0\1\33\1\14\2\0\1\26\1\24\1\11"+
+ "\1\15\1\25\1\0\1\13\1\27\1\21\1\0\1\31\1\0\1\23"+
+ "\3\0\1\4\uff83\0";
/**
* Translates characters to character classes
@@ -52,14 +59,15 @@ class _StoryLexer implements FlexLexer {
private static final int [] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
- "\2\1\1\2\1\1\1\3\2\1\1\3\2\1\2\2"+
- "\1\3\1\0\1\1\1\3\3\0\1\4\4\1\1\4"+
- "\1\1\1\0\1\1\3\0\3\1\1\5\1\0\1\1"+
- "\2\0\2\1\1\0\1\1\2\6\1\0\1\1\1\0"+
- "\1\1\1\0\1\1\1\0\1\1\1\0\1\1\2\7";
+ "\10\0\1\1\2\2\10\1\1\3\1\4\1\5\7\3"+
+ "\2\4\1\6\1\7\1\10\1\11\1\12\2\13\1\14"+
+ "\1\4\1\15\7\1\7\0\1\15\7\0\5\1\5\0"+
+ "\1\16\7\0\3\1\3\0\1\17\5\0\2\1\1\0"+
+ "\1\20\4\0\2\1\4\0\2\1\4\0\1\1\5\0"+
+ "\1\21\1\0\1\22";
private static int [] zzUnpackAction() {
- int [] result = new int[57];
+ int [] result = new int[122];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
@@ -84,17 +92,25 @@ private static int zzUnpackAction(String packed, int offset, int [] result) {
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
private static final String ZZ_ROWMAP_PACKED_0 =
- "\0\0\0\26\0\54\0\102\0\130\0\156\0\204\0\232"+
- "\0\260\0\306\0\334\0\362\0\u0108\0\u011e\0\u0134\0\u014a"+
- "\0\u0160\0\u0176\0\u018c\0\u01a2\0\u01b8\0\u01ce\0\u01e4\0\u01fa"+
- "\0\u0210\0\u0226\0\u023c\0\u0252\0\u0268\0\u027e\0\u0294\0\u02aa"+
- "\0\u02c0\0\u02d6\0\u02ec\0\u0302\0\u0318\0\u032e\0\u0344\0\u035a"+
- "\0\u0370\0\u0386\0\u039c\0\u03b2\0\102\0\u03c8\0\u03de\0\u03f4"+
- "\0\u040a\0\u0420\0\u0436\0\u044c\0\u0462\0\u0478\0\u048e\0\u0478"+
- "\0\u048e";
+ "\0\0\0\40\0\100\0\140\0\200\0\240\0\300\0\340"+
+ "\0\u0100\0\u0120\0\u0140\0\u0160\0\u0180\0\u01a0\0\u01c0\0\u01e0"+
+ "\0\u0200\0\u0220\0\u0240\0\u0140\0\u0260\0\u0140\0\u0280\0\u02a0"+
+ "\0\u02c0\0\u02e0\0\u0300\0\u0320\0\u0340\0\u0360\0\u0380\0\u03a0"+
+ "\0\u03c0\0\u03e0\0\u0400\0\u0420\0\u0440\0\u0140\0\u0140\0\u0140"+
+ "\0\u0160\0\u0460\0\u0480\0\u04a0\0\u04c0\0\u04e0\0\u0500\0\u0520"+
+ "\0\u0540\0\u0560\0\u0580\0\u05a0\0\u05c0\0\u05e0\0\u0600\0\u0140"+
+ "\0\u0620\0\u0640\0\u0660\0\u0680\0\u06a0\0\u06c0\0\u06e0\0\u0700"+
+ "\0\u0720\0\u0740\0\u0760\0\u0780\0\u07a0\0\u07c0\0\u07e0\0\u0800"+
+ "\0\u0820\0\u0840\0\u0860\0\u0880\0\u08a0\0\u08c0\0\u08e0\0\u0900"+
+ "\0\u0920\0\u0940\0\u0960\0\u0980\0\u09a0\0\u09c0\0\u09e0\0\u0140"+
+ "\0\u0a00\0\u0a20\0\u0a40\0\u0a60\0\u0a80\0\u0aa0\0\u0ac0\0\u0ae0"+
+ "\0\u0140\0\u0b00\0\u0b20\0\u0b40\0\u0b60\0\u0b80\0\u0ba0\0\u0bc0"+
+ "\0\u0be0\0\u0c00\0\u0c20\0\u0c40\0\u0c60\0\u0c80\0\u0ca0\0\u0cc0"+
+ "\0\u0ce0\0\u0d00\0\u0d20\0\u0d40\0\u0d60\0\u0d80\0\u0da0\0\u0140"+
+ "\0\u0dc0\0\u0140";
private static int [] zzUnpackRowMap() {
- int [] result = new int[57];
+ int [] result = new int[122];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
@@ -117,37 +133,59 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) {
private static final int [] ZZ_TRANS = zzUnpackTrans();
private static final String ZZ_TRANS_PACKED_0 =
- "\1\4\1\5\1\6\1\7\23\4\1\10\1\11\1\7"+
- "\1\12\21\4\1\13\2\5\1\14\22\13\1\4\1\0"+
- "\24\4\1\0\2\5\1\15\2\0\1\16\17\0\1\4"+
- "\1\5\1\6\1\7\2\4\1\17\20\4\1\15\2\7"+
- "\22\4\1\0\2\10\1\20\2\0\1\16\10\0\1\21"+
- "\1\0\1\22\1\0\1\23\1\0\1\24\1\4\1\10"+
- "\1\11\1\25\2\4\1\17\10\4\1\26\1\4\1\27"+
- "\1\4\1\30\1\4\1\31\1\4\1\0\3\4\1\32"+
- "\20\4\1\13\2\0\24\13\2\15\1\14\22\13\1\0"+
- "\3\15\31\0\1\33\16\0\1\4\1\0\5\4\1\34"+
- "\16\4\1\0\3\20\21\0\1\24\14\0\1\35\33\0"+
- "\1\36\14\0\1\37\14\0\1\24\2\0\23\24\1\4"+
- "\1\20\2\25\21\4\1\31\1\4\1\0\12\4\1\40"+
- "\12\4\1\0\20\4\1\41\4\4\1\0\7\4\1\42"+
- "\14\4\1\31\1\0\1\4\23\31\1\4\1\0\3\4"+
- "\1\43\20\4\10\0\1\44\15\0\1\4\1\0\6\4"+
- "\1\45\15\4\20\0\1\36\15\0\1\46\41\0\1\47"+
- "\1\0\1\4\1\0\16\4\1\41\6\4\1\0\6\4"+
- "\1\50\16\4\1\0\22\4\1\51\1\4\1\43\1\0"+
- "\1\4\23\43\11\0\1\52\14\0\1\4\1\0\7\4"+
- "\1\53\14\4\11\0\1\47\15\0\3\54\22\0\1\4"+
- "\1\0\7\4\1\51\15\4\1\54\2\55\22\4\12\0"+
- "\1\56\13\0\1\4\1\0\10\4\1\57\13\4\41\0"+
- "\1\60\12\0\1\4\1\0\11\4\1\61\12\4\14\0"+
- "\1\62\11\0\1\4\1\0\12\4\1\63\11\4\15\0"+
- "\1\64\10\0\1\4\1\0\13\4\1\65\10\4\16\0"+
- "\1\66\7\0\1\4\1\0\14\4\1\67\7\4\1\70"+
- "\2\0\23\70\1\71\1\0\1\4\23\71";
+ "\1\11\1\12\1\13\1\11\1\14\1\11\1\15\11\11"+
+ "\1\16\1\11\1\17\5\11\1\20\1\11\1\21\1\11"+
+ "\1\22\1\11\1\23\1\11\1\24\3\25\1\26\1\24"+
+ "\1\27\10\24\1\25\1\30\1\24\1\31\5\24\1\32"+
+ "\1\24\1\33\1\24\1\34\1\24\1\35\1\24\1\11"+
+ "\1\36\1\37\35\11\1\40\1\36\1\37\35\40\1\41"+
+ "\1\36\1\37\35\41\1\42\1\36\1\37\2\42\1\43"+
+ "\32\42\1\44\1\45\1\46\1\44\1\47\33\44\1\24"+
+ "\1\36\1\37\1\50\13\24\1\50\20\24\1\11\2\0"+
+ "\35\11\2\0\1\13\75\0\1\51\2\0\35\51\1\11"+
+ "\2\0\4\11\1\52\31\11\2\0\5\11\1\53\30\11"+
+ "\2\0\20\11\1\54\15\11\2\0\11\11\1\55\24\11"+
+ "\2\0\30\11\1\56\5\11\2\0\6\11\1\57\27\11"+
+ "\2\0\34\11\1\60\1\0\3\25\13\0\1\25\27\0"+
+ "\1\61\40\0\1\62\52\0\1\63\30\0\1\64\56\0"+
+ "\1\65\15\0\1\66\65\0\1\67\2\0\1\37\1\0"+
+ "\1\70\1\0\1\71\11\0\1\72\1\0\1\73\5\0"+
+ "\1\74\1\0\1\75\1\0\1\76\1\0\1\77\5\0"+
+ "\1\70\1\0\1\71\11\0\1\72\1\0\1\73\5\0"+
+ "\1\74\1\0\1\75\1\0\1\76\1\0\1\77\1\0"+
+ "\1\40\2\0\35\40\1\41\2\0\35\41\1\42\2\0"+
+ "\2\42\1\0\32\42\1\43\3\0\13\43\1\0\20\43"+
+ "\1\44\2\0\1\44\1\0\33\44\2\0\1\46\35\0"+
+ "\1\11\2\0\5\11\1\100\30\11\2\0\16\11\1\101"+
+ "\17\11\2\0\7\11\1\102\26\11\2\0\26\11\1\56"+
+ "\7\11\2\0\5\11\1\103\30\11\2\0\32\11\1\104"+
+ "\3\11\2\0\34\11\1\14\10\0\1\105\50\0\1\106"+
+ "\30\0\1\107\56\0\1\65\16\0\1\110\64\0\1\111"+
+ "\41\0\1\112\7\0\1\113\40\0\1\114\52\0\1\115"+
+ "\30\0\1\116\56\0\1\117\15\0\1\120\65\0\1\121"+
+ "\1\11\2\0\6\11\1\122\27\11\2\0\7\11\1\123"+
+ "\26\11\2\0\21\11\1\124\14\11\2\0\6\11\1\104"+
+ "\27\11\2\0\14\11\1\14\20\11\11\0\1\125\40\0"+
+ "\1\126\51\0\1\127\24\0\1\111\45\0\1\130\20\0"+
+ "\1\112\2\0\35\112\10\0\1\131\50\0\1\132\30\0"+
+ "\1\133\56\0\1\117\16\0\1\134\64\0\1\135\41\0"+
+ "\1\70\1\11\2\0\7\11\1\136\26\11\2\0\13\11"+
+ "\1\14\22\11\2\0\22\11\1\137\12\11\12\0\1\140"+
+ "\43\0\1\141\46\0\1\142\23\0\1\143\40\0\1\144"+
+ "\51\0\1\145\24\0\1\135\45\0\1\70\20\0\1\11"+
+ "\2\0\10\11\1\146\25\11\2\0\23\11\1\147\11\11"+
+ "\13\0\1\150\52\0\1\151\23\0\1\152\43\0\1\70"+
+ "\46\0\1\153\12\0\1\11\2\0\11\11\1\154\24\11"+
+ "\2\0\5\11\1\155\27\11\14\0\1\156\33\0\1\157"+
+ "\42\0\1\160\52\0\1\161\11\0\1\11\2\0\12\11"+
+ "\1\162\23\11\2\0\24\11\1\123\10\11\15\0\1\163"+
+ "\51\0\1\164\24\0\1\165\33\0\1\166\27\0\1\11"+
+ "\2\0\13\11\1\104\21\11\16\0\1\167\37\0\1\170"+
+ "\36\0\1\171\51\0\1\144\27\0\1\172\36\0\1\135"+
+ "\21\0";
private static int [] zzUnpackTrans() {
- int [] result = new int[1188];
+ int [] result = new int[3552];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
@@ -188,13 +226,14 @@ private static int zzUnpackTrans(String packed, int offset, int [] result) {
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 =
- "\15\1\1\0\2\1\3\0\7\1\1\0\1\1\3\0"+
- "\4\1\1\0\1\1\2\0\2\1\1\0\1\1\1\11"+
- "\1\1\1\0\1\1\1\0\1\1\1\0\1\1\1\0"+
- "\1\1\1\0\3\1";
+ "\10\0\2\1\1\11\10\1\1\11\1\1\1\11\17\1"+
+ "\3\11\10\1\7\0\1\11\7\0\5\1\5\0\1\1"+
+ "\7\0\3\1\3\0\1\11\5\0\2\1\1\0\1\11"+
+ "\4\0\2\1\4\0\2\1\4\0\1\1\5\0\1\11"+
+ "\1\0\1\11";
private static int [] zzUnpackAttribute() {
- int [] result = new int[57];
+ int [] result = new int[122];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
@@ -252,6 +291,53 @@ the source of the yytext() string */
/** denotes if the user-EOF-code has already been executed */
private boolean zzEOFDone;
+ /* user code: */
+ private Stack yystates = new Stack () {{ push(YYINITIAL); }};
+ public boolean trace = false;
+
+ public void yystatePush(int yystate) {
+ if(trace) System.out.println(">>>> PUSH: " + LexicalState.fromLexer(yystate) + " [" + reverseAndMap(yystates) + "]");
+ yybegin(yystate);
+ yystates.push(yystate);
+ }
+
+ private String reverseAndMap(Stack yystates) {
+ StringBuilder builder = new StringBuilder();
+ for(int i=yystates.size()-1; i>=0; i--) {
+ if(builder.length()>0)
+ builder.append(", ");
+ builder.append(LexicalState.fromLexer(yystates.get(i)));
+ }
+ return builder.toString();
+ }
+
+ public void yystatePopNPush(int yystate) {
+ yystatePopNPush(1, yystate);
+ }
+
+ public void yystatePopNPush(int nb, int yystate) {
+ if(trace) System.out.println(">>>> POP'n PUSH : #" + nb + ", " + LexicalState.fromLexer(yystate) + " [" + reverseAndMap(yystates) + "]");
+ for (int i = 0; i < nb; i++) {
+ yystatePop();
+ }
+ yystatePush(yystate);
+ }
+
+ public int yystatePop() {
+ int popped = yystates.pop();
+ if(trace) System.out.println(">>>> POP : " + LexicalState.fromLexer(popped) + " [" + reverseAndMap(yystates) + "]");
+ if(!yystates.isEmpty()) {
+ yybegin(yystates.peek());
+ }// otherwise hopes a push will follow right after
+ return popped;
+ }
+
+ public boolean checkAhead(char c) {
+ if (zzMarkedPos >= zzBuffer.length())
+ return false;
+ return zzBuffer.charAt(zzMarkedPos) == c;
+ }
+
_StoryLexer(java.io.Reader in) {
this.zzReader = in;
@@ -277,7 +363,7 @@ the source of the yytext() string */
char [] map = new char[0x10000];
int i = 0; /* index in packed string */
int j = 0; /* index in unpacked array */
- while (i < 82) {
+ while (i < 106) {
int count = packed.charAt(i++);
char value = packed.charAt(i++);
do map[j++] = value; while (--count > 0);
@@ -416,7 +502,8 @@ public void yypushback(int number) {
private void zzDoEOF() {
if (!zzEOFDone) {
zzEOFDone = true;
-
+ return;
+
}
}
@@ -499,34 +586,78 @@ else if (zzAtEOF) {
zzMarkedPos = zzMarkedPosL;
switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
- case 4:
- { yybegin(IN_SCENARIO); return StoryTokenType.TABLE_ROW;
+ case 10:
+ { return StoryTokenType.TABLE_CELL;
+ }
+ case 19: break;
+ case 15:
+ { yystatePopNPush(2, IN_STEP); return StoryTokenType.STEP_TYPE;
+ }
+ case 20: break;
+ case 14:
+ { yystatePop(); return StoryTokenType.COMMENT;
+ }
+ case 21: break;
+ case 9:
+ { return StoryTokenType.META_KEY;
}
- case 8: break;
+ case 22: break;
case 2:
- { yybegin(IN_SCENARIO); return StoryTokenType.STEP_TEXT;
+ { yystatePush(IN_STORY); yypushback(yytext().length());
}
- case 9: break;
- case 1:
- { return StoryTokenType.STORY_DESCRIPTION;
+ case 23: break;
+ case 7:
+ { return StoryTokenType.STEP_TEXT;
}
- case 10: break;
+ case 24: break;
+ case 11:
+ { yystatePop(); yypushback(1);
+ }
+ case 25: break;
case 5:
- { return StoryTokenType.COMMENT;
+ { yystatePopNPush(1, IN_TABLE); return StoryTokenType.TABLE_DELIM;
}
- case 11: break;
- case 3:
- { return StoryTokenType.WHITE_SPACE;
+ case 26: break;
+ case 13:
+ { yystatePush(IN_DIRECTIVE); yypushback(yytext().length());
}
- case 12: break;
- case 7:
- { yybegin(IN_SCENARIO); return StoryTokenType.SCENARIO_TEXT;
+ case 27: break;
+ case 8:
+ { return StoryTokenType.META_TEXT;
+ }
+ case 28: break;
+ case 12:
+ { return StoryTokenType.TABLE_DELIM;
}
- case 13: break;
+ case 29: break;
case 6:
- { yybegin(IN_STEP); return StoryTokenType.STEP_TYPE;
+ { return StoryTokenType.SCENARIO_TEXT;
+ }
+ case 30: break;
+ case 17:
+ { yystatePopNPush(2, IN_EXAMPLES); return StoryTokenType.EXAMPLE_TYPE;
+ }
+ case 31: break;
+ case 1:
+ { return StoryTokenType.STORY_DESCRIPTION;
+ }
+ case 32: break;
+ case 3:
+ { return StoryTokenType.BAD_CHARACTER;
+ }
+ case 33: break;
+ case 16:
+ { yystatePopNPush(2, IN_META); return StoryTokenType.META;
+ }
+ case 34: break;
+ case 18:
+ { yystatePopNPush(2, IN_SCENARIO); return StoryTokenType.SCENARIO_TYPE;
+ }
+ case 35: break;
+ case 4:
+ { return StoryTokenType.WHITE_SPACE;
}
- case 14: break;
+ case 36: break;
default:
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
diff --git a/src/com/github/kumaraman21/intellijbehave/highlighter/generateLexer.sh b/src/com/github/kumaraman21/intellijbehave/highlighter/generateLexer.sh
new file mode 100755
index 0000000..7f81348
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/highlighter/generateLexer.sh
@@ -0,0 +1,5 @@
+FLEX_DIR=~/Projects/intellij/tools/jflex-1.4.3/bin
+
+$FLEX_DIR/jflex-idea --skel "$FLEX_DIR/../../idea-flex.skeleton" Story.flex
+cat _StoryLexer.java | sed 's/zzBufferL\[zzCurrentPosL\+\+\]/zzBufferL.charAt(zzCurrentPosL\+\+)/' > _StoryLexer.tmp
+mv _StoryLexer.tmp _StoryLexer.java
diff --git a/src/com/github/kumaraman21/intellijbehave/parser/StepPsiElement.java b/src/com/github/kumaraman21/intellijbehave/parser/StepPsiElement.java
index 2d1fb41..e46aff1 100644
--- a/src/com/github/kumaraman21/intellijbehave/parser/StepPsiElement.java
+++ b/src/com/github/kumaraman21/intellijbehave/parser/StepPsiElement.java
@@ -15,37 +15,64 @@
*/
package com.github.kumaraman21.intellijbehave.parser;
+import static org.apache.commons.lang.StringUtils.trim;
+
+import com.github.kumaraman21.intellijbehave.highlighter.StoryTokenType;
import com.github.kumaraman21.intellijbehave.resolver.StepPsiReference;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
-import com.intellij.psi.PsiReference;
+
import org.jbehave.core.steps.StepType;
import org.jetbrains.annotations.NotNull;
-
-import static org.apache.commons.lang.StringUtils.*;
+import org.jetbrains.annotations.Nullable;
public class StepPsiElement extends ASTWrapperPsiElement {
- private StepType stepType;
+ private StepType stepType;
+
+ public StepPsiElement(@NotNull ASTNode node, StepType stepType) {
+ super(node);
+ this.stepType = stepType;
+ }
+
+ @Override
+ @NotNull
+ public StepPsiReference getReference() {
+ return new StepPsiReference(this);
+ }
+
+ public StepType getStepType() {
+ return stepType;
+ }
- public StepPsiElement(@NotNull ASTNode node, StepType stepType) {
- super(node);
- this.stepType = stepType;
- }
+ public boolean isAndStep() {
+ ASTNode keyword = getKeyword();
+ return keyword != null && keyword.getElementType() == StoryTokenType.STEP_TYPE_AND;
+ }
- @Override
- public PsiReference getReference() {
- return new StepPsiReference(this);
- }
+ @Nullable
+ public ASTNode getKeyword() {
+ return getNode().findChildByType(StoryTokenType.STEP_TYPES);
+ }
- public StepType getStepType() {
- return stepType;
- }
+ public String getStepText() {
+ int offset = getStepTextOffset();
+ if(offset==0) {
+ return trim(getText());
+ }
+ return trim(getText().substring(offset));
+ }
- public String getStepText() {
- return trim(substringAfter(getText(), " "));
- }
+ @Nullable
+ public String getActualStepPrefix() {
+ ASTNode keyword = getKeyword();
+ if (keyword == null) { // that's weird!
+ return null;
+ }
+ return keyword.getText();
+ }
- public String getActualStepPrefix() {
- return substringBefore(getText(), " ");
- }
+ public int getStepTextOffset() {
+ String stepPrefix = getActualStepPrefix();
+ return stepPrefix != null ? stepPrefix.length() : 0;
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/parser/StoryElementType.java b/src/com/github/kumaraman21/intellijbehave/parser/StoryElementType.java
index 3d41685..489dd72 100644
--- a/src/com/github/kumaraman21/intellijbehave/parser/StoryElementType.java
+++ b/src/com/github/kumaraman21/intellijbehave/parser/StoryElementType.java
@@ -15,27 +15,36 @@
*/
package com.github.kumaraman21.intellijbehave.parser;
+import static com.github.kumaraman21.intellijbehave.language.StoryFileType.STORY_FILE_TYPE;
+
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
+
+import org.jbehave.core.steps.StepType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
-import static com.github.kumaraman21.intellijbehave.language.StoryFileType.STORY_FILE_TYPE;
-
public class StoryElementType extends IElementType {
+ public static final StoryElementType UNKNOWN_FRAGMENT = new StoryElementType("UNKNOWN_FRAGMENT");
+
+ public static final StoryElementType COMMENT = new StoryElementType("COMMENT");
+ public static final IFileElementType STORY_FILE = new IFileElementType(STORY_FILE_TYPE.getLanguage());
+ public static final StoryElementType STORY = new StoryElementType("STORY");
+ public static final StoryElementType STORY_DESCRIPTION = new StoryElementType("STORY_DESCRIPTION");
+ public static final StoryElementType SCENARIO = new StoryElementType("SCENARIO");
+ public static final StoryElementType META = new StoryElementType("META");
+ public static final StoryElementType EXAMPLES = new StoryElementType("EXAMPLES");
+ public static final StoryElementType TABLE_ROW = new StoryElementType("TABLE_ROW");
- public StoryElementType(@NotNull @NonNls String debugName) {
- super(debugName, STORY_FILE_TYPE.getLanguage());
- }
+ public static final StoryElementType GIVEN_STEP = new StoryElementType("GIVEN_STEP");
+ public static final StoryElementType WHEN_STEP = new StoryElementType("WHEN_STEP");
+ public static final StoryElementType THEN_STEP = new StoryElementType("THEN_STEP");
- public static final IFileElementType STORY_FILE = new IFileElementType(STORY_FILE_TYPE.getLanguage());
- public static final StoryElementType STORY = new StoryElementType("STORY");
- public static final StoryElementType SCENARIO = new StoryElementType("SCENARIO");
+ public static final TokenSet STEPS_TOKEN_SET = TokenSet.create(GIVEN_STEP, WHEN_STEP, THEN_STEP);
- public static final StoryElementType GIVEN_STEP = new StoryElementType("GIVEN_STEP");
- public static final StoryElementType WHEN_STEP = new StoryElementType("WHEN_STEP");
- public static final StoryElementType THEN_STEP = new StoryElementType("THEN_STEP");
- public static final TokenSet STEPS_TOKEN_SET = TokenSet.create(GIVEN_STEP, WHEN_STEP, THEN_STEP);
+ public StoryElementType(@NotNull @NonNls String debugName) {
+ super(debugName, STORY_FILE_TYPE.getLanguage());
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/parser/StoryParser.java b/src/com/github/kumaraman21/intellijbehave/parser/StoryParser.java
index 93c81cf..fee7bf4 100644
--- a/src/com/github/kumaraman21/intellijbehave/parser/StoryParser.java
+++ b/src/com/github/kumaraman21/intellijbehave/parser/StoryParser.java
@@ -16,123 +16,594 @@
package com.github.kumaraman21.intellijbehave.parser;
import com.github.kumaraman21.intellijbehave.highlighter.StoryTokenType;
+import com.github.kumaraman21.intellijbehave.utility.LocalizedStorySupport;
import com.intellij.lang.ASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiParser;
import com.intellij.psi.tree.IElementType;
-import org.jetbrains.annotations.NotNull;
+import com.intellij.psi.tree.TokenSet;
-import static com.github.kumaraman21.intellijbehave.utility.StepTypeMappings.STEP_TEXT_TO_STORY_ELEMENT_TYPE_MAPPING;
+import org.apache.commons.lang.StringUtils;
+import org.jetbrains.annotations.NotNull;
public class StoryParser implements PsiParser {
- @NotNull
- @Override
- public ASTNode parse(IElementType root, PsiBuilder builder) {
- final PsiBuilder.Marker rootMarker = builder.mark();
-
- parseStory(builder);
- rootMarker.done(root);
- return builder.getTreeBuilt();
- }
-
- private void parseStory(PsiBuilder builder) {
- final PsiBuilder.Marker storyMarker = builder.mark();
- parseStoryDescriptionLinesIfPresent(builder);
- parseScenarios(builder);
- storyMarker.done(StoryElementType.STORY);
- }
-
- private void parseStoryDescriptionLinesIfPresent(PsiBuilder builder) {
- if(builder.getTokenType() == StoryTokenType.STORY_DESCRIPTION) {
- while(builder.getTokenType() == StoryTokenType.STORY_DESCRIPTION) {
- parseStoryDescriptionLine(builder);
- }
- }
- }
-
- private void parseStoryDescriptionLine(PsiBuilder builder) {
- builder.advanceLexer();
- }
-
- private void parseScenarios(PsiBuilder builder) {
- if(builder.getTokenType() == StoryTokenType.SCENARIO_TEXT) {
- while(builder.getTokenType() == StoryTokenType.SCENARIO_TEXT) {
- parseScenario(builder);
- }
- }
- else {
- builder.advanceLexer();
- builder.error("Scenario expected");
- }
- }
-
- private void parseScenario(PsiBuilder builder) {
- final PsiBuilder.Marker stepMarker = builder.mark();
- builder.advanceLexer();
- parseSteps(builder);
- parseStoryDescriptionLinesIfPresent(builder);
- stepMarker.done(StoryElementType.SCENARIO);
- }
-
- private void parseSteps(PsiBuilder builder) {
- parseStoryDescriptionLinesIfPresent(builder);
- if(builder.getTokenType() == StoryTokenType.STEP_TYPE) {
-
- StoryElementType previousStepElementType = null;
- while(builder.getTokenType() == StoryTokenType.STEP_TYPE) {
- previousStepElementType = parseStep(builder, previousStepElementType);
- parseStoryDescriptionLinesIfPresent(builder);
- }
- }
- else {
- builder.error("At least one step expected");
- }
- }
-
- private StoryElementType parseStep(PsiBuilder builder, StoryElementType previousStepElementType) {
- final PsiBuilder.Marker stepMarker = builder.mark();
-
- StoryElementType currentStepElementType;
-
- String stepTypeText = builder.getTokenText().trim().toUpperCase();
- if(stepTypeText.equalsIgnoreCase("And")) {
- currentStepElementType = previousStepElementType;
- }
- else {
- currentStepElementType = STEP_TEXT_TO_STORY_ELEMENT_TYPE_MAPPING.get(stepTypeText);
- }
-
- parseStepType(builder);
- parseStepText(builder);
- parseTableIfPresent(builder);
- stepMarker.done(currentStepElementType);
-
- return currentStepElementType;
- }
-
- private void parseStepType(PsiBuilder builder) {
- builder.advanceLexer();
- }
-
- private void parseStepText(PsiBuilder builder) {
- if(builder.getTokenType() == StoryTokenType.STEP_TEXT) {
- builder.advanceLexer();
- }
- else {
- builder.error("Step text expected");
- }
- }
-
- private void parseTableIfPresent(PsiBuilder builder) {
- if(builder.getTokenType() == StoryTokenType.TABLE_ROW) {
- while(builder.getTokenType() == StoryTokenType.TABLE_ROW) {
- parseTableRow(builder);
- }
- }
- }
-
- private void parseTableRow(PsiBuilder builder) {
- builder.advanceLexer();
- }
+ private static final boolean DEBUG = false;
+
+ @NotNull
+ @Override
+ public ASTNode parse(IElementType root, PsiBuilder builder) {
+ final PsiBuilder.Marker rootMarker = builder.mark();
+ builder.setDebugMode(true);
+ parseStory(builder);
+ rootMarker.done(root);
+ return builder.getTreeBuilt();
+ }
+
+ @SuppressWarnings("UnnecessaryLabelOnContinueStatement")
+ private void parseStory(PsiBuilder builder) {
+ final PsiBuilder.Marker storyMarker = builder.mark();
+
+ ParserState state = new ParserState(builder);
+
+ whileLoop:
+ while (!builder.eof()) {
+ IElementType tokenType = builder.getTokenType();
+
+ // Comment and whitespace are not returned by default
+
+ if (isComment(tokenType)) {
+ state.enterComment();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+ else {
+ state.leaveComment();
+ }
+
+ if (isWhitespace(tokenType)) {
+ if(isCrlf(builder.getTokenText())) {
+ // this is never called unfortunately
+ state.leaveTableRow();
+ }
+
+ state.enterWhitespace();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+ else {
+ state.leaveWhitespace();
+ }
+
+
+ if (isStoryDescription(tokenType)) {
+ state.enterStoryDescription();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+ else {
+ state.leaveStoryDescription();
+ }
+
+ if (isScenario(tokenType)) {
+ state.enterScenario();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+ else if(!belongsToScenario(tokenType)) {
+ state.leaveScenario();
+ }
+
+ if (isScenarioText(tokenType)) {
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+
+ if(isMeta(tokenType)) {
+ state.enterMeta();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+
+ if(isStepType(tokenType)) {
+ state.enterStepType(tokenType);
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+
+ if(isStepText(tokenType)) {
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+
+ if(isExampleTable(tokenType)) {
+ state.enterExampleTable();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+ else if(!belongsToTable(tokenType)) {
+ state.leaveExampleTable();
+ }
+
+ if(isTableRow(tokenType)) {
+ state.enterTableRow();
+ builder.advanceLexer();
+ continue whileLoop;
+ }
+
+ // unknown
+ PsiBuilder.Marker unknwonMark = builder.mark();
+ builder.advanceLexer();
+ unknwonMark.done(StoryElementType.UNKNOWN_FRAGMENT);
+ }
+ state.leaveRemainings();
+ storyMarker.done(StoryElementType.STORY);
+ }
+
+ private static boolean isCrlf(String text) {
+ return text.contains("\n") || text.contains("\r");
+ }
+
+ private static class MarkerData {
+ private final PsiBuilder.Marker marker;
+ private final IElementType elementType;
+
+ private MarkerData(PsiBuilder.Marker marker, IElementType elementType) {
+ this.marker = marker;
+ this.elementType = elementType;
+ }
+ public boolean matches(IElementType elementType) {
+ return this.elementType == elementType;
+ }
+ public boolean matches(TokenSet tokenSet) {
+ return tokenSet.contains(this.elementType);
+ }
+ public void applyMark() {
+ marker.done(elementType);
+ }
+ }
+
+ private static class ParserState {
+ private final PsiBuilder builder;
+ private final MarkerData[] markers = new MarkerData[10];
+ private int markerIndex = -1;
+ private StoryElementType previousStepElementType = null;
+
+
+ public ParserState(PsiBuilder builder) {
+ this.builder = builder;
+ }
+
+ private void matchesHeadOrPush(StoryElementType elementType) {
+ if(markerIndex>=0 && markers[markerIndex].matches(elementType)) {
+ return;
+ }
+ markers[++markerIndex] = new MarkerData(builder.mark(), elementType);
+ if(DEBUG) System.out.println("StoryParser$ParserState: PUSH>> " + StringUtils.repeat("..", markerIndex) + elementType);
+ }
+
+ private void popUntilOnlyIfPresent(StoryElementType elementType) {
+ int newMarkerIndex = markerIndex;
+ for(int i=markerIndex; i>=0; i--) {
+ if(markers[i].matches(elementType)) {
+ newMarkerIndex = i - 1;
+ break;
+ }
+ }
+
+ for(int i=newMarkerIndex+1;i<=markerIndex;i++) {
+ if(DEBUG) System.out.println("StoryParser$ParserState: POP >> " + StringUtils.repeat("..", i) + markers[i].elementType);
+ markers[i].applyMark();
+ }
+
+ markerIndex = newMarkerIndex;
+ }
+
+ public void leaveRemainings() {
+ while(markerIndex>=0) {
+ if(DEBUG) System.out.println("StoryParser$ParserState: POP >> " + StringUtils.repeat("..", markerIndex) + markers[markerIndex].elementType);
+ markers[markerIndex--].applyMark();
+ }
+ }
+
+
+ private void popUntilOnlyIfPresent(TokenSet tokenSet) {
+ int newMarkerIndex = markerIndex;
+ for(int i=markerIndex; i>=0; i--) {
+ if(markers[i].matches(tokenSet)) {
+ newMarkerIndex = i - 1;
+ break;
+ }
+ }
+
+ for(int i=newMarkerIndex+1;i<=markerIndex;i++) {
+ System.out.println("StoryParser$ParserState: POP >> " + StringUtils.repeat("..", i) + markers[i].elementType);
+ markers[i].applyMark();
+ }
+
+ markerIndex = newMarkerIndex;
+ }
+
+
+ public void enterComment() {
+ matchesHeadOrPush(StoryElementType.COMMENT);
+ }
+
+ private void leaveComment() {
+ popUntilOnlyIfPresent(StoryElementType.COMMENT);
+ }
+
+ public void enterWhitespace() {
+ }
+
+ public void leaveWhitespace() {
+ }
+
+ public void enterStoryDescription() {
+ leaveRemainings();
+ matchesHeadOrPush(StoryElementType.STORY_DESCRIPTION);
+ }
+
+ private void leaveStoryDescription() {
+ popUntilOnlyIfPresent(StoryElementType.STORY_DESCRIPTION);
+ }
+
+ public void enterScenario() {
+ leaveRemainings();
+ previousStepElementType = null;
+ matchesHeadOrPush(StoryElementType.SCENARIO);
+ }
+
+ private void leaveScenario() {
+ leaveRemainings();
+ previousStepElementType = null;
+ popUntilOnlyIfPresent(StoryElementType.SCENARIO);
+ }
+
+ public void enterMeta() {
+ leaveStoryDescription();
+ leaveExampleTable();
+ leaveTableRow();
+ leaveStep();
+ matchesHeadOrPush(StoryElementType.META);
+ }
+
+ private void leaveMeta() {
+ popUntilOnlyIfPresent(StoryElementType.META);
+ }
+
+ public void enterStepType(IElementType tokenType) {
+ leaveExampleTable();
+ leaveMeta();
+ leaveStep();
+ leaveStoryDescription();
+ leaveTableRow();
+
+ StoryElementType elementType = previousStepElementType;
+ if (tokenType == StoryTokenType.STEP_TYPE_GIVEN) {
+ elementType = StoryElementType.GIVEN_STEP;
+ }
+ else if (tokenType == StoryTokenType.STEP_TYPE_WHEN) {
+ elementType = StoryElementType.WHEN_STEP;
+ }
+ else if (tokenType == StoryTokenType.STEP_TYPE_THEN) {
+ elementType = StoryElementType.THEN_STEP;
+ }
+ else {
+ // should be the AND
+ if (tokenType != StoryTokenType.STEP_TYPE_AND) {
+ // throw...
+ }
+ }
+
+ if (elementType == null) { // yuk!
+ elementType = StoryElementType.GIVEN_STEP;
+ }
+ previousStepElementType = elementType;
+ matchesHeadOrPush(elementType);
+ }
+
+ private void leaveStep() {
+ leaveTableRow();
+ popUntilOnlyIfPresent(StoryElementType.STEPS_TOKEN_SET);
+ }
+
+ public void enterExampleTable() {
+ leaveTableRow();
+ leaveStep();
+ leaveMeta();
+ leaveExampleTable();
+ matchesHeadOrPush(StoryElementType.EXAMPLES);
+ }
+
+ public void leaveExampleTable() {
+ leaveTableRow();
+ popUntilOnlyIfPresent(StoryElementType.EXAMPLES);
+ }
+
+ public void enterTableRow() {
+ matchesHeadOrPush(StoryElementType.TABLE_ROW);
+ }
+
+ public void leaveTableRow() {
+ popUntilOnlyIfPresent(StoryElementType.TABLE_ROW);
+ }
+ }
+
+ private static boolean belongsToScenario(IElementType tokenType) {
+ return isWhitespace(tokenType)
+ || isComment(tokenType)
+ || isScenarioText(tokenType)
+ || isStoryDescription(tokenType)
+ || isStepType(tokenType)
+ || isStepText(tokenType)
+ || isExampleTable(tokenType)
+ || isTableRow(tokenType)
+ || isMeta(tokenType);
+ }
+
+ private boolean belongsToTable(IElementType tokenType) {
+ return isWhitespace(tokenType)
+ || isComment(tokenType)
+ || isTableRow(tokenType);
+ }
+ private static boolean isMeta(IElementType tokenType) {
+ return tokenType == StoryTokenType.META
+ || tokenType == StoryTokenType.META_KEY
+ || tokenType == StoryTokenType.META_TEXT
+ ;
+ }
+
+ private static boolean isGivenStories(IElementType tokenType) {
+ return tokenType == StoryTokenType.GIVEN_STORIES;
+ }
+
+ private static boolean isExampleTable(IElementType tokenType) {
+ return tokenType == StoryTokenType.EXAMPLE_TYPE;
+ }
+
+ private static boolean isTableRow(IElementType tokenType) {
+ return tokenType == StoryTokenType.TABLE_CELL
+ || tokenType == StoryTokenType.TABLE_DELIM;
+ }
+
+ private static boolean isStepType(IElementType tokenType) {
+ return tokenType == StoryTokenType.STEP_TYPE
+ || tokenType == StoryTokenType.STEP_TYPE_GIVEN
+ || tokenType == StoryTokenType.STEP_TYPE_WHEN
+ || tokenType == StoryTokenType.STEP_TYPE_THEN
+ || tokenType == StoryTokenType.STEP_TYPE_AND;
+ }
+
+ private static boolean isStepText(IElementType tokenType) {
+ return tokenType == StoryTokenType.STEP_TEXT;
+ }
+
+ private static boolean isScenario(IElementType tokenType) {
+ return tokenType == StoryTokenType.SCENARIO_TYPE;
+ }
+
+ private static boolean isScenarioText(IElementType tokenType) {
+ return tokenType == StoryTokenType.SCENARIO_TEXT;
+ }
+
+ private static boolean isWhitespace(IElementType tokenType) {
+ return tokenType == StoryTokenType.WHITE_SPACE;
+ }
+
+ private static boolean isComment(IElementType tokenType) {
+ return tokenType == StoryTokenType.COMMENT
+ || tokenType == StoryTokenType.COMMENT_WITH_LOCALE;
+ }
+
+ private static boolean isStoryDescription(IElementType tokenType) {
+ return tokenType == StoryTokenType.STORY_DESCRIPTION
+ || tokenType == StoryTokenType.NARRATIVE_TYPE
+ || tokenType == StoryTokenType.NARRATIVE_TEXT
+ ;
+ }
+
+ private void skipWhitespacesOrComments(PsiBuilder builder) {
+ skipWhitespacesOrComments(builder, false);
+ }
+
+ private String skipWhitespacesOrComments(PsiBuilder builder, boolean checkForLocale) {
+ String storyLocale = "en";
+
+ while (isWhitespace(builder.getTokenType())
+ || builder.getTokenType() == StoryTokenType.COMMENT)
+ {
+ if (builder.getTokenType() == StoryTokenType.COMMENT) {
+ PsiBuilder.Marker commentMark = builder.mark();
+ while (builder.getTokenType() == StoryTokenType.COMMENT) {
+ if (checkForLocale) {
+ String commentText = builder.getTokenText();
+ String locale = LocalizedStorySupport.checkForLanguageDefinition(commentText);
+ if (locale != null) {
+ storyLocale = locale;
+ }
+ }
+ builder.advanceLexer();
+ }
+ commentMark.done(StoryElementType.COMMENT);
+ }
+ else {
+ builder.advanceLexer();
+ }
+ }
+
+ return storyLocale;
+ }
+
+ private void parseStoryDescriptionOrNarrativesLinesIfPresent(PsiBuilder builder) {
+ if (builder.getTokenType() != StoryTokenType.STORY_DESCRIPTION
+ && builder.getTokenType() != StoryTokenType.NARRATIVE_TYPE
+ && builder.getTokenType() != StoryTokenType.NARRATIVE_TEXT)
+ {
+ return;
+ }
+
+ PsiBuilder.Marker marker = builder.mark();
+ while (builder.getTokenType() == StoryTokenType.STORY_DESCRIPTION
+ || builder.getTokenType() == StoryTokenType.NARRATIVE_TYPE
+ || builder.getTokenType() == StoryTokenType.NARRATIVE_TEXT)
+ {
+ parseStoryDescriptionLine(builder);
+ skipWhitespacesOrComments(builder);
+ }
+ marker.done(StoryElementType.STORY_DESCRIPTION);
+ }
+
+ private void parseStoryDescriptionLine(PsiBuilder builder) {
+ builder.advanceLexer();
+ }
+
+ private void parseScenarios(PsiBuilder builder) {
+ if (builder.getTokenType() == StoryTokenType.SCENARIO_TYPE) {
+ while (builder.getTokenType() == StoryTokenType.SCENARIO_TYPE) {
+ parseScenario(builder);
+ skipWhitespacesOrComments(builder);
+ }
+ }
+ else {
+ builder.advanceLexer();
+ builder.error("Scenario expected");
+ }
+ }
+
+ private void parseScenario(PsiBuilder builder) {
+ final PsiBuilder.Marker stepMarker = builder.mark();
+ builder.advanceLexer();
+ skipWhitespacesOrComments(builder);
+ while (builder.getTokenType() == StoryTokenType.SCENARIO_TEXT) {
+ parseScenarioText(builder);
+ skipWhitespacesOrComments(builder);
+ }
+ parseMeta(builder);
+ parseSteps(builder);
+ skipWhitespacesOrComments(builder);
+ parseStoryDescriptionOrNarrativesLinesIfPresent(builder);
+ skipWhitespacesOrComments(builder);
+ parseExamples(builder);
+ stepMarker.done(StoryElementType.SCENARIO);
+ }
+
+ private void parseScenarioText(PsiBuilder builder) {
+ builder.advanceLexer();
+ }
+
+ private void parseMeta(PsiBuilder builder) {
+ if (builder.getTokenType() == StoryTokenType.META) {
+ final PsiBuilder.Marker stepMarker = builder.mark();
+ while (builder.getTokenType() == StoryTokenType.META
+ || builder.getTokenType() == StoryTokenType.META_TEXT
+ || builder.getTokenType() == StoryTokenType.META_KEY)
+ {
+ builder.advanceLexer();
+ skipWhitespacesOrComments(builder);
+ }
+ stepMarker.done(StoryElementType.META);
+ }
+ }
+
+ private void parseSteps(PsiBuilder builder) {
+ parseStoryDescriptionOrNarrativesLinesIfPresent(builder);
+ if (isStepType(builder.getTokenType())) {
+ StoryElementType previousStepElementType = null;
+ while (isStepType(builder.getTokenType())) {
+ previousStepElementType = parseStep(builder, previousStepElementType);
+ skipWhitespacesOrComments(builder);
+ parseStoryDescriptionOrNarrativesLinesIfPresent(builder);
+ }
+ }
+ else {
+ builder.error("At least one step expected");
+ }
+ }
+
+
+ private StoryElementType parseStep(PsiBuilder builder, StoryElementType previousStepElementType) {
+ final PsiBuilder.Marker stepMarker = builder.mark();
+ StoryElementType currentStepElementType;
+
+ // TODO find a nicer way to perform the switch
+ IElementType tokenType = builder.getTokenType();
+ if (tokenType == StoryTokenType.STEP_TYPE_GIVEN) {
+ currentStepElementType = StoryElementType.GIVEN_STEP;
+ }
+ else if (tokenType == StoryTokenType.STEP_TYPE_WHEN) {
+ currentStepElementType = StoryElementType.WHEN_STEP;
+ }
+ else if (tokenType == StoryTokenType.STEP_TYPE_THEN) {
+ currentStepElementType = StoryElementType.THEN_STEP;
+ }
+ else {
+ // should be the AND
+ if (tokenType != StoryTokenType.STEP_TYPE_AND) {
+ // throw...
+ }
+ currentStepElementType = previousStepElementType;
+ }
+
+ if (currentStepElementType == null) {
+ // yuk!
+ currentStepElementType = StoryElementType.GIVEN_STEP;
+ }
+
+ parseStepType(builder);
+ parseStepText(builder);
+ skipWhitespacesOrComments(builder);
+ parseTableIfPresent(builder);
+ skipWhitespacesOrComments(builder);
+ stepMarker.done(currentStepElementType);
+
+ return currentStepElementType;
+ }
+
+ private void parseStepType(PsiBuilder builder) {
+ builder.advanceLexer();
+ }
+
+ private void parseStepText(PsiBuilder builder) {
+ if (builder.getTokenType() == StoryTokenType.STEP_TEXT) {
+ while (builder.getTokenType() == StoryTokenType.STEP_TEXT) {
+ builder.advanceLexer();
+ skipWhitespacesOrComments(builder);
+ }
+ }
+ else {
+ builder.error("Step text expected");
+ }
+ }
+
+ private void parseExamples(PsiBuilder builder) {
+ if (builder.getTokenType() == StoryTokenType.EXAMPLE_TYPE) {
+ final PsiBuilder.Marker stepMarker = builder.mark();
+ builder.advanceLexer();
+ skipWhitespacesOrComments(builder);
+ if (builder.getTokenType() == StoryTokenType.TABLE_DELIM) {
+ parseTableIfPresent(builder);
+ }
+ else {
+ builder.error("Table row expected");
+ }
+
+ stepMarker.done(StoryElementType.EXAMPLES);
+ }
+ }
+
+ private void parseTableIfPresent(PsiBuilder builder) {
+ if (builder.getTokenType() == StoryTokenType.TABLE_DELIM) {
+ while (builder.getTokenType() == StoryTokenType.TABLE_DELIM
+ || builder.getTokenType() == StoryTokenType.TABLE_CELL)
+ {
+ parseTableToken(builder);
+ skipWhitespacesOrComments(builder);
+ }
+ }
+ }
+
+ private void parseTableToken(PsiBuilder builder) {
+ builder.advanceLexer();
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/parser/StoryParserDefinition.java b/src/com/github/kumaraman21/intellijbehave/parser/StoryParserDefinition.java
index 2227a14..68d89f2 100644
--- a/src/com/github/kumaraman21/intellijbehave/parser/StoryParserDefinition.java
+++ b/src/com/github/kumaraman21/intellijbehave/parser/StoryParserDefinition.java
@@ -15,7 +15,7 @@
*/
package com.github.kumaraman21.intellijbehave.parser;
-import com.github.kumaraman21.intellijbehave.highlighter.StoryLexer;
+import com.github.kumaraman21.intellijbehave.highlighter.StoryLexerFactory;
import com.github.kumaraman21.intellijbehave.highlighter.StoryTokenType;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
@@ -29,15 +29,15 @@
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
-import org.jetbrains.annotations.NotNull;
-import static com.github.kumaraman21.intellijbehave.utility.StepTypeMappings.STORY_ELEMENT_TYPE_TO_STEP_TYPE_MAPPING;
+import org.jbehave.core.steps.StepType;
+import org.jetbrains.annotations.NotNull;
public class StoryParserDefinition implements ParserDefinition {
@NotNull
@Override
public Lexer createLexer(Project project) {
- return new StoryLexer();
+ return new StoryLexerFactory().createLexer();
}
@Override
@@ -59,7 +59,7 @@ public TokenSet getWhitespaceTokens() {
@NotNull
@Override
public TokenSet getCommentTokens() {
- return TokenSet.create(StoryTokenType.COMMENT);
+ return TokenSet.create(StoryTokenType.COMMENT, StoryTokenType.COMMENT_WITH_LOCALE);
}
@NotNull
@@ -72,8 +72,14 @@ public TokenSet getStringLiteralElements() {
@Override
public PsiElement createElement(ASTNode node) {
final IElementType type = node.getElementType();
- if (type == StoryElementType.GIVEN_STEP || type == StoryElementType.WHEN_STEP || type == StoryElementType.THEN_STEP) {
- return new StepPsiElement(node, STORY_ELEMENT_TYPE_TO_STEP_TYPE_MAPPING.get(type));
+ if (type == StoryElementType.GIVEN_STEP) {
+ return new StepPsiElement(node, StepType.GIVEN);
+ }
+ else if (type == StoryElementType.WHEN_STEP) {
+ return new StepPsiElement(node, StepType.WHEN);
+ }
+ else if (type == StoryElementType.THEN_STEP) {
+ return new StepPsiElement(node, StepType.THEN);
}
return new ASTWrapperPsiElement(node);
diff --git a/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java b/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java
index 635fd4e..b85e53c 100644
--- a/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java
+++ b/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java
@@ -19,9 +19,9 @@
import org.jbehave.core.steps.StepType;
public class StepDefinitionAnnotation {
- private StepType stepType;
- private String annotationText;
- private PsiAnnotation annotation;
+ private final StepType stepType;
+ private final String annotationText;
+ private final PsiAnnotation annotation;
public StepDefinitionAnnotation(StepType stepType, String annotationText, PsiAnnotation annotation) {
this.stepType = stepType;
diff --git a/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java b/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java
index d39f3ac..ceff21d 100644
--- a/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java
+++ b/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java
@@ -20,8 +20,10 @@
import com.intellij.psi.PsiLiteral;
import org.jbehave.core.annotations.Alias;
import org.jbehave.core.annotations.Aliases;
+import org.jbehave.core.steps.PatternVariantBuilder;
import org.jbehave.core.steps.StepType;
+import java.util.List;
import java.util.Set;
import static com.github.kumaraman21.intellijbehave.utility.StepTypeMappings.ANNOTATION_TO_STEP_TYPE_MAPPING;
@@ -37,27 +39,32 @@ public Set convertFrom(PsiAnnotation[] annotations) {
for (PsiAnnotation annotation : annotations) {
// Given, When, Then
- if(ANNOTATION_TO_STEP_TYPE_MAPPING.keySet().contains(annotation.getQualifiedName())) {
+ if (ANNOTATION_TO_STEP_TYPE_MAPPING.keySet().contains(annotation.getQualifiedName())) {
stepType = ANNOTATION_TO_STEP_TYPE_MAPPING.get(annotation.getQualifiedName());
String annotationText = getTextFromValue(annotation.getParameterList().getAttributes()[0].getValue());
- stepDefinitionAnnotations.add(new StepDefinitionAnnotation(stepType, annotationText, annotation));
- }
- else if(annotation.getQualifiedName().equals(Alias.class.getName())) {
+ stepDefinitionAnnotations.addAll(getPatternVariants(stepType, annotationText, annotation));
+ } else if (annotation.getQualifiedName().equals(Alias.class.getName())) {
String annotationText = getTextFromValue(annotation.getParameterList().getAttributes()[0].getValue());
- stepDefinitionAnnotations.add(new StepDefinitionAnnotation(stepType, annotationText, annotation));
- }
- else if(annotation.getQualifiedName().equals(Aliases.class.getName())) {
-
+ stepDefinitionAnnotations.addAll(getPatternVariants(stepType, annotationText, annotation));
+ } else if (annotation.getQualifiedName().equals(Aliases.class.getName())) {
PsiElement[] values = annotation.getParameterList().getAttributes()[0].getValue().getChildren();
for (PsiElement value : values) {
- if(value instanceof PsiLiteral) {
+ if (value instanceof PsiLiteral) {
String annotationText = getTextFromValue(value);
- stepDefinitionAnnotations.add(new StepDefinitionAnnotation(stepType, annotationText, annotation));
+ stepDefinitionAnnotations.addAll(getPatternVariants(stepType, annotationText, annotation));
}
}
}
}
+ return stepDefinitionAnnotations;
+ }
+ private Set getPatternVariants(StepType stepType, String annotationText, PsiAnnotation annotation) {
+ Set stepDefinitionAnnotations = newHashSet();
+ PatternVariantBuilder b = new PatternVariantBuilder(annotationText);
+ for (String variant : b.allVariants()) {
+ stepDefinitionAnnotations.add(new StepDefinitionAnnotation(stepType, variant, annotation));
+ }
return stepDefinitionAnnotations;
}
diff --git a/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java b/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java
index 561c700..44f2b34 100644
--- a/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java
+++ b/src/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java
@@ -19,6 +19,7 @@
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import org.jbehave.core.steps.StepType;
+import org.jetbrains.annotations.Nullable;
import java.util.Set;
@@ -27,9 +28,9 @@
public abstract class StepDefinitionIterator implements ContentIterator {
private final StepDefinitionAnnotationConverter stepDefinitionAnnotationConverter = new StepDefinitionAnnotationConverter();
- private StepType stepType;
+ private final StepType stepType;
- public StepDefinitionIterator(StepType stepType) {
+ public StepDefinitionIterator(@Nullable StepType stepType) {
this.stepType = stepType;
}
@@ -48,10 +49,10 @@ public boolean processFile(VirtualFile virtualFile) {
Set stepDefinitionAnnotations = stepDefinitionAnnotationConverter.convertFrom(annotations);
for (StepDefinitionAnnotation stepDefinitionAnnotation : stepDefinitionAnnotations) {
- if(stepDefinitionAnnotation.getStepType().equals(stepType)) {
+ if(stepType==null || stepDefinitionAnnotation.getStepType().equals(stepType)) {
boolean shouldContinue = processStepDefinition(stepDefinitionAnnotation);
- if(shouldContinue == false) {
+ if(!shouldContinue) {
return shouldContinue;
}
}
diff --git a/src/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java b/src/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java
index c7cc8f8..f4beb66 100644
--- a/src/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java
+++ b/src/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java
@@ -15,139 +15,155 @@
*/
package com.github.kumaraman21.intellijbehave.resolver;
+import static com.github.kumaraman21.intellijbehave.utility.ProjectUtils.getProjectFileIndex;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.commons.lang.StringUtils.trim;
+
import com.github.kumaraman21.intellijbehave.parser.StepPsiElement;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.util.IncorrectOperationException;
+
import org.jbehave.core.parsers.RegexPrefixCapturingPatternParser;
import org.jbehave.core.parsers.StepMatcher;
import org.jbehave.core.parsers.StepPatternParser;
import org.jbehave.core.steps.StepType;
import org.jetbrains.annotations.NotNull;
-
import java.util.List;
-import static com.github.kumaraman21.intellijbehave.utility.ProjectUtils.getProjectFileIndex;
-import static com.google.common.collect.Lists.newArrayList;
-import static org.apache.commons.lang.StringUtils.trim;
-
public class StepPsiReference implements PsiReference {
- private StepPsiElement stepPsiElement;
+ private StepPsiElement stepPsiElement;
- public StepPsiReference(StepPsiElement stepPsiElement) {
- this.stepPsiElement = stepPsiElement;
- }
+ public StepPsiReference(StepPsiElement stepPsiElement) {
+ this.stepPsiElement = stepPsiElement;
+ }
- @Override
- public PsiElement getElement() {
- return stepPsiElement;
- }
+ @Override
+ public PsiElement getElement() {
+ return stepPsiElement;
+ }
- @Override
- public TextRange getRangeInElement() {
- return TextRange.from(0, stepPsiElement.getTextLength());
- }
+ @Override
+ public TextRange getRangeInElement() {
+ return TextRange.from(0, stepPsiElement.getTextLength());
+ }
- @Override
- public PsiElement resolve() {
- StepType stepType = stepPsiElement.getStepType();
- String stepText = stepPsiElement.getStepText();
+ public StepDefinitionAnnotation stepDefinitionAnnotation() {
+ StepType stepType = stepPsiElement.getStepType();
+ String stepText = stepPsiElement.getStepText();
- StepAnnotationFinder stepAnnotationFinder = new StepAnnotationFinder(stepType, stepText);
- getProjectFileIndex().iterateContent(stepAnnotationFinder);
+ StepAnnotationFinder stepAnnotationFinder = new StepAnnotationFinder(stepType, stepText);
+ getProjectFileIndex().iterateContent(stepAnnotationFinder);
- return stepAnnotationFinder.getMatchingAnnotation();
- }
+ return stepAnnotationFinder.getMatchingAnnotation();
+ }
- @NotNull
- @Override
- public Object[] getVariants() {
- StepType stepType = stepPsiElement.getStepType();
- String actualStepPrefix = stepPsiElement.getActualStepPrefix();
+ @Override
+ public PsiElement resolve() {
+ StepDefinitionAnnotation stepDefinitionAnnotation = stepDefinitionAnnotation();
+ if(stepDefinitionAnnotation==null)
+ return null;
+ return stepDefinitionAnnotation.getAnnotation();
+ }
- StepSuggester stepSuggester = new StepSuggester(stepType, actualStepPrefix);
- getProjectFileIndex().iterateContent(stepSuggester);
+ private static final boolean useVariants = false;
- return stepSuggester.getSuggestions().toArray();
- }
+ @NotNull
+ @Override
+ public Object[] getVariants() {
- private static class StepSuggester extends StepDefinitionIterator {
+ if (useVariants) {
+ StepType stepType = stepPsiElement.getStepType();
+ String actualStepPrefix = stepPsiElement.getActualStepPrefix();
- private List suggestions = newArrayList();
- private String actualStepPrefix;
+ StepSuggester stepSuggester = new StepSuggester(stepType, actualStepPrefix);
+ getProjectFileIndex().iterateContent(stepSuggester);
- public StepSuggester(StepType stepType, String actualStepPrefix) {
- super(stepType);
- this.actualStepPrefix = actualStepPrefix;
+ return stepSuggester.getSuggestions().toArray();
+ }
+ else {
+ return new Object[0];
+ }
}
- @Override
- public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) {
- suggestions.add(actualStepPrefix + " " + stepDefinitionAnnotation.getAnnotationText());
- return true;
- }
+ private static class StepSuggester extends StepDefinitionIterator {
+
+ private final List suggestions = newArrayList();
+ private final String actualStepPrefix;
+
+ public StepSuggester(StepType stepType, String actualStepPrefix) {
+ super(stepType);
+ this.actualStepPrefix = actualStepPrefix;
+ }
- public List getSuggestions() {
- return suggestions;
+ @Override
+ public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) {
+ suggestions.add(actualStepPrefix + " " + stepDefinitionAnnotation.getAnnotationText());
+ return true;
+ }
+
+ public List getSuggestions() {
+ return suggestions;
+ }
}
- }
-private static class StepAnnotationFinder extends StepDefinitionIterator {
+ private static class StepAnnotationFinder extends StepDefinitionIterator {
+
+ private StepType stepType;
+ private String stepText;
+ private StepDefinitionAnnotation matchingAnnotation;
+ private StepPatternParser stepPatternParser = new RegexPrefixCapturingPatternParser();
- private StepType stepType;
- private String stepText;
- private PsiElement matchingAnnotation;
- private StepPatternParser stepPatternParser = new RegexPrefixCapturingPatternParser();
+ private StepAnnotationFinder(StepType stepType, String stepText) {
+ super(stepType);
+ this.stepType = stepType;
+ this.stepText = stepText;
+ }
- private StepAnnotationFinder(StepType stepType, String stepText) {
- super(stepType);
- this.stepType = stepType;
- this.stepText = stepText;
- }
+ @Override
+ public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) {
+ StepMatcher stepMatcher = stepPatternParser.parseStep(stepType, stepDefinitionAnnotation.getAnnotationText());
- @Override
- public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) {
- StepMatcher stepMatcher = stepPatternParser.parseStep(stepType, stepDefinitionAnnotation.getAnnotationText());
+ if (stepMatcher.matches(stepText)) {
+ matchingAnnotation = stepDefinitionAnnotation;
- if(stepMatcher.matches(stepText)) {
- matchingAnnotation = stepDefinitionAnnotation.getAnnotation();
+ return false;
+ }
+ return true;
+ }
- return false;
+ public StepDefinitionAnnotation getMatchingAnnotation() {
+ return matchingAnnotation;
+ }
}
- return true;
- }
- public PsiElement getMatchingAnnotation() {
- return matchingAnnotation;
- }
-}
+ @NotNull
+ @Override
+ public String getCanonicalText() {
+ return trim(stepPsiElement.getText());
+ }
- @NotNull
- @Override
- public String getCanonicalText() {
- return trim(stepPsiElement.getText());
- }
-
- @Override
- public PsiElement handleElementRename(String s) throws IncorrectOperationException {
- throw new IncorrectOperationException();
- }
-
- @Override
- public PsiElement bindToElement(@NotNull PsiElement psiElement) throws IncorrectOperationException {
- throw new IncorrectOperationException();
- }
-
- @Override
- public boolean isReferenceTo(PsiElement psiElement) {
- return psiElement instanceof StepPsiElement && Comparing.equal(resolve(), psiElement);
- }
-
- @Override
- public boolean isSoft() {
- return false;
- }
+ @Override
+ public PsiElement handleElementRename(String s) throws IncorrectOperationException {
+ throw new IncorrectOperationException();
+ }
+
+ @Override
+ public PsiElement bindToElement(@NotNull PsiElement psiElement) throws IncorrectOperationException {
+ throw new IncorrectOperationException();
+ }
+
+ @Override
+ public boolean isReferenceTo(PsiElement psiElement) {
+ return psiElement instanceof StepPsiElement && Comparing.equal(resolve(), psiElement);
+ }
+
+ @Override
+ public boolean isSoft() {
+ return false;
+ }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java b/src/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java
index eb35c6f..b0c7c5b 100644
--- a/src/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java
+++ b/src/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java
@@ -15,22 +15,54 @@
*/
package com.github.kumaraman21.intellijbehave.resolver;
+import static com.github.kumaraman21.intellijbehave.utility.ParametrizedString.StringToken;
+
import com.github.kumaraman21.intellijbehave.parser.StepPsiElement;
+import com.github.kumaraman21.intellijbehave.utility.ParametrizedString;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ex.ProblemDescriptorImpl;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
+
import org.jetbrains.annotations.NotNull;
public class StoryAnnotator implements Annotator {
- @Override
- public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder annotationHolder) {
- if(! (psiElement instanceof StepPsiElement)) {
- return;
+ @Override
+ public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder annotationHolder) {
+ if (!(psiElement instanceof StepPsiElement)) {
+ return;
+ }
+
+ StepPsiElement stepPsiElement = (StepPsiElement) psiElement;
+ StepDefinitionAnnotation annotationDef = stepPsiElement.getReference().stepDefinitionAnnotation();
+ if (annotationDef == null) {
+ annotationHolder.createErrorAnnotation(psiElement, "No definition found for the step");
+ }
+ else {
+ annotateParameters(stepPsiElement, annotationDef, annotationHolder);
+ }
}
- StepPsiElement stepPsiElement = (StepPsiElement) psiElement;
- if(stepPsiElement.getReference().resolve() == null) {
- annotationHolder.createErrorAnnotation(psiElement, "No definition found for the step");
+ private void annotateParameters(StepPsiElement stepPsiElement,
+ StepDefinitionAnnotation annotation,
+ AnnotationHolder annotationHolder)
+ {
+ String stepText = stepPsiElement.getStepText();
+ String annotationText = annotation.getAnnotationText();
+ ParametrizedString pString = new ParametrizedString(annotationText);
+
+ int offset = stepPsiElement.getTextOffset();
+ for (StringToken token : pString.tokenize(stepText)) {
+ int length = token.getValue().length();
+ if (token.isIdentifier()) {
+ annotationHolder.createInfoAnnotation(
+ TextRange.from(offset, length), "Parameter");
+ }
+ offset += length;
+ }
}
- }
}
diff --git a/src/com/github/kumaraman21/intellijbehave/template/JBehaveTemplateLoaderComponent.java b/src/com/github/kumaraman21/intellijbehave/template/JBehaveTemplateLoaderComponent.java
index f7639a5..1d734ab 100644
--- a/src/com/github/kumaraman21/intellijbehave/template/JBehaveTemplateLoaderComponent.java
+++ b/src/com/github/kumaraman21/intellijbehave/template/JBehaveTemplateLoaderComponent.java
@@ -21,6 +21,7 @@
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import static com.github.kumaraman21.intellijbehave.language.StoryFileType.STORY_FILE_TYPE;
@@ -33,13 +34,24 @@ public void initComponent() {
if (template == null) {
template = FileTemplateManager.getInstance()
.addTemplate(STORY_FILE_TYPE.getName(), STORY_FILE_TYPE.getDefaultExtension());
+
+ InputStream stream = getClass().getResourceAsStream("/fileTemplates/JBehave Story.story.ft");
try {
- template.setText(
- loadTextAndClose(new InputStreamReader(getClass().getResourceAsStream("/fileTemplates/JBehave Story.story.ft"))));
+ if(stream!=null)
+ template.setText(loadTextAndClose(new InputStreamReader(stream)));
}
catch (IOException e) {
e.printStackTrace();
}
+ finally {
+ try {
+ if(stream!=null)
+ stream.close();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
}
diff --git a/src/com/github/kumaraman21/intellijbehave/utility/CharTree.java b/src/com/github/kumaraman21/intellijbehave/utility/CharTree.java
new file mode 100644
index 0000000..b5e7d28
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/utility/CharTree.java
@@ -0,0 +1,152 @@
+package com.github.kumaraman21.intellijbehave.utility;
+
+import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author @aloyer
+ */
+public class CharTree {
+ private Map> children = new HashMap>();
+ private final int key;
+ private T value;
+
+ public CharTree(int key) {
+ this(key, null);
+ }
+
+ public CharTree(int key, @Nullable T value) {
+ super();
+ this.key = key;
+ this.value = value;
+ }
+
+ public T lookupValue(CharSequence seq) {
+ return lookupValue(seq, 0);
+ }
+
+ public T lookupValue(CharSequence seq, int offset) {
+ return lookup(seq, offset).value;
+ }
+
+ public Entry lookup(CharSequence seq, int offset) {
+ CharTree ct = this;
+ T found = this.value;
+
+ int i = offset;
+ for (int n = seq.length(); i < n; i++) {
+ ct = ct.get(seq.charAt(i));
+ if (ct == null) {
+ break;
+ }
+ if (ct.value != null) {
+ found = ct.value;
+ }
+ }
+ return new Entry(found, i - offset);
+ }
+
+ public void print(int level) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < level; i++) {
+ builder.append(" | ");
+ }
+ System.out.print(builder.toString());
+ System.out.print("[");
+ System.out.print((char) key);
+ System.out.print("] ");
+ System.out.println(value == null ? "n/a" : value);
+
+ List keys = new ArrayList(children.keySet());
+ Collections.sort(keys);
+ for (Integer key : keys) {
+ children.get(key).print(level + 1);
+ }
+ }
+
+ public void push(CharSequence seq, T value) {
+ push(seq, 0, value);
+ }
+
+ private void push(CharSequence seq, int pos, T value) {
+ if (pos < seq.length()) {
+ int c = seq.charAt(pos);
+ CharTree child = getOrCreate(c);
+ child.push(seq, pos + 1, value);
+ }
+ else {
+ this.value = value;
+ }
+ }
+
+ private CharTree get(int c) {
+ return children.get(c);
+ }
+
+ private CharTree getOrCreate(int c) {
+ CharTree cn = children.get(c);
+ if (cn == null) {
+ cn = new CharTree(c);
+ children.put(c, cn);
+ }
+ return cn;
+ }
+
+ public boolean isLeaf() {
+ return children.isEmpty();
+ }
+
+ public static class Entry {
+ public final T value;
+ public final int length;
+
+ public Entry(T value, int length) {
+ this.value = value;
+ this.length = length;
+ }
+
+ public boolean hasValue() {
+ return value != null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (length != entry.length) {
+ return false;
+ }
+ if (value != null ? !value.equals(entry.value) : entry.value != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = value != null ? value.hashCode() : 0;
+ result = 31 * result + length;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "value=" + value +
+ ", length=" + length +
+ '}';
+ }
+ }
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/utility/JBKeyword.java b/src/com/github/kumaraman21/intellijbehave/utility/JBKeyword.java
new file mode 100644
index 0000000..e27eb91
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/utility/JBKeyword.java
@@ -0,0 +1,244 @@
+package com.github.kumaraman21.intellijbehave.utility;
+
+import org.jbehave.core.configuration.Keywords;
+import org.jbehave.core.steps.StepType;
+
+/**
+ * @author @aloyer
+ */
+
+public enum JBKeyword {
+ Meta
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.meta();
+ }
+ },
+ MetaProperty
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.metaProperty();
+ }
+ },
+ Narrative
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.narrative();
+ }
+ },
+ InOrderTo
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.inOrderTo();
+ }
+ },
+ AsA
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.asA();
+ }
+ },
+ IWantTo
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.iWantTo();
+ }
+ },
+ Scenario
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.scenario();
+ }
+ },
+ GivenStories
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.givenStories();
+ }
+ },
+ ExamplesTable
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.examplesTable();
+ }
+ },
+ ExamplesTableRow
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.examplesTableRow();
+ }
+ },
+ ExamplesTableHeaderSeparator
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.examplesTableHeaderSeparator();
+ }
+ },
+ ExamplesTableValueSeparator
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.examplesTableValueSeparator();
+ }
+ },
+ ExamplesTableIgnorableSeparator
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.examplesTableIgnorableSeparator();
+ }
+ },
+ Given
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.given();
+ }
+ },
+ When
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.when();
+ }
+ },
+ Then
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.then();
+ }
+ },
+ And
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.and();
+ }
+ },
+ Ignorable
+ {
+ @Override
+ public String asString(Keywords keywords) {
+ return keywords.ignorable();
+ }
+ };
+
+ private static Keywords keywords = new Keywords();
+
+ public String asString() {
+ return asString(keywords);
+ }
+
+ public String asString(Keywords keywords) {
+ throw new AbstractMethodError();
+ }
+
+ public boolean isStep() {
+ return isStep(this);
+ }
+
+ public boolean isNarrative() {
+ return isNarrative(this);
+ }
+
+ public boolean isExampleTable() {
+ return isExampleTable(this);
+ }
+
+ public boolean isComment() {
+ return this == Ignorable;
+ }
+
+ public static JBKeyword lookup(StringBuilder builder, Keywords keywords) {
+ int len = builder.length();
+ for (JBKeyword jk : values()) {
+ String asString = jk.asString(keywords);
+ if (asString.length() != builder.length()) {
+ continue;
+ }
+
+ boolean match = true;
+ for (int i = 0; i < len && match; i++) {
+ if (builder.charAt(i) != asString.charAt(i)) {
+ match = false;
+ }
+ }
+ if (match) {
+ return jk;
+ }
+ }
+ return null;
+ }
+
+ public static boolean isStep(JBKeyword keyword) {
+ if (keyword == null) {
+ return false;
+ }
+ switch (keyword) {
+ case Given:
+ case When:
+ case Then:
+ case And:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public boolean isSameAs(StepType type) {
+ switch (type) {
+ case GIVEN:
+ return this == Given;
+ case WHEN:
+ return this == When;
+ case THEN:
+ return this == Then;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isNarrative(JBKeyword keyword) {
+ if (keyword == null) {
+ return false;
+ }
+ switch (keyword) {
+ case Narrative:
+ case InOrderTo:
+ case AsA:
+ case IWantTo:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isExampleTable(JBKeyword keyword) {
+ if (keyword == null) {
+ return false;
+ }
+ switch (keyword) {
+ case ExamplesTable:
+ case ExamplesTableHeaderSeparator:
+ case ExamplesTableIgnorableSeparator:
+ case ExamplesTableRow:
+ case ExamplesTableValueSeparator:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupport.java b/src/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupport.java
new file mode 100644
index 0000000..b2fb1bd
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupport.java
@@ -0,0 +1,53 @@
+package com.github.kumaraman21.intellijbehave.utility;
+
+import org.apache.commons.lang.LocaleUtils;
+import org.jbehave.core.i18n.LocalizedKeywords;
+import org.jetbrains.annotations.NotNull;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author @aloyer
+ */
+public class LocalizedStorySupport {
+
+ private static final Pattern LANGUAGE_PATTERN = Pattern.compile("language:[ ]*([a-z]{2}(_[A-Z]{2}(_[^ ]+)?)?)");
+
+ /**
+ * Returns the language directive if it exists.
+ *
+ * The methods checks if the {@link CharSequence} contains a language directive,
+ * if so it returns the corresponding locale as a {@link String} otherwise returns
+ * null
.
+ *
+ * @param charSeq the sequence where the language directive is searched
+ * @return the defined locale or null
+ */
+ public static String checkForLanguageDefinition(CharSequence charSeq) {
+ Matcher matcher = LANGUAGE_PATTERN.matcher(charSeq);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link LocalizedKeywords} that corresponds to the given locale. If
+ * the locale provided in invalid, {@link Locale#ENGLISH} is used.
+ *
+ * @param localeAsString the locale for which the {@link LocalizedKeywords} must
+ * be returned
+ */
+ @NotNull
+ public LocalizedKeywords getKeywords(String localeAsString) {
+ Locale locale;
+ try {
+ locale = LocaleUtils.toLocale(localeAsString);
+ }
+ catch (Exception e) {
+ locale = Locale.ENGLISH;
+ }
+ return new LocalizedKeywords(locale);
+ }
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/utility/ParametrizedString.java b/src/com/github/kumaraman21/intellijbehave/utility/ParametrizedString.java
new file mode 100644
index 0000000..715b621
--- /dev/null
+++ b/src/com/github/kumaraman21/intellijbehave/utility/ParametrizedString.java
@@ -0,0 +1,416 @@
+package com.github.kumaraman21.intellijbehave.utility;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author @aloyer
+ */
+public class ParametrizedString {
+
+ private static Pattern compileParameterPattern(String parameterPrefix) {
+ return Pattern.compile("(\\" + parameterPrefix + "\\w*)(\\W|\\Z)", Pattern.DOTALL);
+ }
+
+ private List tokens = new ArrayList();
+ private final String content;
+ private final String parameterPrefix;
+ private int parameterCount = -1; // lazily calculated
+
+
+ public ParametrizedString(String content) {
+ this(content, "$");
+ }
+
+ public ParametrizedString(String content, String parameterPrefix) {
+ if (content == null) {
+ throw new IllegalArgumentException("Content cannot be null");
+ }
+ this.content = content;
+ this.parameterPrefix = parameterPrefix;
+ parse(compileParameterPattern(parameterPrefix));
+ }
+
+ @Override
+ public int hashCode() {
+ return content.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof ParametrizedString) && isSameAs((ParametrizedString) obj);
+ }
+
+ public boolean isSameAs(ParametrizedString other) {
+ return other.content.equals(content);
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ private void parse(Pattern parameterPattern) {
+ Matcher matcher = parameterPattern.matcher(content);
+
+ int prev = 0;
+ while (matcher.find()) {
+ int start = matcher.start();
+ int end = matcher.end();
+ if (start > prev) {
+ add(new Token(prev, start - prev, false));
+ }
+ end -= matcher.group(2).length();
+ start += parameterPrefix.length(); // remove prefix from the identifier
+ add(new Token(start, end - start, true));
+ prev = end;
+ }
+ if (prev < content.length()) {
+ add(new Token(prev, content.length() - prev, false));
+ }
+ }
+
+ private void add(Token token) {
+ tokens.add(token);
+ }
+
+ public class Token {
+ private final int offset;
+ private final int length;
+ private final boolean isIdentifier;
+
+ public Token(int offset, int length, boolean isIdentifier) {
+ this.offset = offset;
+ this.length = length;
+ this.isIdentifier = isIdentifier;
+ }
+
+ public String value() {
+ return content.substring(getOffset(), getOffset() + getLength());
+ }
+
+ @Override
+ public String toString() {
+ return "<<" + (isIdentifier() ? "$" : "") + value() + ">>";
+ }
+
+ public boolean regionMatches(int toffset, String other, int ooffset, int len) {
+ return content.regionMatches(getOffset() + toffset, other, ooffset, len);
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public boolean isIdentifier() {
+ return isIdentifier;
+ }
+ }
+
+ public Token getToken(int index) {
+ return tokens.get(index);
+ }
+
+ public List getTokens() {
+ return new ArrayList(tokens);
+ }
+
+ public List getParameters() {
+ List parameters = new ArrayList();
+ for (Token token : tokens) {
+ if (token.isIdentifier()) {
+ parameters.add(token.value());
+ }
+ }
+ return parameters;
+ }
+
+ public int getTokenCount() {
+ return tokens.size();
+ }
+
+ public int getParameterCount() {
+ if (parameterCount == -1) {
+ parameterCount = parameterCount();
+ }
+ return parameterCount;
+ }
+
+ private int parameterCount() {
+ int count = 0;
+ for(Token token : tokens) {
+ if(token.isIdentifier())
+ count++;
+ }
+ return count;
+ }
+
+ public float weightOf(String input) {
+ return ((float) acceptsBeginning(input)) / ((float) getTokenCount());
+ }
+
+ public int acceptsBeginning(String input) {
+ WeightChain chain = calculateWeightChain(input);
+ return chain.getWeight();
+ }
+
+ public WeightChain calculateWeightChain(String input) {
+ WeightChain chain = acceptsBeginning(0, input, 0);
+ chain.input = input;
+ chain.collectWeights();
+ return chain;
+ }
+
+ public static class StringToken {
+ private final String value;
+ private final boolean identifier;
+
+ public StringToken(String value, boolean identifier) {
+ this.value = value;
+ this.identifier = identifier;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public boolean isIdentifier() {
+ return identifier;
+ }
+ }
+
+ public List tokenize(String stepInput) {
+
+ List stringTokens = new ArrayList();
+
+ WeightChain chain = calculateWeightChain(stepInput);
+ List inputTokens = chain.tokenize();
+ while (chain != null) {
+ if (!chain.isZero()) {
+ Token token = tokens.get(chain.getTokenIndex());
+ String value = inputTokens.get(chain.getTokenIndex());
+ stringTokens.add(new StringToken(value, token.isIdentifier()));
+ }
+ chain = chain.getNext();
+ }
+ return stringTokens;
+ }
+
+ public Map extractParameterValues(String input) {
+ Map namedParameters = new HashMap();
+
+ WeightChain chain = calculateWeightChain(input);
+ List inputTokens = chain.tokenize();
+ while (chain != null) {
+ if (!chain.isZero()) {
+ Token token = tokens.get(chain.getTokenIndex());
+ if (token.isIdentifier()) {
+ String value = inputTokens.get(chain.getTokenIndex());
+ namedParameters.put(token.value(), value);
+ }
+ }
+
+ chain = chain.getNext();
+ }
+ return namedParameters;
+ }
+
+ private WeightChain acceptsBeginning(int inputIndex, String input, int tokenIndexStart) {
+ WeightChain pair = new WeightChain();
+ pair.inputIndex = inputIndex;
+
+ WeightChain current = pair;
+
+ List tokenList = this.tokens;
+ for (int tokenIndex = tokenIndexStart, n = tokenList.size(); tokenIndex < n; tokenIndex++) {
+ boolean isLastToken = (tokenIndex == n - 1);
+ Token token = tokenList.get(tokenIndex);
+ if (!token.isIdentifier()) {
+ int remaining = input.length() - inputIndex;
+ if (remaining > token.getLength() && isLastToken) {
+ // more data than the token itself
+ return WeightChain.zero();
+ }
+
+ int overlaping = Math.min(token.getLength(), remaining);
+ if (overlaping > 0) {
+ if (token.regionMatches(0, input, inputIndex, overlaping)) {
+ current.tokenIndex = tokenIndex;
+ current.weight++;
+ if (overlaping == token.getLength()) // full token match
+ {
+ current.weight++;
+ if ((inputIndex + overlaping) == input.length())
+ // no more data, break the loop now
+ {
+ return pair;
+ }
+ } // break looop
+ else {
+ return pair;
+ }
+
+ inputIndex += overlaping;
+ WeightChain next = new WeightChain();
+ next.inputIndex = inputIndex;
+ current.next = next;
+ current = next;
+ }
+ else {
+ // no match
+ return WeightChain.zero();
+ }
+ }
+ else {
+ // not enough data, returns what has been collected
+ return pair;
+ }
+ }
+ else {
+ current.tokenIndex = tokenIndex;
+ current.weight++;
+
+ // not the most efficient part, but no other solution right now
+ WeightChain next = WeightChain.zero();
+ for (int j = inputIndex + 1; j < input.length(); j++) {
+ WeightChain sub = acceptsBeginning(j, input, tokenIndex + 1);
+ if (sub.hasMoreWeightThan(next)) {
+ next = sub;
+ }
+ }
+ current.next = next;
+ return pair;
+ }
+ }
+ return pair;
+ }
+
+ public static class WeightChain {
+ public static WeightChain zero() {
+ return new WeightChain();
+ }
+
+ private String input;
+ private int inputIndex;
+ private int weight;
+ private int tokenIndex = -1;
+ private WeightChain next;
+
+ public WeightChain last() {
+ WeightChain last = this;
+ WeightChain iter = this;
+ while (iter != null) {
+ if (!iter.isZero()) {
+ last = iter;
+ }
+ iter = iter.next;
+ }
+ return last;
+ }
+
+ public boolean isZero() {
+ return weight == 0 && tokenIndex == -1;
+ }
+
+ public int getInputIndex() {
+ return inputIndex;
+ }
+
+ public WeightChain getNext() {
+ return next;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public int getTokenIndex() {
+ return tokenIndex;
+ }
+
+ public boolean hasMoreWeightThan(WeightChain pair) {
+ if (weight > pair.weight) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "WeightChain [inputIndex=" + inputIndex + ", weight=" + weight + ", tokenIndex=" + tokenIndex + "]";
+ }
+
+ public void collectWeights() {
+ int w = weight;
+ WeightChain n = next;
+ while (n != null) {
+ if (!n.isZero()) {
+ w += n.weight;
+ }
+ n = n.next;
+ }
+
+ this.weight = w;
+ }
+
+ public List tokenize() {
+ List parts = new ArrayList();
+ if (isZero()) {
+ return parts;
+ }
+
+ int indexBeg = inputIndex;
+ WeightChain n = next;
+ while (n != null) {
+ if (!n.isZero()) {
+ parts.add(input.substring(indexBeg, n.inputIndex));
+ indexBeg = n.inputIndex;
+ }
+ n = n.next;
+ }
+ parts.add(input.substring(indexBeg));
+
+ return parts;
+ }
+ }
+
+ public boolean matches(String input) {
+ return acceptsBeginning(input) == (2 * getTokenCount() - getParameterCount());
+ }
+
+ public String complete(String input) {
+ WeightChain chain = calculateWeightChain(input);
+ WeightChain last = chain.last();
+ if (last.isZero()) {
+ return "";
+ }
+ int inputIndex = last.inputIndex;
+ int tokenIndex = last.tokenIndex;
+
+ StringBuilder builder = new StringBuilder();
+
+ Token token = getToken(tokenIndex);
+ if (!token.isIdentifier()) {
+ int consumed = input.length() - inputIndex;
+ builder.append(getToken(tokenIndex).value().substring(consumed));
+ }
+ tokenIndex++;
+ for (int i = tokenIndex; i < getTokenCount(); i++) {
+ token = getToken(i);
+ if (token.isIdentifier()) {
+ builder.append(parameterPrefix);
+ }
+ builder.append(token.value());
+ }
+ return builder.toString();
+ }
+
+}
diff --git a/src/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java b/src/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java
index c3398a8..c57b237 100644
--- a/src/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java
+++ b/src/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java
@@ -40,17 +40,4 @@ public class StepTypeMappings {
ANNOTATION_TO_STEP_TYPE_MAPPING.put(Then.class.getName(), StepType.THEN);
}
- public static final Map STORY_ELEMENT_TYPE_TO_STEP_TYPE_MAPPING = new HashMap();
- static {
- STORY_ELEMENT_TYPE_TO_STEP_TYPE_MAPPING.put(StoryElementType.GIVEN_STEP, StepType.GIVEN);
- STORY_ELEMENT_TYPE_TO_STEP_TYPE_MAPPING.put(StoryElementType.WHEN_STEP, StepType.WHEN);
- STORY_ELEMENT_TYPE_TO_STEP_TYPE_MAPPING.put(StoryElementType.THEN_STEP, StepType.THEN);
- }
-
- public static final Map STEP_TEXT_TO_STORY_ELEMENT_TYPE_MAPPING = new HashMap();
- static {
- STEP_TEXT_TO_STORY_ELEMENT_TYPE_MAPPING.put(StepType.GIVEN.name(), StoryElementType.GIVEN_STEP );
- STEP_TEXT_TO_STORY_ELEMENT_TYPE_MAPPING.put(StepType.WHEN.name(), StoryElementType.WHEN_STEP);
- STEP_TEXT_TO_STORY_ELEMENT_TYPE_MAPPING.put(StepType.THEN.name(), StoryElementType.THEN_STEP);
- }
}
diff --git a/test/com/github/kumaraman21/intellijbehave/Samples.java b/test/com/github/kumaraman21/intellijbehave/Samples.java
new file mode 100644
index 0000000..3ef8fd4
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/Samples.java
@@ -0,0 +1,127 @@
+package com.github.kumaraman21.intellijbehave;
+
+/**
+ * @author @aloyer
+ */
+public class Samples {
+
+ public static final String SIMPLE_SAMPLE =
+ "Scenario: An unknown user cannot be logged\n" + //
+ "\n" + //
+ "Meta:\n" + //
+ "@skip\n" + //
+ "\n" + //
+ "Given i am the user with nickname: \"weird\"\n" + //
+ "When i try to login using the password \"soweird\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"\n";
+
+ public static final String SIMPLE_FR =
+ "!-- language:fr\n" +
+ "Scénario: une simple sortie\n" +
+ "Etant donné que nous allons promener notre chienne\n" +
+ "!-- un commentaire qui n'a rien à voir\n" +
+ "Quand on sera dehors\n" +
+ "Alors elle pourra se soulager!\n" +
+ "Et elle sera super contente\n";
+
+ public static final String LONG_SAMPLE =
+ "Narrative: \n" + //
+ "In order to play a game\n" + //
+ "As a player\n" + //
+ "I want to be able to create and manage my account\n" + //
+ "\n" + //
+ "Scenario: An unknown user cannot be logged\n" + //
+ "\n" + //
+ "Meta:\n" + //
+ "@skip\n" + //
+ "\n" + //
+ "Given i am the user with nickname: \"weird\"\n" + //
+ "When i try to login using the password \"soweird\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"\n" + //
+ "\n" + //
+ "\n" + //
+ "Scenario: A known user cannot be logged using a wrong password\n" + //
+ "\n" + //
+ "Given the following existing users:\n" + //
+ "| nickname | password |\n" + //
+ "| Travis | PacMan |\n" + //
+ "Given i am the user with nickname: \"Travis\"\n" + //
+ "When i try to login using the password \"McCallum\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"\n" + //
+ "\n" + //
+ "\n" + //
+ "Scenario: A known user can be logged using the right password\n" + //
+ "\n" + //
+ "Given the following existing users:\n" + //
+ "| nickname | password |\n" + //
+ "| Travis | PacMan |\n" + //
+ "Given i am the user with nickname: \"Travis\"\n" + //
+ "When i try to login using the password \"PacMan\"\n" + //
+ "Then i get logged\n" + //
+ "And a welcome message is displayed\n" + //
+ "\n";
+
+ public static final String META_SAMPLE =
+ "Scenario: An unknown user cannot be logged\n" + //
+ "\n" + //
+ "Meta:\n" + //
+ "@author carmen\n" + //
+ "@skip\n" + //
+ "\n" + //
+ "Given i am the user with nickname: \"weird\"\n";
+
+ public static final String EXAMPLES_SAMPLE =
+ "Scenario: An unknown user cannot be logged\n" + //
+ "\n" + //
+ "Given i am the user with nickname: \"\"\n" + //
+ "When i try to login using the password \"soweird\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"\n" + //
+ "\n" +//
+ "Examples: \n" + //
+ "| login | password |\n" + //
+ "| Travis | Pacman |\n" + //
+ "| Vlad | Thundercat |\n" + //
+ "\n" +//
+ "Scenario: A known user can be logged using the right password\n" + //
+ "\n" + //
+ "Given the following existing users:\n" + //
+ "| nickname | password |\n" + //
+ "| Travis | PacMan |\n" + //
+ "Given i am the user with nickname: \"Travis\"\n" //
+ ;
+
+ public static final String COMPLEX_SAMPLE =
+ "Narrative: \n" + //
+ "In order to play a game\n" + //
+ "As a player\n" + //
+ "I want to be able to create and manage my account\n" + //
+ "\n" + //
+ "Scenario: An unknown user cannot be logged\n" + //
+ "\n" + //
+ "Meta:\n" + //
+ "@author turtle\n" + //
+ "!-- This scenario should be skipped until fixed\n" + //
+ "@skip\n" + //
+ "\n" + //
+ "Given i am the user with nickname: \"\"\n" + //
+ "!-- some weird and hard coded password\n" + //
+ "When i try to login using the password \"soweird\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"\n" + //
+ "\n" +//
+ "Examples: \n" + //
+ "| login | password |\n" + //
+ "!-------------------------|\n" + //
+ "| Travis | Pacman |\n" + //
+ "| Vlad | Thundercat |\n" + //
+ "\n" + //
+ "\n" + //
+ "Scenario: A known user cannot be logged using a wrong password\n" + //
+ "\n" + //
+ "Given the following existing users:\n" + //
+ "| nickname | password |\n" + //
+ "| Travis | PacMan |\n" + //
+ "Given i am the user with nickname: \"Travis\"\n" + //
+ "When i try to login using the password \"McCallum\"\n" + //
+ "Then i get an error message of type \"Wrong Credentials\"\n" + //
+ "\n";
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLexerTest.java b/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLexerTest.java
new file mode 100644
index 0000000..33f9cb4
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLexerTest.java
@@ -0,0 +1,245 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+import static com.github.kumaraman21.intellijbehave.Samples.COMPLEX_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.LONG_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.EXAMPLES_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.META_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.SIMPLE_SAMPLE;
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import com.intellij.psi.tree.IElementType;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * @author @aloyer
+ */
+public class StoryLexerTest {
+
+ private StoryLexer storyLexer;
+
+ @Test
+ @Ignore
+ public void traceAll () {
+ //traceAll(SIMPLE_SAMPLE);
+ //traceAll(LONG_SAMPLE);
+ //traceAll(META_SAMPLE);
+ //traceAll(EXAMPLES_SAMPLE);
+ traceAll(COMPLEX_SAMPLE);
+ }
+
+ @Test
+ public void parseSimpleSample () {
+ storyLexer = new StoryLexer();
+ storyLexer.start(SIMPLE_SAMPLE);
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario: ");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, "An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META, "Meta:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@skip");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i am the user with nickname: \"weird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "When ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i try to login using the password \"soweird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Then ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ }
+
+ @Test
+ public void parseMetaSample () {
+ storyLexer = new StoryLexer();
+ storyLexer.start(META_SAMPLE);
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario: ");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, "An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META, "Meta:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@author");
+ advanceAndAssert(StoryTokenType.META_TEXT, " carmen");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@skip");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i am the user with nickname: \"weird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ }
+
+ @Test
+ public void parseLongSample() {
+ storyLexer = new StoryLexer();
+ storyLexer.start(LONG_SAMPLE);
+
+ assertToken(StoryTokenType.STORY_DESCRIPTION, "Narrative: ");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, "In order to play a game");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, "As a player");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, "I want to be able to create and manage my account");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scenario: ");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, "An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META, "Meta:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@skip");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i am the user with nickname: \"weird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "When ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i try to login using the password \"soweird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Then ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scenario: ");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, "A known user cannot be logged using a wrong password");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "the following existing users:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " nickname ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " password ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Travis ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " PacMan ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i am the user with nickname: \"Travis\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "When ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i try to login using the password \"McCallum\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Then ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+
+ // ...
+ }
+
+ @Test
+ public void parseExamples() {
+ storyLexer = new StoryLexer();
+ storyLexer.start(EXAMPLES_SAMPLE);
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario: ");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, "An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i am the user with nickname: \"\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "When ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i try to login using the password \"soweird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Then ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.EXAMPLE_TYPE, "Examples:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " login ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " password ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Travis ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Pacman ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Vlad ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Thundercat ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scenario: ");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, "A known user can be logged using the right password");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE, "Given ");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, "the following existing users:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+
+ // ...
+
+
+ }
+
+ private void advanceAndAssert(IElementType storyTokenType) {
+ storyLexer.advance();
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private void advanceAndAssert(IElementType storyTokenType, String content) {
+ storyLexer.advance();
+ assertToken(storyTokenType, content);
+ }
+
+ private void assertToken(IElementType storyTokenType, String content) {
+ assertThat(storyLexer.getTokenSequence()).isEqualTo(content);
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private void traceAll(String content) {
+ StoryLexer storyLexer = new StoryLexer();
+ storyLexer.start(content);
+
+ IElementType tokenType;
+ do {
+ tokenType = storyLexer.getTokenType();
+ System.out.println("[" +
+ rightPad(tokenType, "STORY_DESCRIPTION".length()) + "]" +
+ rightPad(storyLexer.lexerState(), "IN_DIRECTIVE".length()) +
+ ": >>" + escape(storyLexer.getTokenSequence()) + "<<");
+
+ storyLexer.advance();
+ tokenType = storyLexer.getTokenType();
+ }
+ while(tokenType!=null);
+ }
+
+ private String rightPad(Object object, int length) {
+ StringBuilder builder = new StringBuilder(object.toString());
+ while(builder.length()@aloyer
+ */
+public class StoryLocalizedLexer_FrenchTest {
+
+ private StoryLocalizedLexer storyLexer;
+
+ @Test
+ public void parse_basicScenario() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.changeLocale("fr");
+ storyLexer.start("Scénario: une simple sortie\n" +
+ "Etant donné que nous allons promener notre chienne\n" +
+ "Quand on sera dehors\n" +
+ "Alors elle pourra se soulager!");
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scénario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " une simple sortie");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Etant donné que");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " nous allons promener notre chienne");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "Quand");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " on sera dehors");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Alors");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " elle pourra se soulager!");
+ advanceAndAssert(null);
+ }
+
+ @Test
+ public void parse_commentAllowsToSwitchLanguage() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ // make sure one is not in fr by default
+ storyLexer.changeLocale("en");
+ storyLexer.start("!-- language:fr\n" +
+ "Scénario: une simple sortie\n" +
+ "Etant donné que nous allons promener notre chienne\n" +
+ "Quand on sera dehors\n" +
+ "Alors elle pourra se soulager!");
+
+ assertToken(StoryTokenType.COMMENT_WITH_LOCALE, "!-- language:fr");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scénario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " une simple sortie");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Etant donné que");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " nous allons promener notre chienne");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "Quand");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " on sera dehors");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Alors");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " elle pourra se soulager!");
+ advanceAndAssert(null);
+ }
+
+ private void advanceAndAssert(@Nullable IElementType storyTokenType) {
+ storyLexer.advance();
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private void advanceAndAssert(IElementType storyTokenType, String content) {
+ storyLexer.advance();
+ assertToken(storyTokenType, content);
+ }
+
+ private void assertToken(IElementType storyTokenType, String content) {
+ assertThat(storyLexer.getTokenSequence()).isEqualTo(content);
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer_MalformedTest.java b/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer_MalformedTest.java
new file mode 100644
index 0000000..24dc85f
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer_MalformedTest.java
@@ -0,0 +1,119 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import com.github.kumaraman21.intellijbehave.utility.LocalizedStorySupport;
+import com.intellij.psi.tree.IElementType;
+
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+/**
+ * @author @aloyer
+ */
+public class StoryLocalizedLexer_MalformedTest {
+
+ private StoryLocalizedLexer storyLexer;
+
+ @Test
+ public void parse_tableWithoutPreamble() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start("| Travis | Pacman |");
+
+ assertToken(StoryTokenType.TABLE_DELIM, "|");
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Travis ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM, "|");
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Pacman ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM, "|");
+ advanceAndAssert(null);
+ }
+
+ @Test
+ public void parse_scenarioWithoutText() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start("Scenario:\n" +
+ "Given\n" +
+ "When\n" +
+ "Then\n" +
+ "And\n");
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "When");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Then");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_AND, "And");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(null);
+ }
+
+ @Test
+ public void parse_scenarioWithStepAsText() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start("Scenario: Given a nice\n" +
+ "Given\n" +
+ "When\n" +
+ "Then\n");
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " Given a nice");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "When");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Then");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(null);
+ }
+
+ private void advanceAndAssert(@Nullable IElementType storyTokenType) {
+ storyLexer.advance();
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private void advanceAndAssert(IElementType storyTokenType, String content) {
+ storyLexer.advance();
+ assertToken(storyTokenType, content);
+ }
+
+ private void assertToken(IElementType storyTokenType, String content) {
+ assertThat(storyLexer.getTokenSequence()).isEqualTo(content);
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private static void traceAll(String content, StoryLocalizedLexer storyLexer) {
+ storyLexer.start(content);
+
+ IElementType tokenType;
+ do {
+ tokenType = storyLexer.getTokenType();
+ System.out.println(
+ rightPad("" + storyLexer.getPosition(), 3) + " " +
+ "[" + rightPad(tokenType, "STORY_DESCRIPTION".length()) + "]" +
+ rightPad(storyLexer.lexerState(), "IN_DIRECTIVE".length()) +
+ ": >>" + escape(storyLexer.getTokenSequence()) + "<<");
+
+ storyLexer.advance();
+ tokenType = storyLexer.getTokenType();
+ }
+ while (tokenType != null);
+ }
+
+ private static String rightPad(Object object, int length) {
+ StringBuilder builder = new StringBuilder(object.toString());
+ while (builder.length() < length) {
+ builder.append(" ");
+ }
+ return builder.toString();
+ }
+
+ private static String escape(CharSequence tokenSequence) {
+ return tokenSequence.toString().replace("\n", "\\n").replace("\r", "\\r");
+ }
+
+
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer_SamplesTest.java b/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer_SamplesTest.java
new file mode 100644
index 0000000..33a299a
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/highlighter/StoryLocalizedLexer_SamplesTest.java
@@ -0,0 +1,235 @@
+package com.github.kumaraman21.intellijbehave.highlighter;
+
+import static com.github.kumaraman21.intellijbehave.Samples.EXAMPLES_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.LONG_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.META_SAMPLE;
+import static com.github.kumaraman21.intellijbehave.Samples.SIMPLE_SAMPLE;
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import com.github.kumaraman21.intellijbehave.utility.LocalizedStorySupport;
+import com.intellij.psi.tree.IElementType;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * @author @aloyer
+ */
+public class StoryLocalizedLexer_SamplesTest {
+
+ private StoryLocalizedLexer storyLexer;
+
+ @Test
+ @Ignore
+ public void traceAll() {
+ //traceAll(SIMPLE_SAMPLE);
+ traceAll(LONG_SAMPLE);
+ //traceAll(META_SAMPLE);
+ //traceAll(EXAMPLES_SAMPLE);
+ //traceAll(COMPLEX_SAMPLE);
+ }
+
+ @Test
+ public void parseSimpleSample() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start(SIMPLE_SAMPLE);
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META, "Meta:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@skip");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i am the user with nickname: \"weird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "When");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i try to login using the password \"soweird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Then");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ }
+
+ @Test
+ public void parseMetaSample() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start(META_SAMPLE);
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META, "Meta:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@author");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_TEXT, "carmen");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@skip");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i am the user with nickname: \"weird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ }
+
+ @Test
+ public void parseLongSample() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start(LONG_SAMPLE);
+
+ assertToken(StoryTokenType.NARRATIVE_TYPE, "Narrative:");
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, " ");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.NARRATIVE_TYPE, "In order to");
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, " play a game");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.NARRATIVE_TYPE, "As a");
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, " player");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.NARRATIVE_TYPE, "I want to");
+ advanceAndAssert(StoryTokenType.STORY_DESCRIPTION, " be able to create and manage my account");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META, "Meta:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.META_KEY, "@skip");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i am the user with nickname: \"weird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "When");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i try to login using the password \"soweird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Then");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " A known user cannot be logged using a wrong password");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " the following existing users:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " nickname ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " password ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Travis ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " PacMan ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i am the user with nickname: \"Travis\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "When");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i try to login using the password \"McCallum\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Then");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+
+ // ...
+ }
+
+ @Test
+ public void parseExamples() {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start(EXAMPLES_SAMPLE);
+
+ assertToken(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " An unknown user cannot be logged");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i am the user with nickname: \"\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_WHEN, "When");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i try to login using the password \"soweird\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_THEN, "Then");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " i get an error message of type \"Wrong Credentials\"");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.EXAMPLE_TYPE, "Examples:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " login ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " password ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Travis ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Pacman ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Vlad ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.TABLE_CELL, " Thundercat ");
+ advanceAndAssert(StoryTokenType.TABLE_DELIM);
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.SCENARIO_TYPE, "Scenario:");
+ advanceAndAssert(StoryTokenType.SCENARIO_TEXT, " A known user can be logged using the right password");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+ advanceAndAssert(StoryTokenType.STEP_TYPE_GIVEN, "Given");
+ advanceAndAssert(StoryTokenType.STEP_TEXT, " the following existing users:");
+ advanceAndAssert(StoryTokenType.WHITE_SPACE);
+
+ // ...
+ }
+
+ private void advanceAndAssert(IElementType storyTokenType) {
+ storyLexer.advance();
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private void advanceAndAssert(IElementType storyTokenType, String content) {
+ storyLexer.advance();
+ assertToken(storyTokenType, content);
+ }
+
+ private void assertToken(IElementType storyTokenType, String content) {
+ assertThat(storyLexer.getTokenSequence()).isEqualTo(content);
+ assertThat(storyLexer.getTokenType()).isEqualTo(storyTokenType);
+ }
+
+ private void traceAll(String content) {
+ storyLexer = new StoryLocalizedLexer(new LocalizedStorySupport());
+ storyLexer.start(content);
+
+ IElementType tokenType;
+ do {
+ tokenType = storyLexer.getTokenType();
+ System.out.println(
+ rightPad("" + storyLexer.getPosition(), 3) + " " +
+ "[" + rightPad(tokenType, "STORY_DESCRIPTION".length()) + "]" +
+ rightPad(storyLexer.lexerState(), "IN_DIRECTIVE".length()) +
+ ": >>" + escape(storyLexer.getTokenSequence()) + "<<");
+
+ storyLexer.advance();
+ tokenType = storyLexer.getTokenType();
+ }
+ while (tokenType != null);
+ }
+
+ private String rightPad(Object object, int length) {
+ StringBuilder builder = new StringBuilder(object.toString());
+ while (builder.length() < length) {
+ builder.append(" ");
+ }
+ return builder.toString();
+ }
+
+ private String escape(CharSequence tokenSequence) {
+ return tokenSequence.toString().replace("\n", "\\n").replace("\r", "\\r");
+ }
+
+
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/parser/IntelliJBehaveBaseTestCase.java b/test/com/github/kumaraman21/intellijbehave/parser/IntelliJBehaveBaseTestCase.java
new file mode 100644
index 0000000..8628455
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/parser/IntelliJBehaveBaseTestCase.java
@@ -0,0 +1,112 @@
+package com.github.kumaraman21.intellijbehave.parser;
+
+import com.github.kumaraman21.intellijbehave.Samples;
+import com.github.kumaraman21.intellijbehave.highlighter.StoryLexerFactory;
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.impl.PsiBuilderImpl;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.psi.impl.DebugUtil;
+import com.intellij.testFramework.PsiTestCase;
+import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
+import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
+import com.intellij.testFramework.fixtures.TestFixtureBuilder;
+
+/**
+ * @author @aloyer
+ */
+public class IntelliJBehaveBaseTestCase extends PsiTestCase {
+
+ private Project myProject;
+ protected IdeaProjectTestFixture myFixture;
+ protected CodeStyleSettings mySettings;
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ protected CodeStyleSettings getSettings() {
+ return CodeStyleSettingsManager.getSettings(myProject);
+ }
+
+// protected void setSettings() {
+// final StoryFileType fileType = StoryFileType.STORY_FILE_TYPE;
+// mySettings = getSettings();
+// mySettings.getIndentOptions(fileType).INDENT_SIZE = 2;
+// mySettings.getIndentOptions(fileType).CONTINUATION_INDENT_SIZE = 2;
+// mySettings.getIndentOptions(fileType).TAB_SIZE = 2;
+// }
+
+ protected void setUp() throws Exception {
+ System.setProperty("idea.platform.prefix", "Idea");
+ myFixture = createFixture();
+ myFixture.setUp();
+ myProject = myFixture.getProject();
+ }
+
+ protected IdeaProjectTestFixture createFixture() {
+ TestFixtureBuilder fixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createLightFixtureBuilder();
+ return fixtureBuilder.getFixture();
+ }
+
+ protected void tearDown() {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ try {
+ if(!myFixture.getProject().isDisposed())
+ myFixture.tearDown();
+ } catch (Exception e) {
+ // mute
+ }
+ }
+ });
+ }
+
+ public void testCase_Simple () {
+ ASTNode astNode = doParse(Samples.SIMPLE_SAMPLE);
+ System.out.println("IntelliJBehaveBaseTestCase.testCase_Simple: " + DebugUtil.treeToString(astNode, false));
+ }
+
+ public void testCase_Long () {
+ ASTNode astNode = doParse(Samples.LONG_SAMPLE);
+ System.out.println("IntelliJBehaveBaseTestCase.testCase_Long: " + DebugUtil.treeToString(astNode, false));
+ }
+
+ public void testCase_Meta () {
+ ASTNode astNode = doParse(Samples.META_SAMPLE);
+ System.out.println("IntelliJBehaveBaseTestCase.testCase_Meta: " + DebugUtil.treeToString(astNode, false));
+ }
+
+ public void testCase_Examples () {
+ ASTNode astNode = doParse(Samples.EXAMPLES_SAMPLE);
+ System.out.println("IntelliJBehaveBaseTestCase.testCase_Examples: " + DebugUtil.treeToString(astNode, false));
+ }
+
+ public void testCase_Complex() {
+ ASTNode astNode = doParse(Samples.COMPLEX_SAMPLE);
+ System.out.println("IntelliJBehaveBaseTestCase.testCase_Complex: " + DebugUtil.treeToString(astNode, false));
+ }
+
+ public void testCase_SimpleFR() {
+ ASTNode astNode = doParse(Samples.SIMPLE_FR);
+ System.out.println("IntelliJBehaveBaseTestCase.testCase_Complex: " + DebugUtil.treeToString(astNode, false));
+ }
+
+ private ASTNode doParse(String content) {
+ PsiBuilder builder = new PsiBuilderImpl(myProject,
+ null,
+ new StoryParserDefinition(),
+ new StoryLexerFactory().createLexer(),
+ null,
+ content,
+ null,
+ null);
+
+ StoryParser parser = new StoryParser();
+ return parser.parse(StoryElementType.STORY_FILE, builder);
+ }
+
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/parser/StoryParserTest.java b/test/com/github/kumaraman21/intellijbehave/parser/StoryParserTest.java
new file mode 100644
index 0000000..442b6ab
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/parser/StoryParserTest.java
@@ -0,0 +1,29 @@
+package com.github.kumaraman21.intellijbehave.parser;
+
+import com.github.kumaraman21.intellijbehave.Samples;
+import com.github.kumaraman21.intellijbehave.highlighter.StoryLexer;
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.impl.PsiBuilderImpl;
+import com.intellij.openapi.project.Project;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * @author @aloyer
+ *
+ * @see IntelliJBehaveBaseTestCase
+ */
+@Ignore("... need a full plateform initialization!?!")
+public class StoryParserTest {
+
+ @Test
+ public void test1() throws Throwable {
+ Project project = Mockito.mock(Project.class);
+ PsiBuilder builder = new PsiBuilderImpl(project, null, new StoryParserDefinition(), new StoryLexer(), null, Samples.SIMPLE_SAMPLE, null, null);
+
+ StoryParser parser = new StoryParser();
+ parser.parse(StoryElementType.STORY_FILE, builder);
+ }
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java b/test/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java
new file mode 100644
index 0000000..c2b00ec
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java
@@ -0,0 +1,34 @@
+package com.github.kumaraman21.intellijbehave.utility;
+
+import static com.github.kumaraman21.intellijbehave.utility.JBKeyword.Narrative;
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import com.github.kumaraman21.intellijbehave.utility.CharTree;
+import com.github.kumaraman21.intellijbehave.utility.JBKeyword;
+
+import org.jbehave.core.i18n.LocalizedKeywords;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author @aloyer
+ */
+public class CharTreeTest {
+
+ private CharTree charTree;
+
+ @Before
+ public void setUp () {
+ LocalizedKeywords keywords = new LocalizedKeywords();
+ charTree = new CharTree('/', null);
+ for (JBKeyword kw : JBKeyword.values()) {
+ String asString = kw.asString(keywords);
+ charTree.push(asString, kw);
+ }
+ }
+
+ @Test
+ public void narrative_doubleDot() {
+ assertThat(charTree.lookup("Narrative: \n", 0)).isEqualTo(new CharTree.Entry(Narrative, 10));
+ }
+}
diff --git a/test/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java b/test/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java
new file mode 100644
index 0000000..c51dbd2
--- /dev/null
+++ b/test/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java
@@ -0,0 +1,25 @@
+package com.github.kumaraman21.intellijbehave.utility;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+/**
+ * @author @aloyer
+ */
+public class LocalizedStorySupportTest {
+
+ @Test
+ public void checkForLanguageDefinition_validCases() {
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- language:fr")).isEqualTo("fr");
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- language: fr")).isEqualTo("fr");
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- language: fr ")).isEqualTo("fr");
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- language: fr ")).isEqualTo("fr");
+ }
+ @Test
+ public void checkForLanguageDefinition_invalidCases() {
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- languge:fr")).isNull();
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- Language: fr")).isNull();
+ assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- lang: fr ")).isNull();
+ }
+}
diff --git a/testData/story/test1.story b/testData/story/test1.story
new file mode 100644
index 0000000..e69de29
diff --git a/testLib/fest-assert-core-2.0M5.jar b/testLib/fest-assert-core-2.0M5.jar
new file mode 100644
index 0000000..e7c4b78
Binary files /dev/null and b/testLib/fest-assert-core-2.0M5.jar differ
diff --git a/testLib/fest-util-1.2.0.jar b/testLib/fest-util-1.2.0.jar
new file mode 100644
index 0000000..560c705
Binary files /dev/null and b/testLib/fest-util-1.2.0.jar differ
diff --git a/testLib/junit-4.10.jar b/testLib/junit-4.10.jar
new file mode 100644
index 0000000..954851e
Binary files /dev/null and b/testLib/junit-4.10.jar differ
diff --git a/testLib/mockito-all-1.9.0.jar b/testLib/mockito-all-1.9.0.jar
new file mode 100644
index 0000000..273fd50
Binary files /dev/null and b/testLib/mockito-all-1.9.0.jar differ