diff --git a/org.agileware.natural.common.feature/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs similarity index 100% rename from org.agileware.natural.common.feature/.settings/org.eclipse.core.resources.prefs rename to .settings/org.eclipse.core.resources.prefs diff --git a/LaunchNatural.launch b/LaunchNatural.launch index 00e60790..cee476a0 100644 --- a/LaunchNatural.launch +++ b/LaunchNatural.launch @@ -1,35 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.common.feature/.project b/org.agileware.natural.common.feature/.project deleted file mode 100644 index 4880be54..00000000 --- a/org.agileware.natural.common.feature/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - org.agileware.natural.common.feature - - - - - - org.eclipse.pde.FeatureBuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.pde.FeatureNature - org.eclipse.m2e.core.maven2Nature - - diff --git a/org.agileware.natural.common.feature/build.properties b/org.agileware.natural.common.feature/build.properties deleted file mode 100644 index 64f93a9f..00000000 --- a/org.agileware.natural.common.feature/build.properties +++ /dev/null @@ -1 +0,0 @@ -bin.includes = feature.xml diff --git a/org.agileware.natural.common.feature/feature.xml b/org.agileware.natural.common.feature/feature.xml deleted file mode 100644 index b4243dcb..00000000 --- a/org.agileware.natural.common.feature/feature.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - [Enter Feature Description here.] - - - - [Enter Copyright Description here.] - - - - [Enter License Description here.] - - - - - diff --git a/org.agileware.natural.common.feature/pom.xml b/org.agileware.natural.common.feature/pom.xml deleted file mode 100644 index d38b3660..00000000 --- a/org.agileware.natural.common.feature/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - 4.0.0 - - org.agileware - natural - 1.0.0-SNAPSHOT - - org.agileware.natural.common.feature - eclipse-feature - 1.3.0-SNAPSHOT - - - - diff --git a/org.agileware.natural.common/META-INF/MANIFEST.MF b/org.agileware.natural.common/META-INF/MANIFEST.MF deleted file mode 100644 index 59f35422..00000000 --- a/org.agileware.natural.common/META-INF/MANIFEST.MF +++ /dev/null @@ -1,25 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Natural Core SDK -Bundle-SymbolicName: org.agileware.natural.common -Bundle-Version: 1.3.0.qualifier -Bundle-Activator: org.agileware.natural.common.Activator -Require-Bundle: org.eclipse.core.runtime;bundle-version="3.17.100";visibility:=reexport, - org.eclipse.jface.text;bundle-version="3.16.200";visibility:=reexport, - org.eclipse.ui;bundle-version="3.116.0", - org.eclipse.ui.ide;bundle-version="3.17.0", - org.eclipse.jdt.ui;bundle-version="3.21.0", - org.eclipse.jdt.core;bundle-version="3.21.0", - org.eclipse.core.resources;bundle-version="3.13.700", - com.google.guava;bundle-version="27.1.0", - com.google.inject;bundle-version="3.0.0", - org.eclipse.emf.ecore -Bundle-ActivationPolicy: lazy -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-Vendor: Roberto Lo Giacco -Export-Package: org.agileware.natural.common; - uses:="org.osgi.framework, - org.eclipse.jdt.core, - org.eclipse.jface.text, - org.eclipse.jface.text.hyperlink, - org.eclipse.jdt.core.search" diff --git a/org.agileware.natural.common/category.xml b/org.agileware.natural.common/category.xml deleted file mode 100644 index 446905e0..00000000 --- a/org.agileware.natural.common/category.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - Rich editors for behavioral testing definition files - - - diff --git a/org.agileware.natural.common/src/org/agileware/natural/common/Activator.java b/org.agileware.natural.common/src/org/agileware/natural/common/Activator.java deleted file mode 100644 index 7d9da098..00000000 --- a/org.agileware.natural.common/src/org/agileware/natural/common/Activator.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.agileware.natural.common; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -public class Activator implements BundleActivator { - - private static BundleContext context; - - static BundleContext getContext() { - return context; - } - - /** - * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) - */ - public void start(BundleContext bundleContext) throws Exception { - Activator.context = bundleContext; - } - - /** - * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) - */ - public void stop(BundleContext bundleContext) throws Exception { - Activator.context = null; - } - -} diff --git a/org.agileware.natural.common/src/org/agileware/natural/common/JavaAnnotationMatcher.java b/org.agileware.natural.common/src/org/agileware/natural/common/JavaAnnotationMatcher.java deleted file mode 100644 index 2c1a23bc..00000000 --- a/org.agileware.natural.common/src/org/agileware/natural/common/JavaAnnotationMatcher.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.agileware.natural.common; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeSet; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.search.IJavaSearchConstants; -import org.eclipse.jdt.core.search.IJavaSearchScope; -import org.eclipse.jdt.core.search.SearchEngine; -import org.eclipse.jdt.core.search.SearchMatch; -import org.eclipse.jdt.core.search.SearchParticipant; -import org.eclipse.jdt.core.search.SearchPattern; -import org.eclipse.jdt.core.search.SearchRequestor; - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -@Singleton -public class JavaAnnotationMatcher { - - @Inject - private AbstractAnnotationDescriptor descriptor; - - private Map> cache = new HashMap>(); - - protected IJavaSearchScope getScope(String filter) { - if (filter == null) - return SearchEngine.createWorkspaceScope(); - - String[] names = filter.split(","); - final List packages = new ArrayList(); - - SearchPattern pattern = null; - for (String name : names) { - SearchPattern current = SearchPattern.createPattern(name.trim(), - IJavaSearchConstants.PACKAGE, - IJavaSearchConstants.ALL_OCCURRENCES, - SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); - if (pattern == null) { - pattern = current; - } else { - pattern = SearchPattern.createOrPattern(pattern, current); - } - } - try { - new SearchEngine().search(pattern, - new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, SearchEngine.createWorkspaceScope(), new SearchRequestor() { - public void acceptSearchMatch(SearchMatch match) throws CoreException { - packages.add((IJavaElement) match.getElement()); - } - }, null); - } catch (CoreException e) { - e.printStackTrace(); - } - return SearchEngine.createJavaSearchScope(packages.toArray(new IJavaElement[0])); - } - - public void findMatches(final String description, final Command command) { - long time = System.currentTimeMillis(); - if (!cache.isEmpty()) { - for (List entries : cache.values()) { - for (Entry entry : entries) { - if (description.matches(entry.annotationValue)) { - command.match(entry.annotationValue, entry.method); - } - } - } - return; - } - - // combine search patterns - SearchPattern pattern = null; - for (final String annotationName : descriptor.getNames()) { - SearchPattern current = SearchPattern.createPattern(annotationName, - IJavaSearchConstants.ANNOTATION_TYPE, - IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE, - SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); - if (pattern == null) { - pattern = current; - } else { - pattern = SearchPattern.createOrPattern(pattern, current); - } - } - // execute search - try { - new SearchEngine().search(pattern, - new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, - this.getScope(null), - new SearchRequestor() { - public void acceptSearchMatch(SearchMatch match) throws CoreException { - if (match.getElement() instanceof IMethod) { - IMethod method = (IMethod) match.getElement(); - IAnnotation[] annotations = method.getAnnotations(); - for (IAnnotation type : annotations) { - // check annotation package - if (AbstractAnnotationDescriptor.checkPackage(type, descriptor.getPackage())) { - // verify pattern - String annotationValue = (String) type.getMemberValuePairs()[0].getValue(); - List entries = cache.get(method.getCompilationUnit()); - if (entries == null) { - entries = new ArrayList(); - cache.put(method.getCompilationUnit(), entries); - } - entries.add(new Entry(annotationValue, method)); - if (description.matches(annotationValue)) { - command.match(annotationValue, method); - } - } - } - } - } - }, - null); - } catch (CoreException e) { - e.printStackTrace(); - } - System.out.println("stepdef match lookup completed in " + (System.currentTimeMillis() - time) + "ms"); - } - - public Collection findProposals() { - if (cache.isEmpty()) { - findMatches("", new Command() { - @Override - public void match(String annotationValue, IMethod method) { } - }); - } - - Collection proposals = new TreeSet(); - for (List entries : cache.values()) { - for (Entry entry : entries) { - proposals.add(entry.getAnnotationValue()); - } - } - return proposals; - } - - public void evict(ICompilationUnit element) { - cache.clear(); - System.out.println(">>> cache cleared"); - } - - public static interface Command { - void match(String annotationValue, IMethod method); - } - - public static class Entry { - private String annotationValue; - private IMethod method; - - private Entry(String annotationValue, IMethod method) { - this.annotationValue = annotationValue; - this.method = method; - } - - public String getAnnotationValue() { - return annotationValue; - } - } -} diff --git a/org.agileware.natural.cucumber.feature/feature.xml b/org.agileware.natural.cucumber.feature/feature.xml index c8493837..f50c86fe 100644 --- a/org.agileware.natural.cucumber.feature/feature.xml +++ b/org.agileware.natural.cucumber.feature/feature.xml @@ -66,7 +66,14 @@ to corresponding Java annotated classes. unpack="false"/> + + 4.0.0 @@ -8,7 +9,6 @@ org.agileware.natural.cucumber.tests eclipse-test-plugin - 1.0.0-SNAPSHOT @@ -16,14 +16,15 @@ org.eclipse.xtend xtend-maven-plugin + + org.eclipse.tycho + tycho-surefire-plugin + + org.agileware.natural.cucumber.tests + org.agileware.natural.cucumber.tests.CucumberTestSuite + + - - - org.hamcrest - hamcrest - 2.2 - - diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/Constants.xtend b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/Constants.xtend index 9cd2c38e..9b3be2c5 100644 --- a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/Constants.xtend +++ b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/Constants.xtend @@ -8,49 +8,52 @@ class Constants { @version:1.0.0 @pet_store Feature: Add a new pet + In order to sell a pet As a store owner I want to add a new pet to the catalog - Background: Add a dog - Given I have the following pet - | name | status | - | Fido | available | - And I add the pet to the store - But the pet is not yet mine + Background: Add a dog + Given I have the following pet + | name | status | + | Fido | available | + And I add the pet to the store + But the pet is not yet mine - @add @fido - Scenario: Add another dog - Then the should be available in the store + @add + @fido + Scenario: Add another dog + Then the should be available in the store - @update @fido - Scenario: - Given the pet is available in the store -9.8 - """ - The quick brown fox - Jumps over the lazy dog - """ - When I update the pet with - | name | status | - | Fido | unavailable | - Then the pet should be "unavailable" in the store + @update + @fido + Scenario: + Given the pet is available in the store -9.8 + """ + The quick brown fox + Jumps over the lazy dog + """ + When I update the pet with + | name | status | + | Fido | unavailable | + Then the pet should be "unavailable" in the store - @eat-pickles - Scenario Outline: Eating pickles - Given there are pickles - When I eat pickles - Then I should have pickles + @eat-pickles + Scenario Outline: Eating pickles + Given there are pickles + When I eat pickles + Then I should have pickles - @hungry - Examples: - | start | eat | left | - | 12 | 10 | 2 | - | 20 | 15 | 5 | + @hungry + Examples: + | start | eat | left | + | 12 | 10 | 2 | + | 20 | 15 | 5 | - @full - Examples: With a title - | start | eat | left | - | 12 | 2 | 10 | - | 20 | 5 | 15 | + @full + Examples: With a title + | start | eat | left | + | 12 | 2 | 10 | + | 20 | 5 | 15 | ''' } diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExampleTestSuite.xtend b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExampleTestSuite.xtend deleted file mode 100644 index 5aae77ac..00000000 --- a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExampleTestSuite.xtend +++ /dev/null @@ -1,194 +0,0 @@ -package org.agileware.natural.cucumber.tests - -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Suite - -@RunWith(Suite) -@Suite.SuiteClasses(BackgroundExamples, ScenarioExamples, ScenarioOutlineExamples, PathologicalExamples) -class CucumberExampleTestSuite { - - static class BackgroundExamples extends CucumberExamplesTest { - - @Test - def void background_01() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - - Background: Foo - Given "Jack" went up the hill - - Scenario: - Given a step - ''') - } - - @Test - def void background_02() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - - @foo @bar - Background: Jack and Jill - The quick brown fox - Jumps over the lazy dog - - Given the stock is traded at 5.0 - """ - The quick brown fox - Jumps over the lazy dog - """ - And the alert status should be OFF - When the stock is traded at 11.0 - | precondition | be-captured | - | abc | be captured | - | xyz | not be captured | - Then the alert status should be ON - - Scenario: - Given a step - ''') - } - } - - static class ScenarioExamples extends CucumberExamplesTest { - - @Test - def void scenario_01() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - Scenario: A - Given a step - ''') - } - - @Test - def void scenario_02() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - The quick brown fox - Jumps over the lazy dog - - Scenario: - The quick brown fox - Jumps over the lazy dog - - Given a step - - Scenario: B - And another - ''') - } - - @Test - def void scenario_03() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - - @foo @bar - Scenario: Stock Symbols - The quick brown fox - Jumps over the lazy dog - - Given the stock is traded at 5.0 - """ - The quick brown fox - Jumps over the lazy dog - """ - And the alert status should be OFF - When the stock is traded at 11.0 - | precondition | be-captured | - | abc | be captured | - | xyz | not be captured | - Then the alert status should be ON - ''') - } - } - - static class ScenarioOutlineExamples extends CucumberExamplesTest { - @Test - def void scenario_outline_01() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - - Scenario Outline: Eating pickles - Given there are pickles - When I eat pickles - Then I should have pickles - - Examples: - | start | eat | left | - | 12 | 10 | 2 | - | 20 | 15 | 5 | - ''') - } - - @Test - def void scenario_outline_02() { - assertExampleParses(''' - # language: en - Feature: Hello, Cucumber! - - @eating-pickles - Scenario Outline: Eating pickles - Given there are pickles - When I eat pickles - Then I should have pickles - - @foo - Examples: A - | start | eat | left | - | 12 | 10 | 2 | - - @bar - Examples: B - | start | eat | left | - | 20 | 15 | 5 | - ''') - } - - } - - static class PathologicalExamples extends CucumberExamplesTest { - - @Test - def void pathological_01() { - assertExampleParses(''' - # language: en - Feature: ASCII punctuation - ,./;'[]\-= - <>?:"{}|_+ - !@#$%^&*()`~ - - Scenario: - Given a step - ''') - } - - @Test - def void pathological_03() { - assertExampleParses(''' - # language: en - Feature: Two-Byte Characters - 田中さんにあげて下さい - パーティーへ行かないか - 和製漢語 - 部落格 - 사회과학원 어학연구소 - 찦차를 타고 온 펲시맨과 쑛다리 똠방각하 - 社會科學院語學研究所 - 울란바토르 - 𠜎𠜱𠝹𠱓𠱸𠲖𠳏 - - Scenario: - Given a step𝅳𝅴𝅵𝅶𝅷𝅸𝅹𝅺󠀁󠀠󠀡󠀢󠀣󠀤󠀥󠀦󠀧󠀨󠀩󠀪󠀫󠀬󠀭󠀮󠀯󠀰󠀱󠀲󠀳󠀴󠀵󠀶󠀷󠀸󠀹󠀺󠀻󠀼󠀽󠀾󠀿󠁀󠁁󠁂󠁃󠁄󠁅󠁆󠁇󠁈󠁉󠁊󠁋󠁌󠁍󠁎󠁏󠁐󠁑󠁒󠁓󠁔󠁕󠁖󠁗󠁘󠁙󠁚󠁛󠁜󠁝󠁞󠁟󠁠󠁡󠁢󠁣󠁤󠁥󠁦󠁧󠁨󠁩󠁪󠁫󠁬󠁭󠁮󠁯󠁰󠁱󠁲󠁳󠁴󠁵󠁶󠁷󠁸󠁹󠁺󠁻󠁼󠁽󠁾󠁿 - ''') - } - } -} diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExamplesTest.xtend b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExamplesTest.xtend index 06d46267..708a89f1 100644 --- a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExamplesTest.xtend +++ b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberExamplesTest.xtend @@ -1,11 +1,211 @@ package org.agileware.natural.cucumber.tests -import org.agileware.natural.cucumber.cucumber.Feature +import org.agileware.natural.cucumber.cucumber.CucumberModel import org.agileware.natural.testing.AbstractExamplesTest import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.XtextRunner +import org.junit.Test import org.junit.runner.RunWith @RunWith(XtextRunner) @InjectWith(CucumberInjectorProvider) -class CucumberExamplesTest extends AbstractExamplesTest {} +class CucumberExamplesTest extends AbstractExamplesTest { + + @Test + def void background_01() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + + Background: Foo + Given "Jack" went up the hill + + Scenario: + Given a step + ''') + } + + @Test + def void background_02() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + + @foo @bar + Background: Jack and Jill + The quick brown fox + Jumps over the lazy dog + + Given the stock is traded at 5.0 + """ + The quick brown fox + Jumps over the lazy dog + """ + And the alert status should be OFF + When the stock is traded at 11.0 + | precondition | be-captured | + | abc | be captured | + | xyz | not be captured | + Then the alert status should be ON + + Scenario: + Given a step + ''') + } + + @Test + def void scenario_01() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + Scenario: A + Given a step + ''') + } + + @Test + def void scenario_02() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + The quick brown fox + Jumps over the lazy dog + + Scenario: + The quick brown fox + Jumps over the lazy dog + + Given a step + + Scenario: B + And another + ''') + } + + @Test + def void scenario_03() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + + @foo @bar + Scenario: Stock Symbols + The quick brown fox + Jumps over the lazy dog + + Given the stock is traded at 5.0 + """ + The quick brown fox + Jumps over the lazy dog + """ + And the alert status should be OFF + When the stock is traded at 11.0 + | precondition | be-captured | + | abc | be captured | + | xyz | not be captured | + Then the alert status should be ON + ''') + } + + @Test + def void scenario_outline_01() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + + Scenario Outline: Eating pickles + Given there are pickles + When I eat pickles + Then I should have pickles + + Examples: + | start | eat | left | + | 12 | 10 | 2 | + | 20 | 15 | 5 | + ''') + } + + @Test + def void scenario_outline_02() { + assertExampleParses(''' + # language: en + Feature: Hello, Cucumber! + + @eating-pickles + Scenario Outline: Eating pickles + Given there are pickles + When I eat pickles + Then I should have pickles + + @foo + Examples: A + | start | eat | left | + | 12 | 10 | 2 | + + @bar + Examples: B + | start | eat | left | + | 20 | 15 | 5 | + ''') + } + + @Test + def void pathological_01() { + assertExampleParses(''' + # language: en + Feature: ASCII punctuation + # ,./;'[]\-= + # <>?:"{}|_+ + # !@#$%^&*()`~ + + Scenario: + Given a step + ''') + } + + @Test + def void pathological_02() { + assertExampleParses(''' + # language: en + Feature: Two-Byte Characters + 田中さんにあげて下さい + パーティーへ行かないか + 和製漢語 + 部落格 + 사회과학원 어학연구소 + 찦차를 타고 온 펲시맨과 쑛다리 똠방각하 + 社會科學院語學研究所 + 울란바토르 + 𠜎𠜱𠝹𠱓𠱸𠲖𠳏 + + Scenario: + Given a step𝅳𝅴𝅵𝅶𝅷𝅸𝅹𝅺󠀁󠀠󠀡󠀢󠀣󠀤󠀥󠀦󠀧󠀨󠀩󠀪󠀫󠀬󠀭󠀮󠀯󠀰󠀱󠀲󠀳󠀴󠀵󠀶󠀷󠀸󠀹󠀺󠀻󠀼󠀽󠀾󠀿󠁀󠁁󠁂󠁃󠁄󠁅󠁆󠁇󠁈󠁉󠁊󠁋󠁌󠁍󠁎󠁏󠁐󠁑󠁒󠁓󠁔󠁕󠁖󠁗󠁘󠁙󠁚󠁛󠁜󠁝󠁞󠁟󠁠󠁡󠁢󠁣󠁤󠁥󠁦󠁧󠁨󠁩󠁪󠁫󠁬󠁭󠁮󠁯󠁰󠁱󠁲󠁳󠁴󠁵󠁶󠁷󠁸󠁹󠁺󠁻󠁼󠁽󠁾󠁿 + ''') + } + + @Test + def void pathological_03() { + assertExampleParses(''' + # language: en + ''') + } + + @Test + def void pathological_04() { + assertExampleParses(''' + # language: en + Feature: Hello, World! + ''') + } + + @Test + def void pathological_05() { + assertExampleParses(''' + # language: en + Feature: + Background: + Scenario: A + Scenario: B + ''') + } +} diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberFormatterTest.xtend b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberFormatterTest.xtend index becc924a..8fb6cf25 100644 --- a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberFormatterTest.xtend +++ b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberFormatterTest.xtend @@ -1,6 +1,6 @@ package org.agileware.natural.cucumber.tests -import org.agileware.natural.cucumber.cucumber.Feature +import org.agileware.natural.cucumber.cucumber.CucumberModel import org.agileware.natural.testing.AbstractFormatterTest import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.XtextRunner @@ -11,7 +11,7 @@ import static org.agileware.natural.cucumber.tests.Constants.EXAMPLE_FEATURE @RunWith(XtextRunner) @InjectWith(CucumberInjectorProvider) -class CucumberFormatterTest extends AbstractFormatterTest { +class CucumberFormatterTest extends AbstractFormatterTest { @Test def void preserveValidFormatting() { @@ -24,13 +24,13 @@ class CucumberFormatterTest extends AbstractFormatterTest { val toBeFormatted = ''' # language: en Feature: Jack and Jill - - Background: - Given a precondition - - Scenario: Jack falls down - When Jack falls down - Then Jill comes tumbling after + + Background: + Given a precondition + + Scenario: Jack falls down + When Jack falls down + Then Jill comes tumbling after ''' assertFormatted(toBeFormatted) } @@ -52,13 +52,13 @@ class CucumberFormatterTest extends AbstractFormatterTest { val expectation = ''' # language: en Feature: Jack and Jill + + Background: + Given a precondition - Background: - Given a precondition - - Scenario: Jack falls down - When Jack falls down - Then Jill comes tumbling after + Scenario: Jack falls down + When Jack falls down + Then Jill comes tumbling after ''' assertFormatted(toBeFormatted, expectation) } @@ -69,7 +69,7 @@ class CucumberFormatterTest extends AbstractFormatterTest { val toBeFormatted = ''' # language: en @foo - @bar:1 + @bar Feature: Jack and Jill Background: @@ -88,21 +88,22 @@ class CucumberFormatterTest extends AbstractFormatterTest { val expectation = ''' # language: en @foo - @bar:1 + @bar Feature: Jack and Jill + + Background: + Given a precondition - Background: - Given a precondition - - @foo @bar - Scenario: A - And another + @foo + @bar + Scenario: A + And another - @foo - @bar - Scenario: B - When something happens - Then there should be a result + @foo + @bar + Scenario: B + When something happens + Then there should be a result ''' assertFormatted(toBeFormatted, expectation) } @@ -113,15 +114,15 @@ class CucumberFormatterTest extends AbstractFormatterTest { val toBeFormatted = ''' # language: en Feature: With a title - - Scenario Outline: - Given - And - - Examples: - | foo | bar | - | 12 | 10 | - | 20 | 15 | + + Scenario Outline: + Given + And + + Examples: + | foo | bar | + | 12 | 10 | + | 20 | 15 | ''' assertFormatted(toBeFormatted) } @@ -144,15 +145,15 @@ class CucumberFormatterTest extends AbstractFormatterTest { val expectation = ''' # language: en Feature: With a title - - Scenario Outline: - Given - And - - Examples: - | foo | bar | - | 12 | 10 | - | 20 | 15 | + + Scenario Outline: + Given + And + + Examples: + | foo | bar | + | 12 | 10 | + | 20 | 15 | ''' assertFormatted(toBeFormatted, expectation) } @@ -163,22 +164,22 @@ class CucumberFormatterTest extends AbstractFormatterTest { val toBeFormatted = ''' # language: en Feature: With a title - - Scenario Outline: - Given - And - - @foo - Examples: - | foo | bar | - | 12 | 10 | - | 20 | 15 | - - @bar - Examples: - | foo | bar | - | 12 | 10 | - | 20 | 15 | + + Scenario Outline: + Given + And + + @foo + Examples: + | foo | bar | + | 12 | 10 | + | 20 | 15 | + + @bar + Examples: + | foo | bar | + | 12 | 10 | + | 20 | 15 | ''' assertFormatted(toBeFormatted) } @@ -208,27 +209,26 @@ class CucumberFormatterTest extends AbstractFormatterTest { val expectation = ''' # language: en Feature: With a title - - Scenario Outline: - Given - And - - @foo - Examples: - | foo | bar | - | 12 | 10 | - | 20 | 15 | - - @bar - Examples: - | foo | bar | - | 12 | 10 | - | 20 | 15 | + + Scenario Outline: + Given + And + + @foo + Examples: + | foo | bar | + | 12 | 10 | + | 20 | 15 | + + @bar + Examples: + | foo | bar | + | 12 | 10 | + | 20 | 15 | ''' assertFormatted(toBeFormatted, expectation) } - - + @Test def void indentDocString_01() { // SHOULD DocString to column 1 @@ -246,44 +246,43 @@ class CucumberFormatterTest extends AbstractFormatterTest { val expectation = ''' # language: en Feature: With a title - - Scenario: - Given the DocString - """ - The quick brown fox - Jumps over the lazy dog - """ + + Scenario: + Given the DocString + """ + The quick brown fox + Jumps over the lazy dog + """ ''' assertFormatted(toBeFormatted, expectation) } - + @Test def void indentStepInterior_01() { // MUST preserve existing formatting val toBeFormatted = ''' # language: en Feature: With a title - - Scenario: Stock Symbols - The quick brown fox - Jumps over the lazy dog - - Given the stock is traded at 5.0 - """ - The quick brown fox - Jumps over the lazy dog - """ - And the alert status should be OFF - When the stock is traded at 11.0 - | precondition | be-captured | - | abc | be captured | - | xyz | not be captured | - Then the alert status should be ON + + Scenario: Stock Symbols + The quick brown fox + Jumps over the lazy dog + + Given the stock is traded at 5.0 + """ + The quick brown fox + Jumps over the lazy dog + """ + And the alert status should be OFF + When the stock is traded at 11.0 + | precondition | be-captured | + | abc | be captured | + | xyz | not be captured | + Then the alert status should be ON ''' assertFormatted(toBeFormatted) } - - + @Test def void indentStepInterior_02() { // SHOULD align Table and DocString to column 1 @@ -307,19 +306,19 @@ class CucumberFormatterTest extends AbstractFormatterTest { val expectation = ''' # language: en Feature: With a title - - Scenario: - Given the stock is traded at 5.0 - """ - The quick brown fox - Jumps over the lazy dog - """ - And the alert status should be OFF - When the stock is traded at 11.0 - | precondition | be-captured | - | abc | be captured | - | xyz | not be captured | - Then the alert status should be ON + + Scenario: + Given the stock is traded at 5.0 + """ + The quick brown fox + Jumps over the lazy dog + """ + And the alert status should be OFF + When the stock is traded at 11.0 + | precondition | be-captured | + | abc | be captured | + | xyz | not be captured | + Then the alert status should be ON ''' assertFormatted(toBeFormatted, expectation) } diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberParsingTest.xtend b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberParsingTest.xtend index ebd50e72..4f3f644c 100644 --- a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberParsingTest.xtend +++ b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberParsingTest.xtend @@ -1,7 +1,7 @@ package org.agileware.natural.cucumber.tests import com.google.inject.Inject -import org.agileware.natural.cucumber.cucumber.Feature +import org.agileware.natural.cucumber.cucumber.CucumberModel import org.agileware.natural.cucumber.cucumber.ScenarioOutline import org.agileware.natural.cucumber.serializer.CucumberSerializer import org.agileware.natural.testing.AbstractParserTest @@ -16,7 +16,7 @@ import static org.hamcrest.Matchers.* @RunWith(XtextRunner) @InjectWith(CucumberInjectorProvider) -class CucumberParsingTest extends AbstractParserTest { +class CucumberParsingTest extends AbstractParserTest { @Inject CucumberSerializer serializer @@ -34,11 +34,8 @@ class CucumberParsingTest extends AbstractParserTest { assertThat(model, notNullValue()) assertThat(validate(model), empty()) - - println(serializer.serialize(model)) } - - + @Test def void parseTableData() { val model = parse(''' @@ -61,14 +58,17 @@ class CucumberParsingTest extends AbstractParserTest { assertThat(model, notNullValue()) assertThat(validate(model), empty()) - - val t1 = model.scenarios.get(0).steps.get(0).table - assertThat(t1, notNullValue()) - assertThat(t1.rows, hasSize(1)) - - val t2 = (model.scenarios.get(1) as ScenarioOutline).examples.get(0).table - assertThat(t2, notNullValue()) - assertThat(t2.rows, hasSize(3)) + + val doc = model.document + assertThat(doc, notNullValue()) + +// val t1 = doc.scenarios.get(0).steps.get(0).table +// assertThat(t1, notNullValue()) +// assertThat(t1.rows, hasSize(1)) +// +// val t2 = (doc.scenarios.get(1) as ScenarioOutline).examples.get(0).table +// assertThat(t2, notNullValue()) +// assertThat(t2.rows, hasSize(3)) } @Test diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberTestSuite.java b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberTestSuite.java index c8683c39..edad0f63 100644 --- a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberTestSuite.java +++ b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberTestSuite.java @@ -5,8 +5,9 @@ @RunWith(Suite.class) @Suite.SuiteClasses({ - CucumberExampleTestSuite.class, + CucumberExamplesTest.class, CucumberParsingTest.class, - CucumberFormatterTest.class + CucumberFormatterTest.class, + CucumberValidatorTest.class }) public class CucumberTestSuite {} diff --git a/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberValidatorTest.xtend b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberValidatorTest.xtend new file mode 100644 index 00000000..6701e386 --- /dev/null +++ b/org.agileware.natural.cucumber.tests/src/org/agileware/natural/cucumber/tests/CucumberValidatorTest.xtend @@ -0,0 +1,51 @@ +package org.agileware.natural.cucumber.tests + +import org.agileware.natural.cucumber.cucumber.CucumberModel +import org.agileware.natural.testing.AbstractParserTest +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.XtextRunner +import org.junit.Test +import org.junit.runner.RunWith + +import static org.agileware.natural.cucumber.validation.CucumberIssueCodes.* +import static org.agileware.natural.testing.Matchers.* +import static org.hamcrest.MatcherAssert.* +import static org.hamcrest.Matchers.* + +@RunWith(XtextRunner) +@InjectWith(CucumberInjectorProvider) +class CucumberValidatorTest extends AbstractParserTest { + + @Test + def void featureMissingTitle() { + val issues = validate(parse(''' + # language: en + Feature: + Scenario: A + Given a step + ''')) + + assertThat(issues, hasItems(theWarning(MISSING_FEATURE_TITLE))) + } + + @Test + def void featureMissingScenarios() { + val issues = validate(parse(''' + # language: en + Feature: With a title + ''')) + + assertThat(issues, hasItems(theWarning(MISSING_SCENARIOS))) + } + + @Test + def void scenarioMissingSteps() { + val issues = validate(parse(''' + # language: en + Feature: With a title + Background: + ''')) + + assertThat(issues, hasItems(theWarning(MISSING_SCENARIO_STEPS))) + } +} diff --git a/org.agileware.natural.cucumber.ui.tests/build.properties b/org.agileware.natural.cucumber.ui.tests/build.properties index d2866c42..5c6bbf99 100644 --- a/org.agileware.natural.cucumber.ui.tests/build.properties +++ b/org.agileware.natural.cucumber.ui.tests/build.properties @@ -4,4 +4,3 @@ source.. = src/,\ bin.includes = .,\ META-INF/ bin.excludes = **/*.xtend -jars.extra.classpath = platform:/plugin/org.agileware.natural.testing/lib/hamcrest-2.2.jar diff --git a/org.agileware.natural.cucumber.ui.tests/pom.xml b/org.agileware.natural.cucumber.ui.tests/pom.xml index ecc4857e..c09645fd 100644 --- a/org.agileware.natural.cucumber.ui.tests/pom.xml +++ b/org.agileware.natural.cucumber.ui.tests/pom.xml @@ -1,4 +1,5 @@ - 4.0.0 @@ -19,11 +20,14 @@ org.eclipse.tycho tycho-surefire-plugin + false true + true + org.agileware.natural.cucumber.ui.tests + org.agileware.natural.cucumber.ui.tests.CucumberUiTestSuite - 1.0.0-SNAPSHOT diff --git a/org.agileware.natural.cucumber.ui.tests/src/org/agileware/natural/cucumber/ui/tests/CucumberHighlightingTest.xtend b/org.agileware.natural.cucumber.ui.tests/src/org/agileware/natural/cucumber/ui/tests/CucumberHighlightingTest.xtend index dbfbf72e..4d92b705 100644 --- a/org.agileware.natural.cucumber.ui.tests/src/org/agileware/natural/cucumber/ui/tests/CucumberHighlightingTest.xtend +++ b/org.agileware.natural.cucumber.ui.tests/src/org/agileware/natural/cucumber/ui/tests/CucumberHighlightingTest.xtend @@ -5,12 +5,334 @@ import org.eclipse.xtext.testing.XtextRunner import org.eclipse.xtext.ui.testing.AbstractHighlightingTest import org.junit.Test import org.junit.runner.RunWith +import org.agileware.natural.cucumber.ui.syntaxcoloring.HighlightingConfiguration +import com.google.inject.Inject @RunWith(XtextRunner) @InjectWith(CucumberUiInjectorProvider) class CucumberHighlightingTest extends AbstractHighlightingTest { + + @Inject extension HighlightingConfiguration + @Test - def void helloHighlighting() { - // TODO... + def void featureKeyword() { + ''' + Feature: + '''.testHighlighting("Feature:", keywordTextStyle) + } + + @Test + def void backgroundKeyword() { + ''' + Feature: + Background: + '''.testHighlighting("Background:", keywordTextStyle) + } + + @Test + def void scenarioKeyword() { + ''' + Feature: + Scenario: + '''.testHighlighting("Scenario:", keywordTextStyle) + } + + @Test + def void scenarioOutlineKeyword() { + ''' + Feature: + Scenario Outline: + '''.testHighlighting("Scenario Outline:", keywordTextStyle) + } + + @Test + def void examplesKeyword() { + ''' + Feature: + Scenario Outline: + Examples: + | foo | + '''.testHighlighting("Examples:", keywordTextStyle) + } + + @Test + def void givenStepKeyword() { + ''' + Feature: + Scenario: + Given foo + '''.testHighlighting("Given", keywordTextStyle) + } + + @Test + def void whenStepKeyword() { + ''' + Feature: + Scenario: + When foo + '''.testHighlighting("When", keywordTextStyle) + } + + @Test + def void thenStepKeyword() { + ''' + Feature: + Scenario: + Then foo + '''.testHighlighting("Then", keywordTextStyle) + } + + @Test + def void andStepKeyword() { + ''' + Feature: + Scenario: + And foo + '''.testHighlighting("And", keywordTextStyle) + } + + @Test + def void butStepKeyword() { + ''' + Feature: + Scenario: + But foo + '''.testHighlighting("But", keywordTextStyle) + } + + @Test + def void anyStepKeyword() { + ''' + Feature: + Scenario: + * foo + '''.testHighlighting("*", keywordTextStyle) + } + + @Test + def void docString_01() { + ''' + Feature: + """ + The quick brown fox + Jumps pver the lazy dog + """ + '''.testHighlighting(''' + """ + The quick brown fox + Jumps pver the lazy dog + """ + ''', docStringTextStyle) + } + + @Test + def void docString_02() { + ''' + Feature: + Scenario: + Given foo + """ + The quick brown fox + Jumps pver the lazy dog + """ + '''.testHighlighting(''' + """ + The quick brown fox + Jumps pver the lazy dog + """ + ''', docStringTextStyle) + } + + @Test + def void table_01() { + ''' + Feature: + | x | 0 | + | y | 1 | + '''.testHighlighting(''' + | x | 0 | + | y | 1 | + ''', tableTextStyle) + } + + @Test + def void table_02() { + ''' + Feature: + Scenario: + Given foo + | x | 0 | + | y | 1 | + '''.testHighlighting(''' + | x | 0 | + | y | 1 | + ''', docStringTextStyle) + } + + @Test + def void table_03() { + ''' + Feature: + Scenario Outline: + Given and + Examples: + | x | 0 | + | y | 1 | + '''.testHighlighting(''' + | x | 0 | + | y | 1 | + ''', docStringTextStyle) + } + + @Test + def void comment_01() { + ''' + # language: en + Feature: + '''.testHighlighting("# language: en", commentTextStyle) + } + + @Test + def void comment_02() { + ''' + Feature: #Foo + '''.testHighlighting("#Foo", commentTextStyle) + } + + @Test + def void string_01() { + ''' + Feature: + Scenario: + Given "foo" + '''.testHighlighting("\"foo\"", stringTextStyle) + } + + @Test + def void string_02() { + ''' + Feature: + Scenario: + Given ullamcorper "pretium ut eu" neque. + '''.testHighlighting("\"pretium ut eu\"", stringTextStyle) + } + + @Test + def void string_03() { + ''' + Feature: + Scenario: + Given 'foo' + '''.testHighlighting("'foo'", stringTextStyle) + } + + @Test + def void string_04() { + ''' + Feature: + Scenario: + Given ullamcorper 'pretium ut eu' neque. + '''.testHighlighting("'pretium ut eu'", stringTextStyle) + } + + + @Test + def void number_01() { + ''' + Feature: + Scenario: + Given 0 + '''.testHighlighting("0", numberTextStyle) + } + + @Test + def void number_02() { + ''' + Feature: + Scenario: + Given 1 + '''.testHighlighting("1", numberTextStyle) + } + + @Test + def void number_03() { + ''' + Feature: + Scenario: + Given -1 + '''.testHighlighting("-1", numberTextStyle) + } + + + @Test + def void number_04() { + ''' + Feature: + Scenario: + Given 1.0 + '''.testHighlighting("1.0", numberTextStyle) + } + + @Test + def void number_05() { + ''' + Feature: + Scenario: + Given -1.0 + '''.testHighlighting("-1.0", numberTextStyle) + } + + @Test + def void number_06() { + ''' + Feature: + Scenario: + Given 3.30e23 + '''.testHighlighting("3.30e23", numberTextStyle) + } + + @Test + def void number_07() { + ''' + Feature: + Scenario: + Given 6.67E-11 + '''.testHighlighting("6.67E-11", numberTextStyle) + } + + @Test + def void number_08() { + ''' + Feature: + Scenario: + Given -3.30e23 + '''.testHighlighting("-3.30e23", numberTextStyle) + } + + @Test + def void number_09() { + ''' + Feature: + Scenario: + Given 0xFFFFFF + '''.testHighlighting("0xFFFFFF", numberTextStyle) + } + + @Test + def void number_10() { + ''' + Feature: + Scenario: + Given -0xFFFFFF + '''.testHighlighting("-0xFFFFFF", numberTextStyle) + } + + @Test + def void number_11() { + ''' + Feature: + Scenario: + Given 1.0.0 + '''.testHighlighting("1.0.0", defaultTextStyle) } } diff --git a/org.agileware.natural.cucumber.ui/META-INF/MANIFEST.MF b/org.agileware.natural.cucumber.ui/META-INF/MANIFEST.MF index cab0ddab..d3f1a3b2 100644 --- a/org.agileware.natural.cucumber.ui/META-INF/MANIFEST.MF +++ b/org.agileware.natural.cucumber.ui/META-INF/MANIFEST.MF @@ -8,6 +8,8 @@ Bundle-SymbolicName: org.agileware.natural.cucumber.ui; singleton:=true Bundle-ActivationPolicy: lazy Require-Bundle: org.agileware.natural.cucumber, org.agileware.natural.cucumber.ide, + org.agileware.natural.lang.ui, + org.agileware.natural.stepmatcher.ui, org.eclipse.xtext.ui, org.eclipse.xtext.ui.shared, org.eclipse.xtext.ui.codetemplates.ui, @@ -24,11 +26,29 @@ Require-Bundle: org.agileware.natural.cucumber, Import-Package: org.apache.log4j, org.eclipse.xtext.ui.codemining;resolution:=optional Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: org.agileware.natural.cucumber.ui.contentassist;uses:="org.eclipse.emf.ecore,org.eclipse.xtext.ui.editor.contentassist,org.eclipse.xtext", +Export-Package: org.agileware.natural.cucumber.ui.codemining; + uses:="org.eclipse.xtext.ui.codemining, + org.eclipse.jface.text, + org.eclipse.xtext.resource, + org.eclipse.xtext.util", + org.agileware.natural.cucumber.ui.contentassist; + uses:="org.eclipse.emf.ecore, + org.agileware.natural.lang.ui.contentassist, + org.eclipse.xtext.ui.editor.contentassist, + org.eclipse.xtext", org.agileware.natural.cucumber.ui.internal;uses:="org.osgi.framework,com.google.inject,org.eclipse.ui.plugin", - org.agileware.natural.cucumber.ui.labeling;uses:="org.eclipse.xtext.ui.label,org.eclipse.emf.edit.ui.provider,org.agileware.natural.cucumber.cucumber", - org.agileware.natural.cucumber.ui.outline;uses:="org.eclipse.xtext.ui.editor.outline.impl,org.agileware.natural.cucumber.cucumber", + org.agileware.natural.cucumber.ui.labeling; + uses:="org.agileware.natural.lang.model, + org.eclipse.xtext.ui.label, + org.eclipse.emf.edit.ui.provider, + org.agileware.natural.cucumber.cucumber", + org.agileware.natural.cucumber.ui.outline;uses:="org.eclipse.xtext.ui.editor.outline.impl,org.agileware.natural.lang.model,org.agileware.natural.cucumber.cucumber", org.agileware.natural.cucumber.ui.quickfix;uses:="org.eclipse.xtext.ui.editor.quickfix", - org.agileware.natural.cucumber.ui.syntaxcoloring;uses:="org.eclipse.xtext.resource,org.eclipse.xtext.ui.editor.utils,org.eclipse.xtext.ui.editor.syntaxcoloring", + org.agileware.natural.cucumber.ui.syntaxcoloring; + uses:="org.eclipse.xtext.ide.editor.syntaxcoloring, + org.eclipse.xtext.resource, + org.eclipse.xtext.util, + org.eclipse.xtext.ui.editor.utils, + org.eclipse.xtext.ui.editor.syntaxcoloring", org.agileware.natural.cucumber.validation;uses:="org.eclipse.swt.widgets,org.eclipse.jface.dialogs,org.eclipse.xtext.ui.validation" -Bundle-Activator: org.agileware.natural.cucumber.ui.internal.CucumberActivator +Bundle-Activator: org.agileware.natural.cucumber.ui.CucumberActivator diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/BuilderParticipant.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/BuilderParticipant.java index a162d7f9..ebb78666 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/BuilderParticipant.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/BuilderParticipant.java @@ -1,19 +1,21 @@ package org.agileware.natural.cucumber.ui; -import org.agileware.natural.common.JavaAnnotationMatcher; +import org.agileware.natural.stepmatcher.ui.JavaAnnotationMatcher; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import com.google.inject.Inject; public class BuilderParticipant extends org.eclipse.xtext.builder.BuilderParticipant { + @Inject private JavaAnnotationMatcher matcher; - - public void build(final IBuildContext context, IProgressMonitor monitor) throws CoreException { + + @Override + public void build(final IBuildContext context, final IProgressMonitor monitor) throws CoreException { super.build(context, monitor); if (!context.getResourceSet().getResources().isEmpty()) { - //TODO iterate over the resources to evict only those that have changed + // TODO iterate over the resources to evict only those that have changed // matcher.evict(null); } } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberActivator.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberActivator.java index d8bd9a67..723ad49d 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberActivator.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberActivator.java @@ -1,7 +1,6 @@ package org.agileware.natural.cucumber.ui; -import org.agileware.natural.common.AbstractAnnotationDescriptor; -import org.agileware.natural.common.JavaElementChangeReporter; +import org.agileware.natural.stepmatcher.ui.JavaElementChangeReporter; import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.JavaCore; @@ -9,9 +8,9 @@ @SuppressWarnings("unused") public class CucumberActivator extends org.agileware.natural.cucumber.ui.internal.CucumberActivator { - + @Inject - public void setJavaElementChangeReporter(JavaElementChangeReporter reporter) { + public void setJavaElementChangeReporter(final JavaElementChangeReporter reporter) { // Listen to Java class changes JavaCore.addElementChangedListener(reporter, ElementChangedEvent.POST_CHANGE); } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberHyperlinkHelper.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberHyperlinkHelper.java index 9d40f76f..72ba4a3a 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberHyperlinkHelper.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberHyperlinkHelper.java @@ -5,11 +5,10 @@ import java.util.Collection; import java.util.List; -import org.agileware.natural.common.JavaAnnotationMatcher; -import org.agileware.natural.common.JavaHyperlink; import org.agileware.natural.cucumber.cucumber.Step; +import org.agileware.natural.stepmatcher.ui.JavaAnnotationMatcher; +import org.agileware.natural.stepmatcher.ui.JavaHyperlink; import org.eclipse.emf.ecore.EObject; -import org.eclipse.jdt.core.IMethod; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.xtext.nodemodel.INode; @@ -27,37 +26,34 @@ public class CucumberHyperlinkHelper extends HyperlinkHelper { @Inject private EObjectAtOffsetHelper helper; - + @Inject private JavaAnnotationMatcher matcher; @Override - public IHyperlink[] createHyperlinksByOffset(XtextResource resource, int offset, boolean createMultipleHyperlinks) { - IHyperlink[] defaults = super.createHyperlinksByOffset(resource, offset, createMultipleHyperlinks); - List hyperlinks = (defaults == null ? new ArrayList() : Arrays.asList(defaults)); + public IHyperlink[] createHyperlinksByOffset(final XtextResource resource, final int offset, + final boolean createMultipleHyperlinks) { + final IHyperlink[] defaults = super.createHyperlinksByOffset(resource, offset, createMultipleHyperlinks); + final List hyperlinks = (defaults == null ? new ArrayList() : Arrays.asList(defaults)); - EObject eObject = helper.resolveElementAt(resource, offset); + final EObject eObject = helper.resolveElementAt(resource, offset); if (eObject instanceof Step) { - IParseResult parseResult = resource.getParseResult(); + final IParseResult parseResult = resource.getParseResult(); INode node = NodeModelUtils.findLeafNodeAtOffset(parseResult.getRootNode(), offset); while (!(node instanceof CompositeNode && node.getSemanticElement() instanceof Step)) { node = node.getParent(); } - String description = ((Step) eObject).getDescription(); - hyperlinks.addAll(findLinkTargets(description, new Region(node.getOffset(), node.getText().trim().length()))); + final String description = ((Step) eObject).getDescription(); + final Region region = new Region(node.getOffset(), node.getText().trim().length()); + hyperlinks.addAll(findLinkTargets(description, region)); } return hyperlinks.isEmpty() ? null : Iterables.toArray(hyperlinks, IHyperlink.class); } - private Collection findLinkTargets(String description, final Region region) { + private Collection findLinkTargets(final String description, final Region region) { final List results = new ArrayList(); - matcher.findMatches(description.trim(), new JavaAnnotationMatcher.Command() { - - public void match(String annotationValue, IMethod method) { - results.add(new JavaHyperlink("Open definition " + annotationValue, method, region)); - - } - }); + matcher.findMatches(description.trim(), (annotationValue, method) -> results + .add(new JavaHyperlink("Open definition " + annotationValue, method, region))); return results; } } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberUiModule.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberUiModule.java index 3d453f46..a71ab803 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberUiModule.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/CucumberUiModule.java @@ -6,24 +6,25 @@ import org.agileware.natural.cucumber.ui.syntaxcoloring.HighlightingConfiguration; import org.agileware.natural.cucumber.ui.syntaxcoloring.LexicalHighlightingCalculator; import org.agileware.natural.cucumber.ui.syntaxcoloring.SemanticHighlightingCalculator; +import org.agileware.natural.stepmatcher.IStepMatcher; +import org.agileware.natural.stepmatcher.ui.AbstractAnnotationDescriptor; +import org.agileware.natural.stepmatcher.ui.JavaAnnotationMatcher; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.xtext.builder.IXtextBuilderParticipant; +import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator; import org.eclipse.xtext.ui.editor.hyperlinking.IHyperlinkHelper; import org.eclipse.xtext.ui.editor.syntaxcoloring.AbstractAntlrTokenToAttributeIdMapper; import org.eclipse.xtext.ui.editor.syntaxcoloring.IHighlightingConfiguration; -import org.eclipse.xtext.ui.editor.syntaxcoloring.ISemanticHighlightingCalculator; /** * Use this class to register components to be used within the IDE. */ public class CucumberUiModule extends AbstractCucumberUiModule { - - public CucumberUiModule(AbstractUIPlugin plugin) { + + public CucumberUiModule(final AbstractUIPlugin plugin) { super(plugin); } - - // TODO requires Xbase - // @Override + public Class bindIHyperlinkHelper() { return CucumberHyperlinkHelper.class; } @@ -35,7 +36,7 @@ public Class bindIHighlightingConfiguratio public Class bindAbstractAntlrTokenToAttributeIdMapper() { return LexicalHighlightingCalculator.class; } - + public Class bindISemanticHighlightingCalculator() { return SemanticHighlightingCalculator.class; } @@ -44,4 +45,28 @@ public Class bindISemanticHighlightin public Class bindIXtextBuilderParticipant() { return BuilderParticipant.class; } + + public Class bindIStepMatcher() { + return JavaAnnotationMatcher.class; + } + + public final static String[] STEPS = { "Given", "When", "Then", "And", "But" }; + private static final String CUCUMBER_PACKAGE = "io.cucumber.java.en"; + + public Class bindAnnotationDescriptor() { + return CucumberAnnotationDescriptor.class; + } + + public static class CucumberAnnotationDescriptor extends AbstractAnnotationDescriptor { + + @Override + public String[] getNames() { + return STEPS; + } + + @Override + public String getPackage() { + return CUCUMBER_PACKAGE; + } + } } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/contentassist/CucumberProposalProvider.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/contentassist/CucumberProposalProvider.java index 98c8e4f7..66bfd366 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/contentassist/CucumberProposalProvider.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/contentassist/CucumberProposalProvider.java @@ -5,8 +5,8 @@ import java.util.Collection; -import org.agileware.natural.common.AbstractAnnotationDescriptor; -import org.agileware.natural.common.JavaAnnotationMatcher; +import org.agileware.natural.stepmatcher.ui.AbstractAnnotationDescriptor; +import org.agileware.natural.stepmatcher.ui.JavaAnnotationMatcher; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.impl.RuleCallImpl; @@ -16,28 +16,34 @@ import com.google.inject.Inject; /** - * see http://www.eclipse.org/Xtext/documentation/latest/xtext.html#contentAssist on how to customize content assistant + * see + * http://www.eclipse.org/Xtext/documentation/latest/xtext.html#contentAssist on + * how to customize content assistant */ public class CucumberProposalProvider extends AbstractCucumberProposalProvider { @Inject private JavaAnnotationMatcher matcher; - + @Inject private AbstractAnnotationDescriptor descriptor; - - public void complete_Step(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { - if (((RuleCallImpl)context.getLastCompleteNode().getGrammarElement()).getRule().getName().equals("EOL") && context.getPrefix().length() == 0) { - for (String entry : descriptor.getNames()) { + + @Override + public void complete_Step(final EObject model, final RuleCall ruleCall, final ContentAssistContext context, + final ICompletionProposalAcceptor acceptor) { + if (((RuleCallImpl) context.getLastCompleteNode().getGrammarElement()).getRule().getName().equals("NL") + && context.getPrefix().length() == 0) { + for (final String entry : descriptor.getNames()) { acceptor.accept(createCompletionProposal(entry + " ", context)); } } } - - public void complete_StepDescription(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { - Collection proposals = matcher.findProposals(); + + public void complete_StepDescription(final EObject model, final RuleCall ruleCall, + final ContentAssistContext context, final ICompletionProposalAcceptor acceptor) { + final Collection proposals = matcher.findProposals(); for (String proposal : proposals) { - String display = proposal; + final String display = proposal; if (proposal.charAt(0) == '^') { proposal = proposal.substring(1); } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/labeling/CucumberLabelProvider.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/labeling/CucumberLabelProvider.java index 36f7ea31..7cd387b8 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/labeling/CucumberLabelProvider.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/labeling/CucumberLabelProvider.java @@ -4,15 +4,15 @@ package org.agileware.natural.cucumber.ui.labeling; import org.agileware.natural.cucumber.cucumber.Background; -import org.agileware.natural.cucumber.cucumber.DocString; import org.agileware.natural.cucumber.cucumber.Example; import org.agileware.natural.cucumber.cucumber.Feature; import org.agileware.natural.cucumber.cucumber.Scenario; import org.agileware.natural.cucumber.cucumber.ScenarioOutline; import org.agileware.natural.cucumber.cucumber.Step; -import org.agileware.natural.cucumber.cucumber.Table; -import org.agileware.natural.cucumber.cucumber.Tag; -import org.eclipse.emf.common.util.EList; +import org.agileware.natural.lang.model.DocString; +import org.agileware.natural.lang.model.Meta; +import org.agileware.natural.lang.model.MetaElement; +import org.agileware.natural.lang.model.Table; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.xtext.ui.label.DefaultEObjectLabelProvider; @@ -20,92 +20,96 @@ /** * Provides labels for a EObjects. - * + * * see * http://www.eclipse.org/Xtext/documentation/latest/xtext.html#labelProvider */ public class CucumberLabelProvider extends DefaultEObjectLabelProvider { @Inject - public CucumberLabelProvider(AdapterFactoryLabelProvider delegate) { + public CucumberLabelProvider(final AdapterFactoryLabelProvider delegate) { super(delegate); } - - String text(Feature ele) { - return ele.getTitle(); + + String text(final Feature ele) { + return ele.getTitle() == null ? "Feature" : merge("Feature: ", ele.getTitle()); } - String image(Feature ele) { + String image(final Feature ele) { return "feature.png"; } - String text(Background ele) { - return ele.getTitle() == null ? "Background" : ele.getTitle(); + String text(final Background ele) { + return ele.getTitle() == null ? "Background" : merge("Background: ", ele.getTitle()); } - String image(Background ele) { + String image(final Background ele) { return "background.gif"; } - String text(Scenario ele) { - return ele.getTitle() == null ? "Scenario" : ele.getTitle(); + String text(final Scenario ele) { + return ele.getTitle() == null ? "Scenario" : merge("Scenario: ", ele.getTitle()); } - String image(Scenario ele) { + String image(final Scenario ele) { return "scenario.png"; } - String text(ScenarioOutline ele) { - return ele.getTitle() == null ? "Scenario Outline" : ele.getTitle(); + String text(final ScenarioOutline ele) { + return ele.getTitle() == null ? "Scenario Outline" : merge("Scenario Outline: ", ele.getTitle()); } - String image(ScenarioOutline ele) { + String image(final ScenarioOutline ele) { return "scenario_outline.png"; } - - String text(Step ele) { - return ele.getDescription().trim(); + + String text(final Step ele) { + return ele.getDescription(); } - String image(Step ele) { + String image(final Step ele) { return "step.gif"; } - String text(Table ele) { + String text(final Table ele) { return "Table of " + ele.getRows().size() + " rows"; } - String image(Table ele) { + String image(final Table ele) { return "table.gif"; } - - String text(DocString ele) { + + String text(final DocString ele) { return "DocString"; } - String image(DocString ele) { + String image(final DocString ele) { return "code.gif"; } - String text(Example ele) { - return ele.getTitle().isEmpty() ? "Examples" : ele.getTitle(); + String text(final Example ele) { + return ele.getTitle() == null ? "Examples" : merge("Examples: ", ele.getTitle()); } - String image(Example ele) { + String image(final Example ele) { return "example.gif"; } - String text(Tag ele) { - return ele.getId(); + String text(final Meta ele) { + return "Meta"; } - String image(Tag ele) { + String text(final MetaElement ele) { + return ele.getId().substring(1); + } + + String image(final MetaElement ele) { return "annotation.gif"; } - - private static String merge(EList strings) { - StringBuilder builder = new StringBuilder(); - for (String string : strings) { + + private static String merge(final String... strings) { + final StringBuilder builder = new StringBuilder(); + for (final String string : strings) { builder.append(string); } return builder.toString(); diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/outline/CucumberOutlineTreeProvider.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/outline/CucumberOutlineTreeProvider.java index 44d88c44..c2a3cc94 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/outline/CucumberOutlineTreeProvider.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/outline/CucumberOutlineTreeProvider.java @@ -3,33 +3,79 @@ */ package org.agileware.natural.cucumber.ui.outline; -import org.agileware.natural.cucumber.cucumber.DocString; +import org.agileware.natural.cucumber.cucumber.AbstractScenario; +import org.agileware.natural.cucumber.cucumber.CucumberModel; +import org.agileware.natural.cucumber.cucumber.Example; +import org.agileware.natural.cucumber.cucumber.Feature; +import org.agileware.natural.cucumber.cucumber.ScenarioOutline; import org.agileware.natural.cucumber.cucumber.Step; -import org.agileware.natural.cucumber.cucumber.Table; -import org.agileware.natural.cucumber.cucumber.Text; +import org.agileware.natural.lang.model.DocString; +import org.agileware.natural.lang.model.Narrative; +import org.agileware.natural.lang.model.Table; import org.eclipse.xtext.ui.editor.outline.impl.DefaultOutlineTreeProvider; +import org.eclipse.xtext.ui.editor.outline.impl.DocumentRootNode; /** * customization of the default outline structure - * + * */ public class CucumberOutlineTreeProvider extends DefaultOutlineTreeProvider { - - protected boolean _isLeaf(DocString modelElement) { + + protected void _createChildren(final DocumentRootNode parentNode, final CucumberModel model) { + if (model.getDocument() != null) { + createNode(parentNode, model.getDocument()); + } + } + + protected void _createChildren(final DocumentRootNode parentNode, final Feature model) { + if (model.getMeta() != null) { + createNode(parentNode, model.getMeta()); + } + + for (final AbstractScenario scenario : model.getScenarios()) { + createNode(parentNode, scenario); + } + } + + protected void _createChildren(final DocumentRootNode parentNode, final AbstractScenario model) { + if (model.getMeta() != null) { + createNode(parentNode, model.getMeta()); + } + + for (final Step step : model.getSteps()) { + createNode(parentNode, step); + } + } + + protected void _createChildren(final DocumentRootNode parentNode, final ScenarioOutline model) { + if (model.getMeta() != null) { + createNode(parentNode, model.getMeta()); + } + + for (final Step step : model.getSteps()) { + createNode(parentNode, step); + } + + for (final Example step : model.getExamples()) { + createNode(parentNode, step); + } + } + + protected boolean _isLeaf(final Narrative modelElement) { return true; } - - protected boolean _isLeaf(Text modelElement) { + + protected boolean _isLeaf(final DocString modelElement) { return true; } - - protected boolean _isLeaf(Table modelElement) { + + protected boolean _isLeaf(final Table modelElement) { // do not allow expansion of table nodes return true; } - - protected boolean _isLeaf(Step modelElement) { + + protected boolean _isLeaf(final Step modelElement) { // only allow expansion of step nodes with tables - return modelElement.getTable() == null && modelElement.getCode() == null; + return modelElement.getTable() == null && modelElement.getText() == null; } } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/HighlightingConfiguration.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/HighlightingConfiguration.java index 29154a3f..5f2154f5 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/HighlightingConfiguration.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/HighlightingConfiguration.java @@ -2,85 +2,56 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.RGB; -import org.eclipse.xtext.ui.editor.syntaxcoloring.IHighlightingConfiguration; +import org.eclipse.xtext.ui.editor.syntaxcoloring.DefaultHighlightingConfiguration; import org.eclipse.xtext.ui.editor.syntaxcoloring.IHighlightingConfigurationAcceptor; import org.eclipse.xtext.ui.editor.utils.TextStyle; -public class HighlightingConfiguration implements IHighlightingConfiguration { +public class HighlightingConfiguration extends DefaultHighlightingConfiguration { - public static final String STEP_KEYWORD = "stepCondition"; - public static final String TAG = "tag"; - public static final String TABLE = "table"; - public static final String PLACEHOLDER = "placeholder"; - public static final String DOC_STRING = "docstring"; - public static final String KEYWORD_ID = "keyword"; - public static final String COMMENT_ID = "comment"; - public static final String STRING_ID = "string"; - public static final String NUMBER_ID = "number"; - public static final String DEFAULT_ID = "default"; - public static final String INVALID_TOKEN_ID = "error"; + public static final String TAG_ID = "tag"; + public static final String TABLE_ID = "table"; + + public static final String PLACEHOLDER_ID = "placeholder"; + + public static final String DOC_STRING_ID = "docstring"; + + @Override public void configure(IHighlightingConfigurationAcceptor acceptor) { - acceptor.acceptDefaultHighlighting(KEYWORD_ID, "Keyword", keywordTextStyle()); - acceptor.acceptDefaultHighlighting(COMMENT_ID, "Comment", commentTextStyle()); - acceptor.acceptDefaultHighlighting(STRING_ID, "String", stringTextStyle()); - acceptor.acceptDefaultHighlighting(NUMBER_ID, "Number", numberTextStyle()); - acceptor.acceptDefaultHighlighting(DEFAULT_ID, "Default", defaultTextStyle()); - acceptor.acceptDefaultHighlighting(INVALID_TOKEN_ID, "Invalid Symbol", errorTextStyle()); - // Gherkin - acceptor.acceptDefaultHighlighting(STEP_KEYWORD, "Step Condition", stepKeywordTextStyle()); - acceptor.acceptDefaultHighlighting(TAG, "Tag", tagTextStyle()); - acceptor.acceptDefaultHighlighting(TABLE, "Table", numberTextStyle()); - acceptor.acceptDefaultHighlighting(PLACEHOLDER, "Placeholder", tagTextStyle()); - acceptor.acceptDefaultHighlighting(DOC_STRING, "Doc String", numberTextStyle()); - } + acceptor.acceptDefaultHighlighting(TAG_ID, "Tag", tagTextStyle()); + acceptor.acceptDefaultHighlighting(TABLE_ID, "Table", tableTextStyle()); + acceptor.acceptDefaultHighlighting(PLACEHOLDER_ID, "Placeholder", placeholderTextStyle()); + acceptor.acceptDefaultHighlighting(DOC_STRING_ID, "DocString", docStringTextStyle()); - public static TextStyle defaultTextStyle() { - TextStyle textStyle = new TextStyle(); - // textStyle.setBackgroundColor(new RGB(255, 255, 255)); - textStyle.setColor(new RGB(0, 0, 0)); - return textStyle; + super.configure(acceptor); } - public static TextStyle errorTextStyle() { - TextStyle textStyle = defaultTextStyle().copy(); - // textStyle.setColor(new RGB(255, 0, 0)); + @Override + public TextStyle numberTextStyle() { + TextStyle textStyle = stringTextStyle().copy(); return textStyle; } - - public static TextStyle numberTextStyle() { - TextStyle textStyle = defaultTextStyle().copy(); - textStyle.setColor(new RGB(125, 125, 125)); + + public TextStyle placeholderTextStyle() { + TextStyle textStyle = stringTextStyle().copy(); return textStyle; } - - public static TextStyle stringTextStyle() { + + public TextStyle docStringTextStyle() { TextStyle textStyle = defaultTextStyle().copy(); - textStyle.setColor(new RGB(42, 0, 255)); + textStyle.setColor(new RGB(125, 125, 125)); return textStyle; } - public static TextStyle commentTextStyle() { + public TextStyle tableTextStyle() { TextStyle textStyle = defaultTextStyle().copy(); - textStyle.setColor(new RGB(63, 127, 95)); + textStyle.setColor(new RGB(125, 125, 125)); return textStyle; } - public static TextStyle keywordTextStyle() { + public TextStyle tagTextStyle() { TextStyle textStyle = defaultTextStyle().copy(); - textStyle.setColor(new RGB(127, 0, 85)); - textStyle.setStyle(SWT.BOLD); - return textStyle; - } - - public static TextStyle stepKeywordTextStyle() { - TextStyle textStyle = keywordTextStyle(); - textStyle.setStyle(SWT.ITALIC); - return textStyle; - } - - public static TextStyle tagTextStyle() { - TextStyle textStyle = numberTextStyle(); + textStyle.setColor(new RGB(125, 125, 125)); textStyle.setStyle(SWT.ITALIC); return textStyle; } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/LexicalHighlightingCalculator.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/LexicalHighlightingCalculator.java index 9e726295..45c44a9d 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/LexicalHighlightingCalculator.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/LexicalHighlightingCalculator.java @@ -2,38 +2,46 @@ import java.util.regex.Pattern; +import org.eclipse.xtext.ide.editor.syntaxcoloring.HighlightingStyles; import org.eclipse.xtext.ui.editor.syntaxcoloring.AbstractAntlrTokenToAttributeIdMapper; -import org.eclipse.xtext.ui.editor.syntaxcoloring.DefaultHighlightingConfiguration; -public class LexicalHighlightingCalculator extends - AbstractAntlrTokenToAttributeIdMapper { +public class LexicalHighlightingCalculator extends AbstractAntlrTokenToAttributeIdMapper { private static final Pattern QUOTED = Pattern.compile("(?:^'([^']*)'$)|(?:^\"([^\"]*)\")$", Pattern.MULTILINE); - + @Override - protected String calculateId(String tokenName, int tokenType) { - if(QUOTED.matcher(tokenName).matches()) { - return DefaultHighlightingConfiguration.KEYWORD_ID; + protected String calculateId(final String tokenName, final int tokenType) { + if ("'|'".equals(tokenName)) { + return HighlightingConfiguration.TABLE_ID; } - if("RULE_STRING".equals(tokenName)) { - return DefaultHighlightingConfiguration.STRING_ID; + if (QUOTED.matcher(tokenName).matches()) { + return HighlightingStyles.KEYWORD_ID; } - if("RULE_INT".equals(tokenName)) { - return DefaultHighlightingConfiguration.NUMBER_ID; + if ("RULE_STRING".equals(tokenName)) { + return HighlightingStyles.STRING_ID; } - if("RULE_SL_COMMENT".equals(tokenName)) { - return DefaultHighlightingConfiguration.COMMENT_ID; + if ("RULE_NUMBER".equals(tokenName)) { + return HighlightingStyles.NUMBER_ID; } - if("RULE_TAGNAME".equals(tokenName)) { - return HighlightingConfiguration.TAG; + if ("RULE_SL_COMMENT".equals(tokenName)) { + return HighlightingStyles.COMMENT_ID; } - if("RULE_DOC_STRING".equals(tokenName)) { - return HighlightingConfiguration.DOC_STRING; + if ("RULE_TAG".equals(tokenName)) { + return HighlightingConfiguration.TAG_ID; } - if("RULE_TABLE_ROW".equals(tokenName)) { - return HighlightingConfiguration.TABLE; + if ("RULE_DOC_STRING_LITERAL".equals(tokenName)) { + return HighlightingConfiguration.DOC_STRING_ID; + } + if ("RULE_TABLE_CELL".equals(tokenName)) { + return HighlightingConfiguration.TABLE_ID; + } + if ("RULE_STEP_KEYWORD".equals(tokenName)) { + return HighlightingStyles.KEYWORD_ID; + } + if ("RULE_PLACEHOLDER".equals(tokenName)) { + return HighlightingConfiguration.PLACEHOLDER_ID; } - return DefaultHighlightingConfiguration.DEFAULT_ID; - } + return HighlightingStyles.DEFAULT_ID; + } } diff --git a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/SemanticHighlightingCalculator.java b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/SemanticHighlightingCalculator.java index 78d84bad..2ab7408c 100644 --- a/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/SemanticHighlightingCalculator.java +++ b/org.agileware.natural.cucumber.ui/src/org/agileware/natural/cucumber/ui/syntaxcoloring/SemanticHighlightingCalculator.java @@ -1,58 +1,91 @@ package org.agileware.natural.cucumber.ui.syntaxcoloring; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.ABSTRACT_SCENARIO__TITLE; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.EXAMPLE__TITLE; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.FEATURE__TITLE; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.STEP__DESCRIPTION; +import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature; + +import java.util.regex.Pattern; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.agileware.natural.cucumber.cucumber.AbstractScenario; +import org.agileware.natural.cucumber.cucumber.CucumberModel; +import org.agileware.natural.cucumber.cucumber.Example; import org.agileware.natural.cucumber.cucumber.Feature; -import org.agileware.natural.cucumber.cucumber.Scenario; import org.agileware.natural.cucumber.cucumber.ScenarioOutline; import org.agileware.natural.cucumber.cucumber.Step; -import org.eclipse.emf.common.util.EList; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor; +import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator; +import org.eclipse.xtext.nodemodel.ILeafNode; import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.ui.editor.syntaxcoloring.IHighlightedPositionAcceptor; -import org.eclipse.xtext.ui.editor.syntaxcoloring.ISemanticHighlightingCalculator; +import org.eclipse.xtext.util.CancelIndicator; public class SemanticHighlightingCalculator implements ISemanticHighlightingCalculator { - - public void provideHighlightingFor(XtextResource resource, IHighlightedPositionAcceptor acceptor) { - if (resource == null || resource.getParseResult() == null || resource.getContents().size() <= 0) { + + private static final Pattern NUMBER_PATTERN = Pattern.compile("(0)|([1-9][0-9]*)|(0x[0-9a-fA-F]+)"); + + private static Stream allLeafNodesFor(final EObject semanticObject, + final EStructuralFeature structuralFeature) { + return findNodesForFeature(semanticObject, structuralFeature) + .stream() + .flatMap(n -> StreamSupport.stream(n.getLeafNodes().spliterator(), false)) + .filter(n -> !n.isHidden()); + } + + @Override + public void provideHighlightingFor(final XtextResource resource, final IHighlightedPositionAcceptor acceptor, + CancelIndicator cancelIndicator) { + if (resource == null || resource.getParseResult() == null || resource.getContents().size() <= 0 + || cancelIndicator.isCanceled()) { return; } - Feature feature = (Feature) resource.getContents().get(0); - if (feature.getBackground() != null) { - provideHighlightingForSteps(feature.getBackground().getSteps(), acceptor); + + final CucumberModel model = (CucumberModel) resource.getContents().get(0); + if (model.getDocument() != null) { + provideHighlightingFor(model.getDocument(), acceptor); } - for (Object child : feature.getScenarios()) { - if (child instanceof Scenario) { - Scenario scenario = (Scenario) child; - provideHighlightingForSteps(scenario.getSteps(), acceptor); - } - if (child instanceof ScenarioOutline) { - ScenarioOutline outline = (ScenarioOutline) child; - provideHighlightingForSteps(outline.getSteps(), acceptor); - } + } + + private void provideHighlightingFor(final Feature feature, final IHighlightedPositionAcceptor acceptor) { + provideHighlightingForTextLiterals(allLeafNodesFor(feature, FEATURE__TITLE), acceptor); + for (final AbstractScenario scenario : feature.getScenarios()) { + provideHighlightingFor(scenario, acceptor); } } - private void provideHighlightingForSteps(EList steps, IHighlightedPositionAcceptor acceptor) { - for (Step step : steps) { - INode node = NodeModelUtils.getNode(step); - acceptor.addPosition( - node.getOffset(), - node.getText().trim().indexOf(" "), - HighlightingConfiguration.STEP_KEYWORD); - if (step.eContainer() instanceof ScenarioOutline && step.getDescription() != null) { - this.provideHighlightingForPlaceholders(node.getText(), node, 0, acceptor); + private void provideHighlightingFor(final AbstractScenario scenario, final IHighlightedPositionAcceptor acceptor) { + provideHighlightingForTextLiterals(allLeafNodesFor(scenario, ABSTRACT_SCENARIO__TITLE), acceptor); + for (final Step step : scenario.getSteps()) { + provideHighlightingFor(step, acceptor); + if (scenario instanceof ScenarioOutline) { + for (final Example example : ((ScenarioOutline) scenario).getExamples()) { + provideHighlightingFor(example, acceptor); + } } } } - private void provideHighlightingForPlaceholders(String description, INode node, int current, IHighlightedPositionAcceptor acceptor) { - int start = description.indexOf('<', current); - int stop = description.indexOf('>', start); - if (start > 0 && stop > 0 && description.charAt(start + 1) != ' ') { - acceptor.addPosition(node.getTotalOffset() + start, stop - start + 1, HighlightingConfiguration.PLACEHOLDER); - this.provideHighlightingForPlaceholders(description, node, stop + 1, acceptor); - } + private void provideHighlightingFor(final Example example, final IHighlightedPositionAcceptor acceptor) { + provideHighlightingForTextLiterals(allLeafNodesFor(example, EXAMPLE__TITLE), acceptor); } + private void provideHighlightingFor(final Step step, final IHighlightedPositionAcceptor acceptor) { + provideHighlightingForTextLiterals(allLeafNodesFor(step, STEP__DESCRIPTION), acceptor); + } + + private void provideHighlightingForTextLiterals(final Stream leafNodes, + final IHighlightedPositionAcceptor acceptor) { + leafNodes.forEach(node -> { + // Temporary work around for LexicalHighlighter failing to flag certain numbers + // forms + if (NUMBER_PATTERN.matcher(node.getText()).matches()) { + acceptor.addPosition(node.getOffset(), node.getLength(), + HighlightingConfiguration.NUMBER_ID); + } + }); + } } diff --git a/org.agileware.natural.cucumber.web/.settings/org.eclipse.wst.common.component b/org.agileware.natural.cucumber.web/.settings/org.eclipse.wst.common.component index 24bc4761..4f85fa10 100644 --- a/org.agileware.natural.cucumber.web/.settings/org.eclipse.wst.common.component +++ b/org.agileware.natural.cucumber.web/.settings/org.eclipse.wst.common.component @@ -1,40 +1,46 @@ - + + - + + - + + - + + - + + - + + @@ -47,28 +53,32 @@ uses - + + - + + - + + - + + diff --git a/org.agileware.natural.cucumber.web/WebRoot/xtext-resources/generated/mode-feature.js b/org.agileware.natural.cucumber.web/WebRoot/xtext-resources/generated/mode-feature.js index 640527bf..ca984bfb 100644 --- a/org.agileware.natural.cucumber.web/WebRoot/xtext-resources/generated/mode-feature.js +++ b/org.agileware.natural.cucumber.web/WebRoot/xtext-resources/generated/mode-feature.js @@ -1,10 +1,6 @@ define(["ace/lib/oop", "ace/mode/text", "ace/mode/text_highlight_rules"], function(oop, mText, mTextHighlightRules) { var HighlightRules = function() { - var keywords = "Scenario"; this.$rules = { - "start": [ - {token: "keyword", regex: "\\b(?:" + keywords + ")\\b"} - ] }; }; oop.inherits(HighlightRules, mTextHighlightRules.TextHighlightRules); diff --git a/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/CucumberServlet.java b/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/CucumberServlet.java new file mode 100644 index 00000000..d6de568c --- /dev/null +++ b/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/CucumberServlet.java @@ -0,0 +1,34 @@ +/* + * generated by Xtext 2.22.0 + */ +package org.agileware.natural.cucumber.web; + +import com.google.inject.Injector; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import org.eclipse.xtext.util.DisposableRegistry; +import org.eclipse.xtext.web.servlet.XtextServlet; + +/** + * Deploy this class into a servlet container to enable DSL-specific services. + */ +@WebServlet(name = "XtextServices", urlPatterns = "/xtext-service/*") +public class CucumberServlet extends XtextServlet { + + DisposableRegistry disposableRegistry; + + public void init() throws ServletException { + super.init(); + Injector injector = new CucumberWebSetup().createInjectorAndDoEMFRegistration(); + this.disposableRegistry = injector.getInstance(DisposableRegistry.class); + } + + public void destroy() { + if (disposableRegistry != null) { + disposableRegistry.dispose(); + disposableRegistry = null; + } + super.destroy(); + } + +} diff --git a/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/CucumberServlet.xtend b/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/CucumberServlet.xtend deleted file mode 100644 index 24c50dd9..00000000 --- a/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/CucumberServlet.xtend +++ /dev/null @@ -1,32 +0,0 @@ -/* - * generated by Xtext 2.21.0 - */ -package org.agileware.natural.cucumber.web - -import javax.servlet.annotation.WebServlet -import org.eclipse.xtext.util.DisposableRegistry -import org.eclipse.xtext.web.servlet.XtextServlet - -/** - * Deploy this class into a servlet container to enable DSL-specific services. - */ -@WebServlet(name = 'XtextServices', urlPatterns = '/xtext-service/*') -class CucumberServlet extends XtextServlet { - - DisposableRegistry disposableRegistry - - override init() { - super.init() - val injector = new CucumberWebSetup().createInjectorAndDoEMFRegistration() - disposableRegistry = injector.getInstance(DisposableRegistry) - } - - override destroy() { - if (disposableRegistry !== null) { - disposableRegistry.dispose() - disposableRegistry = null - } - super.destroy() - } - -} diff --git a/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/ServerLauncher.java b/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/ServerLauncher.java new file mode 100644 index 00000000..b101be14 --- /dev/null +++ b/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/ServerLauncher.java @@ -0,0 +1,65 @@ +/* + * generated by Xtext 2.22.0 + */ +package org.agileware.natural.cucumber.web; + +import java.net.InetSocketAddress; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.log.Slf4jLog; +import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.MetaInfConfiguration; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.WebInfConfiguration; +import org.eclipse.jetty.webapp.WebXmlConfiguration; + +/** + * This program starts an HTTP server for testing the web integration of your DSL. + * Just execute it and point a web browser to http://localhost:8080/ + */ +public class ServerLauncher { + public static void main(String[] args) { + Server server = new Server(new InetSocketAddress("localhost", 8080)); + WebAppContext ctx = new WebAppContext(); + ctx.setResourceBase("WebRoot"); + ctx.setWelcomeFiles(new String[] {"index.html"}); + ctx.setContextPath("/"); + ctx.setConfigurations(new Configuration[] { + new AnnotationConfiguration(), + new WebXmlConfiguration(), + new WebInfConfiguration(), + new MetaInfConfiguration() + }); + ctx.setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, + ".*/org\\.agileware\\.natural\\.cucumber\\.web/.*,.*\\.jar"); + ctx.setInitParameter("org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false"); + server.setHandler(ctx); + Slf4jLog log = new Slf4jLog(ServerLauncher.class.getName()); + try { + server.start(); + log.info("Server started " + server.getURI() + "..."); + new Thread() { + + public void run() { + try { + log.info("Press enter to stop the server..."); + int key = System.in.read(); + if (key != -1) { + server.stop(); + } else { + log.warn( + "Console input is not available. In order to stop the server, you need to cancel process manually."); + } + } catch (Exception e) { + log.warn(e); + } + } + + }.start(); + server.join(); + } catch (Exception exception) { + log.warn(exception.getMessage()); + System.exit(1); + } + } +} diff --git a/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/ServerLauncher.xtend b/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/ServerLauncher.xtend deleted file mode 100644 index 3ef81055..00000000 --- a/org.agileware.natural.cucumber.web/src/org/agileware/natural/cucumber/web/ServerLauncher.xtend +++ /dev/null @@ -1,54 +0,0 @@ -/* - * generated by Xtext 2.21.0 - */ -package org.agileware.natural.cucumber.web - -import java.net.InetSocketAddress -import org.eclipse.jetty.annotations.AnnotationConfiguration -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.util.log.Slf4jLog -import org.eclipse.jetty.webapp.MetaInfConfiguration -import org.eclipse.jetty.webapp.WebAppContext -import org.eclipse.jetty.webapp.WebInfConfiguration -import org.eclipse.jetty.webapp.WebXmlConfiguration - -/** - * This program starts an HTTP server for testing the web integration of your DSL. - * Just execute it and point a web browser to http://localhost:8080/ - */ -class ServerLauncher { - def static void main(String[] args) { - val server = new Server(new InetSocketAddress('localhost', 8080)) - server.handler = new WebAppContext => [ - resourceBase = 'WebRoot' - welcomeFiles = #["index.html"] - contextPath = "/" - configurations = #[ - new AnnotationConfiguration, - new WebXmlConfiguration, - new WebInfConfiguration, - new MetaInfConfiguration - ] - setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, '.*/org\\.agileware\\.natural\\.cucumber\\.web/.*,.*\\.jar') - setInitParameter("org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false") - ] - val log = new Slf4jLog(ServerLauncher.name) - try { - server.start - log.info('Server started ' + server.getURI + '...') - new Thread[ - log.info('Press enter to stop the server...') - val key = System.in.read - if (key != -1) { - server.stop - } else { - log.warn('Console input is not available. In order to stop the server, you need to cancel process manually.') - } - ].start - server.join - } catch (Exception exception) { - log.warn(exception.message) - System.exit(1) - } - } -} diff --git a/org.agileware.natural.cucumber/META-INF/MANIFEST.MF b/org.agileware.natural.cucumber/META-INF/MANIFEST.MF index a09a64d4..37a452f0 100644 --- a/org.agileware.natural.cucumber/META-INF/MANIFEST.MF +++ b/org.agileware.natural.cucumber/META-INF/MANIFEST.MF @@ -6,7 +6,8 @@ Bundle-Vendor: Roberto Lo Giacco Bundle-Version: 1.0.0.qualifier Bundle-SymbolicName: org.agileware.natural.cucumber; singleton:=true Bundle-ActivationPolicy: lazy -Require-Bundle: org.agileware.natural.common;visibility:=reexport, +Require-Bundle: org.agileware.natural.stepmatcher;visibility:=reexport, + org.agileware.natural.lang;visibility:=reexport, org.eclipse.xtext, org.eclipse.xtext.xbase, org.eclipse.equinox.common;bundle-version="3.5.0", @@ -18,11 +19,12 @@ Require-Bundle: org.agileware.natural.common;visibility:=reexport, org.antlr.runtime;bundle-version="[3.2.0,3.2.1)" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: org.agileware.natural.cucumber;uses:="com.google.inject,org.eclipse.xtext.service,org.eclipse.xtext", - org.agileware.natural.cucumber.cucumber;uses:="org.eclipse.emf.ecore,org.eclipse.emf.common.util", + org.agileware.natural.cucumber.cucumber;uses:="org.eclipse.emf.ecore,org.eclipse.emf.common.util,org.agileware.natural.lang.model", org.agileware.natural.cucumber.cucumber.impl; uses:="org.eclipse.emf.ecore, org.eclipse.emf.ecore.impl, org.eclipse.emf.common.util, + org.agileware.natural.lang.model, org.eclipse.emf.common.notify, org.agileware.natural.cucumber.cucumber", org.agileware.natural.cucumber.cucumber.util; @@ -31,7 +33,11 @@ Export-Package: org.agileware.natural.cucumber;uses:="com.google.inject,org.ecli org.eclipse.emf.common.notify.impl, org.eclipse.emf.ecore.util, org.agileware.natural.cucumber.cucumber", - org.agileware.natural.cucumber.formatting2;uses:="org.eclipse.xtext.formatting2,org.agileware.natural.cucumber.cucumber", + org.agileware.natural.cucumber.formatting2; + uses:="org.agileware.natural.lang.model, + org.eclipse.xtext.formatting2.regionaccess, + org.eclipse.xtext.formatting2, + org.agileware.natural.cucumber.cucumber", org.agileware.natural.cucumber.generator;uses:="org.eclipse.xtext.generator,org.eclipse.emf.ecore.resource", org.agileware.natural.cucumber.parser.antlr;uses:="org.eclipse.xtext.parser.antlr,org.agileware.natural.cucumber.parser.antlr.internal,org.agileware.natural.cucumber.services", org.agileware.natural.cucumber.parser.antlr.internal; @@ -39,8 +45,8 @@ Export-Package: org.agileware.natural.cucumber;uses:="com.google.inject,org.ecli org.eclipse.xtext.parser.antlr, org.antlr.runtime, org.agileware.natural.cucumber.services", - org.agileware.natural.cucumber.scoping;uses:="org.eclipse.xtext.scoping.impl", + org.agileware.natural.cucumber.scoping;uses:="org.agileware.natural.lang.scoping", org.agileware.natural.cucumber.serializer;uses:="org.agileware.natural.cucumber.cucumber", - org.agileware.natural.cucumber.services;uses:="org.eclipse.xtext.service,org.eclipse.xtext", - org.agileware.natural.cucumber.validation;uses:="org.eclipse.xtext.util,org.eclipse.xtext.validation" + org.agileware.natural.cucumber.services;uses:="org.agileware.natural.lang.services,org.eclipse.xtext.service,org.eclipse.xtext", + org.agileware.natural.cucumber.validation;uses:="org.agileware.natural.lang.validation,org.eclipse.xtext.util,org.eclipse.xtext.validation" Import-Package: org.apache.log4j diff --git a/org.agileware.natural.cucumber/README.md b/org.agileware.natural.cucumber/README.md index 21bbd4c2..4f34bef6 100644 --- a/org.agileware.natural.cucumber/README.md +++ b/org.agileware.natural.cucumber/README.md @@ -2,4 +2,4 @@ Xtext language model for Cucumber editor -![Cucumber AST](model/Cucumber.jpg) \ No newline at end of file +![Cucumber AST](cucumber.model.jpg) \ No newline at end of file diff --git a/org.agileware.natural.cucumber/cucumber.model.jpg b/org.agileware.natural.cucumber/cucumber.model.jpg new file mode 100644 index 00000000..64f8fd8e Binary files /dev/null and b/org.agileware.natural.cucumber/cucumber.model.jpg differ diff --git a/org.agileware.natural.cucumber/model/Cucumber.jpg b/org.agileware.natural.cucumber/model/Cucumber.jpg deleted file mode 100644 index ca56bbc0..00000000 Binary files a/org.agileware.natural.cucumber/model/Cucumber.jpg and /dev/null differ diff --git a/org.agileware.natural.cucumber/model/generated/Cucumber.ecore b/org.agileware.natural.cucumber/model/generated/Cucumber.ecore index 8001475b..c74753f4 100644 --- a/org.agileware.natural.cucumber/model/generated/Cucumber.ecore +++ b/org.agileware.natural.cucumber/model/generated/Cucumber.ecore @@ -2,23 +2,29 @@ - - + + + + + + - - - - - + - + + - + - - - - + - - - - - - + + - - - - - - - - - - - - - - - diff --git a/org.agileware.natural.cucumber/model/generated/Cucumber.genmodel b/org.agileware.natural.cucumber/model/generated/Cucumber.genmodel index fe8495cd..3cd12b86 100644 --- a/org.agileware.natural.cucumber/model/generated/Cucumber.genmodel +++ b/org.agileware.natural.cucumber/model/generated/Cucumber.genmodel @@ -1,23 +1,26 @@ + complianceLevel="8.0" copyrightFields="false" runtimeVersion="2.20" usedGenPackages="platform:/resource/org.agileware.natural.lang/model/generated/Natural.genmodel#//model"> + + + - + + + + + + - - - - - @@ -27,31 +30,13 @@ - + + + + - - - - - - - - - - - - - - - - - - - - - diff --git a/org.agileware.natural.cucumber/model/representations.aird b/org.agileware.natural.cucumber/model/representations.aird index 26b3a5ef..af85fc22 100644 --- a/org.agileware.natural.cucumber/model/representations.aird +++ b/org.agileware.natural.cucumber/model/representations.aird @@ -2,996 +2,558 @@ generated/Cucumber.ecore + platform:/resource/org.agileware.natural.lang/model/generated/Natural.ecore - + - - - - + + + + - + - + - - - - -
- - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + - - + + - - - - - - + + + + + + + + + + - - - + + + - - + + - - + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - + + + + + + + + + + + + + + - - + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
+ + + - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + + + + - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + + + + - - - - - + + + + + - + + + + + + + + + - - - - + + + + - + - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + - - - - - - - - - + - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + + + + - + - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + - + - + - + - + + + + + + + + + + + + + + + + + - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - labelSize - labelColor - - - labelSize - labelColor - - - - - - - - - + + + + + labelSize - labelColor - + labelSize - labelColor - + - - + + labelSize - labelColor - + labelSize - labelColor - + - - + + labelSize - labelColor - + labelSize - labelColor - + - - - labelSize - labelColor - - - labelSize - labelColor - - - - - - - - - - labelColor + + labelSize - - labelColor + labelSize - - - - - - labelSize - labelColor - - - labelSize - labelColor - - - - - - - - - - labelSize - labelColor - - - labelSize - labelColor - - - - - - - - - - - italic - - - - - - - - - - - - italic - - - - - - + - + - + italic - + - - - - - - - italic - - - - - - - - - - - - italic - - - - - - + - + - + italic - + - - - - + + + + - + italic - + - - - - - - - - - - - - - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - - - labelColor - labelSize - - - labelColor - labelSize - - - - - - - - - - labelColor - labelSize - - - labelColor - labelSize - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - - - labelColor - labelSize - - - labelColor - labelSize - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - - - - - - - - - - - labelColor - labelSize - - - labelColor - labelSize - - - - - + diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/Cucumber.xtext b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/Cucumber.xtext index 02884ccb..eb0729ac 100644 --- a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/Cucumber.xtext +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/Cucumber.xtext @@ -1,201 +1,117 @@ -grammar org.agileware.natural.cucumber.Cucumber hidden(WS, SL_COMMENT) +grammar org.agileware.natural.cucumber.Cucumber with org.agileware.natural.lang.Natural import "http://www.eclipse.org/emf/2002/Ecore" as ecore generate cucumber "http://www.agileware.org/natural/cucumber" -Feature: - tags+=Tag* - 'Feature:' - title=Title EOL+ - narrative=Narrative? - background=Background? - scenarios+=AbstractScenario+ +/** + * + */ +CucumberModel: {CucumberModel} + BLANK_SPACE? + document=Feature? +; + +/** + * + */ +Feature: {Feature} + (meta=Meta BLANK_SPACE?)? + 'Feature:' title=RawText? NL + (BLANK_SPACE? narrative=Narrative)? + (BLANK_SPACE? scenarios+=AbstractScenario)* + BLANK_SPACE? ; /** * Base class for Scenario implementations. - * A Scenario is any type with the following structure: + * An AbstractScenario is any type with the following structure: * - * tags+=Tag* - * 'Scenario:' - * title=Title? EOL+ + * meta=Meta? + * 'Keyword:' title=Title? NL * narrative=Narrative? - * steps+=Step+; + * steps+=Step* */ AbstractScenario: Scenario | ScenarioOutline | Background ; - /** - * A Section is a generic element with the structure: * - * tags+=Tag* - * 'Keyword:' - * title=Title? EOL+ - * narrative=Narrative? */ -Section: Feature - | Background - | AbstractScenario - | Example -; - -Background: - tags+=Tag* - 'Background:' - title=Title? EOL+ - narrative=Narrative? - steps+=Step+ -; - -Scenario: - tags+=Tag* - 'Scenario:' - title=Title? EOL+ - narrative=Narrative? - steps+=Step+ -; - -ScenarioOutline: - tags+=Tag* - 'Scenario' 'Outline:' - title=Title? EOL+ - narrative=Narrative? - steps+=Step+ - examples+=Example+ -; - -Step: - keyword=STEP_KEYWORD - description=StepDescription EOL* - (table=Table | code=DocString)? -; - -Example: - tags+=Tag* - 'Examples:' - title=Title? EOL+ - narrative=Narrative? - table=Table -; - - -Table: {Table} - rows+=TableRow+ - EOL* +Background: {Background} + (meta=Meta BLANK_SPACE?)? + 'Background:' title=RawText? NL + (BLANK_SPACE? narrative=Narrative)? + (BLANK_SPACE? steps+=Step)* ; -TableRow: {TableRow} - cols+=TableCol+ '|' EOL -; - -TableCol: {TableCol} - cell=TABLE_CELL -; - -DocString: {DocString} - ('"""' EOL -> text=Text '"""') - // TODO alternate quote support in formatter - // | ("'''" EOL -> text=Text? "'''") - EOL* +/** + * + */ +Scenario: {Scenario} + (meta=Meta BLANK_SPACE?)? + 'Scenario:' title=RawText? NL + (BLANK_SPACE? narrative=Narrative)? + (BLANK_SPACE? steps+=Step)* ; -Title: - (WORD | INT | STRING | PLACEHOLDER) - (WORD | INT | STRING | PLACEHOLDER | STEP_KEYWORD | TAGNAME)* +/** + * + */ +ScenarioOutline: {ScenarioOutline} + (meta=Meta BLANK_SPACE?)? + // note: Do not fix keyword warning (replace with terminal ok) + 'Scenario Outline:' title=RawText? NL + (BLANK_SPACE? narrative=Narrative)? + (BLANK_SPACE? steps+=Step)* + (BLANK_SPACE? examples+=Example)* ; -Narrative: - ( - (WORD | INT | STRING | PLACEHOLDER) - (WORD | INT | STRING | PLACEHOLDER | STEP_KEYWORD | TAGNAME)* - EOL+ - )+ +/** + * + */ +Step: {Step} + keyword=STEP_KEYWORD description=RawText NL + ((BLANK_SPACE? table=Table) | (BLANK_SPACE? text=DocString))? ; -StepDescription: - (WORD | INT | STRING | PLACEHOLDER | STEP_KEYWORD | TAGNAME)+ +terminal STEP_KEYWORD: + ('Given' | 'When' | 'Then' | 'And' | 'But' | '*') ; - -Tag: id=TAGNAME EOL?; - -Text: {Text} - lines+=TextLine* +terminal PLACEHOLDER: + ('<' !('>' | ' ' | '\t' | '\n' | '\r')+ '>') + | ('[' !(']' | ' ' | '\t' | '\n' | '\r')+ ']') ; -TextLine: {TextLine} - value=TEXT_VALUE EOL+ +/** + * + */ +Example: {Example} + (meta=Meta BLANK_SPACE?)? + 'Examples:' title=RawText? NL + (BLANK_SPACE? narrative=RawTextBlock NL)? + (BLANK_SPACE? table=Table) ; -TEXT_VALUE returns ecore::EString: - TEXT_LITERAL TEXT_LITERAL* -; -TEXT_LITERAL: WORD - | INT +@Override +StartLiteral: ID + | NUMBER | STRING + | PLACEHOLDER + | GLOB | ANY_OTHER ; -terminal INT: - '-'? - ('0'..'9')+ - ('.' ('0'..'9')+)? -; - -terminal STEP_KEYWORD: ('Given' | 'When' | 'Then' | 'And' | 'But') (' ' | '\t')+; - -terminal PLACEHOLDER: '<' !('>' | ' ' | '\t' | '\n' | '\r')+ '>'; - -terminal TABLE_CELL: '|' !('|' | '\n' | '\r')*; - -terminal STRING: - '"' ('\\' ('b' | 't' | 'n' | 'f' | 'r' | 'u' | '"' | "'" | '\\') | !('\\' | '"' | '\r' | '\n'))* '"' | - "'" ('\\' ('b' | 't' | 'n' | 'f' | 'r' | 'u' | '"' | "'" | '\\') | !('\\' | "'" | '\r' | '\n'))* "'"; - -terminal SL_COMMENT: '#' !('\n' | '\r')* NL; - -terminal TAGNAME: '@' !(' ' | '\t' | '\n' | '\r')+ ; - -terminal WORD: - !('@' | '|' | ' ' | '\t' | '\n' | '\r') - !(' ' | '\t' | '\n' | '\r')* -; - -terminal WS: (' ' | '\t'); - -terminal EOL: NL; -terminal fragment NL: ('\r'? '\n'?); - -terminal ANY_OTHER: .; - -// ---------------------------------------------------------- -// -// Unicode Ranges -// -// ---------------------------------------------------------- - -terminal fragment DIGIT: ASCII_DIGIT; - -terminal fragment LETTER: LATIN_ALPHABET; - -// Basic Latin -//// - -terminal fragment ASCII_SPACE: '\u0020'; - -terminal fragment ASCII_DIGIT: ('\u0030'..'\u0039'); - -// all ascii chars other than letters, digits, space, or control codes -terminal fragment ASCII_SYMBOLS: ('\u0021'..'\u002F') - | ('\u003A'..'\u0040') - | ('\u005B'..'\u0060') - | ('\u007B'..'\u007E') +@Override +Literal: ID + | NUMBER + | STRING + | PLACEHOLDER + | STEP_KEYWORD + | GLOB + | ANY_OTHER ; -terminal fragment LATIN_ALPHABET: ('\u0041'..'\u005A') - | ('\u0061'..'\u007A') -; diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/CucumberRuntimeModule.java b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/CucumberRuntimeModule.java index b443a9c9..d4b1c694 100644 --- a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/CucumberRuntimeModule.java +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/CucumberRuntimeModule.java @@ -3,30 +3,16 @@ */ package org.agileware.natural.cucumber; -import org.agileware.natural.common.AbstractAnnotationDescriptor; +import org.agileware.natural.stepmatcher.DefaultStepMatcher; +import org.agileware.natural.stepmatcher.IStepMatcher; /** - * Use this class to register components to be used at runtime / without the Equinox extension registry. + * Use this class to register components to be used at runtime / without the + * Equinox extension registry. */ public class CucumberRuntimeModule extends org.agileware.natural.cucumber.AbstractCucumberRuntimeModule { - public final static String[] STEPS = { "Given", "When", "Then", "And", "But" }; - private static final String CUCUMBER_PACKAGE = "io.cucumber.java.en"; - - - public Class bindAnnotationDescriptor() { - return CucumberAnnotationDescriptor.class; - } - - public static class CucumberAnnotationDescriptor extends AbstractAnnotationDescriptor { - - @Override - public String[] getNames() { - return STEPS; - } - @Override - public String getPackage() { - return CUCUMBER_PACKAGE; - } + public Class bindIStepMatcher() { + return DefaultStepMatcher.class; } } diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/GenerateCucumber.mwe2 b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/GenerateCucumber.mwe2 index 5309b2a6..e9f3e0d0 100644 --- a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/GenerateCucumber.mwe2 +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/GenerateCucumber.mwe2 @@ -7,7 +7,7 @@ import org.eclipse.xtext.xtext.generator.ui.codemining.CodeMiningFragment var rootPath = ".." Workflow { - + component = XtextGenerator { configuration = { project = StandardProjectConfig { @@ -37,13 +37,13 @@ Workflow { language = StandardLanguage { name = "org.agileware.natural.cucumber.Cucumber" fileExtensions = "feature" - + referencedResource = "platform:/resource/org.agileware.natural.lang/model/generated/Natural.genmodel" serializer = { generateStub = true } validator = { - // composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator" - // Generates checks for @Deprecated grammar annotations, an IssueProvider and a corresponding PropertyPage + // composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator" + // Generates checks for @Deprecated grammar annotations, an IssueProvider and a corresponding PropertyPage generateDeprecationValidation = true } formatter = { @@ -56,6 +56,9 @@ Workflow { } parserGenerator = { debugGrammar = true + options = auto-inject { + classSplitting = true + } } projectWizard = { generate = false @@ -66,7 +69,7 @@ Workflow { generate = false generateToolbarButton = false } - fragment = CodeMiningFragment { + fragment = CodeMiningFragment { generateStub = true generateXtendStub = false } diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/formatting2/CucumberFormatter.xtend b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/formatting2/CucumberFormatter.xtend index 39a74070..7f742205 100644 --- a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/formatting2/CucumberFormatter.xtend +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/formatting2/CucumberFormatter.xtend @@ -6,167 +6,173 @@ package org.agileware.natural.cucumber.formatting2 import com.google.inject.Inject import org.agileware.natural.cucumber.cucumber.AbstractScenario import org.agileware.natural.cucumber.cucumber.Background -import org.agileware.natural.cucumber.cucumber.DocString +import org.agileware.natural.cucumber.cucumber.CucumberModel import org.agileware.natural.cucumber.cucumber.Example import org.agileware.natural.cucumber.cucumber.Feature import org.agileware.natural.cucumber.cucumber.Scenario import org.agileware.natural.cucumber.cucumber.ScenarioOutline import org.agileware.natural.cucumber.cucumber.Step -import org.agileware.natural.cucumber.cucumber.Table -import org.agileware.natural.cucumber.cucumber.Tag import org.agileware.natural.cucumber.services.CucumberGrammarAccess +import org.agileware.natural.lang.formatting2.NaturalFormatHelper +import org.agileware.natural.lang.model.Block +import org.agileware.natural.lang.model.DocString +import org.agileware.natural.lang.model.Meta +import org.agileware.natural.lang.model.MetaElement +import org.agileware.natural.lang.model.Narrative +import org.agileware.natural.lang.model.Paragraph +import org.agileware.natural.lang.model.Table +import org.eclipse.xtext.Assignment +import org.eclipse.xtext.Keyword import org.eclipse.xtext.formatting2.AbstractFormatter2 +import org.eclipse.xtext.formatting2.FormatterRequest import org.eclipse.xtext.formatting2.IFormattableDocument import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion +import org.eclipse.xtext.util.Strings class CucumberFormatter extends AbstractFormatter2 { @Inject extension CucumberGrammarAccess cucumberGrammarAccess + @Inject NaturalFormatHelper.Factory formatHelperFactory + + var extension NaturalFormatHelper _formatHelper = null + + override protected initialize(FormatterRequest request) { + _formatHelper = formatHelperFactory.create(request.textRegionAccess, naturalGrammarAccess) + _formatHelper.initialize(request) + + super.initialize(request) + } + + def dispatch void format(CucumberModel model, extension IFormattableDocument doc) { + // println(textRegionAccess) + model.document.format() + // println(doc) + } + def dispatch void format(Feature model, extension IFormattableDocument doc) { - println(textRegionAccess) - // format tags - for (t : model.tags) { - t.format() + resetIndentation() + + // Condense all BLANK_SPACE regions into single line break + model.allRegionsFor.ruleCallsTo(BLANK_SPACERule).forEach [ region | + // println('''Trimming BLANK_SPACE: «region.offset» «region.length»''') + trimBlankSpace(region, 1, doc) + ] + + // Format Tags + model.meta.format() + + // Cleanup whitespace around keyword/title + if (model.title === null) { + model.regionFor.keyword(documentAccess.documentKeyword_3).append[noSpace] + } else { + model.regionFor.assignment(documentAccess.titleAssignment_4).prepend[oneSpace].append[noSpace] } - // format background - model.background.format() + increaseIndent() + indentBlock(model.startIndent, model.endIndent, doc) - // format scenarios - for (s : model.scenarios) { - s.format() + // Format narrative + if (model.narrative !== null) { + model.narrative.format().prepend[indent] + if (!model.narrative.hasLeadingBlankSpace) { + model.narrative.prepend[setNewLines(2)] + } } - println(doc) + // Format scenarios + model.scenarios.forEach[format().prepend[indent]] + + decreaseIndent() } def dispatch void format(Background model, extension IFormattableDocument doc) { - // format tags - for (t : model.tags) { - t.format() - } - - // align keyword to column 0 - model.regionFor - .keyword(backgroundAccess.backgroundKeyword_1) - .prepend[noIndentation] - - // Indent interior - val begin = model.regionFor.ruleCallTo(EOLRule) - val end = endRegionFor(model, doc) - interior(begin, end)[indent] - - // format steps - for (s : model.steps) { - s.prepend[indent] - s.format() - } + // Apply default scenario formatting + val keyword = backgroundAccess.backgroundKeyword_2 + val titleAssignment = backgroundAccess.titleAssignment_3 + formatScenarioBlock(model, keyword, titleAssignment, doc) } def dispatch void format(Scenario model, extension IFormattableDocument doc) { - // format tags - for (t : model.tags) { - t.format() - } - - // align keyword to column 0 - model.regionFor - .keyword(scenarioAccess.scenarioKeyword_1) - .prepend[noIndentation] - - // Indent interior - val begin = model.regionFor.ruleCallTo(EOLRule) - val end = endRegionFor(model, doc) - interior(begin, end)[indent] - - // format steps - for (s : model.steps) { - s.prepend[indent] - s.format() - } + // Apply default scenario formatting + val keyword = scenarioAccess.scenarioKeyword_2 + val titleAssignment = scenarioAccess.titleAssignment_3 + formatScenarioBlock(model, keyword, titleAssignment, doc) } def dispatch void format(ScenarioOutline model, extension IFormattableDocument doc) { - // format tags - for (t : model.tags) { - t.format() + + // Apply default scenario formatting + val keyword = scenarioOutlineAccess.scenarioOutlineKeyword_2 + val titleAssignment = scenarioOutlineAccess.titleAssignment_3 + formatScenarioBlock(model, keyword, titleAssignment, doc) + + increaseIndent() + model.examples.forEach[format().prepend[indent]] + decreaseIndent() + } + + def dispatch void format(Meta model, extension IFormattableDocument doc) { + model.tags.forEach[format] + } + + def dispatch void format(MetaElement model, extension IFormattableDocument doc) { + // Trim leading/trailing whitespace + model.surround[noSpace] + +// if (model.value !== null) { +// // Cleanup whitespace around value assignment +// model.regionFor.keyword(':').prepend[noSpace].append[oneSpace] +// model.regionFor.assignment(metaElementAccess.valueAssignment_2_1).prepend[oneSpace].append[noSpace] +// } + + // Insert newline if not present from BLANK_SPACE + if (model.isLast()) { + model.append[setNewLines(0)] + } else if (!model.hasTrailingBlankSpace) { + model.append[newLine] } - - // align keyword to column 0 - model.regionFor - .keyword(scenarioOutlineAccess.scenarioKeyword_1) - .prepend[noIndentation] - - // Indent interior - val begin = model.regionFor.ruleCallTo(EOLRule) - val end = endRegionFor(model, doc) - interior(begin, end)[indent] - - // format steps - for (s : model.steps) { - s.prepend[indent] - s.format() + } + + def dispatch void format(Step model, extension IFormattableDocument doc) { + // TODO cleanup whitespace + if (model.text !== null) { + model.text.format().prepend[indent] } - // format examples - for (e : model.examples) { - e.prepend[indent] - e.format() + if (model.table !== null) { + model.table.format().prepend[indent] } } def dispatch void format(Example model, extension IFormattableDocument doc) { - // format tags - for (t : model.tags) { - t.format() - } - // TODO this is just a hacky work-around until we can figure - // why having tags changes the indentation behavior of the - // keyword - if (!model.tags.isEmpty()) { - val region = model.regionFor.keyword(exampleAccess.examplesKeyword_1) - region.prepend[indent] + if (model.meta !== null) { + model.meta.format() + + // Work-around for strange keyword placement when tags are present + model.regionFor.keyword(exampleAccess.examplesKeyword_2).prepend[indent] } - // format table - model.table.rows.forEach[prepend[indent]] - model.table.format() + // indent Table + model.table.format().prepend[indent] } - def dispatch void format(Step model, extension IFormattableDocument doc) { - // TODO cleanup keyword/description - // format table - if (model.table !== null) { - model.table.rows.forEach[prepend[indent]] - model.table.format() - } + def dispatch void format(Narrative model, extension IFormattableDocument doc) { + model.sections.forEach[format().prepend[indent]] + } - // format code - // TODO proper Text model support (see branch scratch/textmodel) - if (model.code !== null) { - val open = docStringAccess.quotationMarkQuotationMarkQuotationMarkKeyword_1_0 - val close = docStringAccess.quotationMarkQuotationMarkQuotationMarkKeyword_1_3 - - model.code.regionFor.keyword(open).prepend[indent] - model.code.text.lines.forEach[prepend[indent]] - model.code.regionFor.keyword(close).prepend[indent] - model.code.format() - } + def dispatch void format(Paragraph model, extension IFormattableDocument doc) { + formatMultilineText(model, paragraphAccess.valueAssignment_1, indentationLevel, doc) } def dispatch void format(Table model, extension IFormattableDocument doc) { - // TODO... + model.rows.forEach[prepend[indent]] } def dispatch void format(DocString model, extension IFormattableDocument doc) { - // TODO... - } - - def dispatch void format(Tag model, extension IFormattableDocument doc) { - // TODO... + formatMultilineText(model, docStringAccess.valueAssignment_1, indentationLevel, doc) } // ---------------------------------------------------------- @@ -174,107 +180,114 @@ class CucumberFormatter extends AbstractFormatter2 { // Helper Methods // // ---------------------------------------------------------- - - /** - * Returns the semantic element that closes the Feature - * - * Delegates to: - * 1. The last element in scenarios - * 2. The last element in background - * 3. The EOL rule after title - */ - protected def ISemanticRegion endRegionFor(Feature model, extension IFormattableDocument doc) { + def void formatScenarioBlock(AbstractScenario model, Keyword keyword, Assignment titleAssignment, + extension IFormattableDocument doc) { + + // Set block spacing + if (!model.hasLeadingBlankSpace) { + model.prepend[setNewLines(2)] + } + + // Format meta tags + if (model.meta !== null) { + model.meta.format() + + // Work-around for strange keyword placement when tags are present + model.regionFor.keyword(keyword).prepend[indent] + } + + // Cleanup whitespace around keyword/title + if (model.title === null) { + model.regionFor.keyword(keyword).append[noSpace] + } else { + model.regionFor.assignment(titleAssignment).prepend[oneSpace].append[noSpace] + } + + increaseIndent() + indentBlock(model.startIndent, model.endIndent, doc) + + // Format narrative + if (model.narrative !== null) { + model.narrative.format().prepend[indent] + } + + // Format steps + model.steps.forEach[format().prepend[indent]] + + decreaseIndent() + } + + def ISemanticRegion startIndent(Feature model) { + return model.regionFor.ruleCallTo(NLRule) + } + + def ISemanticRegion endIndent(Feature model) { if (!model.scenarios.isEmpty()) { - return endRegionFor(model.scenarios.last, doc) - } else if (model.background !== null) { - return endRegionFor(model.background, doc) + return model.scenarios.last.endIndent() + } else if (model.narrative !== null) { + return model.narrative.endIndent() } - return model.regionFor.ruleCallTo(EOLRule) + return model.regionFor.ruleCallTo(NLRule) } - /** - * Returns the semantic element that closes an AbstractScenario - * - * Delegates to: - * 1. The last element in `steps` - * 3. The EOL rule after title - */ - protected def ISemanticRegion endRegionFor(AbstractScenario model, extension IFormattableDocument doc) { - if (!model.steps.isEmpty()) { - return endRegionFor(model.steps.last, doc) + def ISemanticRegion startIndent(AbstractScenario model) { + return model.regionFor.ruleCallTo(NLRule) + } + + def ISemanticRegion endIndent(Narrative model) { + return model.sections.last.endIndent() + } + + def ISemanticRegion endIndent(Block model) { + if (model instanceof Table) { + return model.rows.last.regionFor.ruleCallTo(NLRule) } - return model.regionFor.ruleCallTo(EOLRule) + return model.regionFor.ruleCallTo(NLRule) } - /** - * Returns the semantic element that closes a ScenarioOutline - * - * Delegates to: - * 1. The last element in examples - * 2. The last element in steps - * 3. The EOL rule after title - */ - protected def ISemanticRegion endRegionFor(ScenarioOutline model, extension IFormattableDocument doc) { - if (!model.examples.isEmpty()) { - return endRegionFor(model.examples.last, doc) - } else if (!model.steps.isEmpty()) { - return endRegionFor(model.steps.last, doc) + def ISemanticRegion endIndent(AbstractScenario model) { + if (model instanceof ScenarioOutline) { + if (!model.examples.isEmpty()) { + return model.examples.last.endIndent() + } + } + + if (!model.steps.isEmpty()) { + return model.steps.last.endIndent() + } else if (model.narrative !== null) { + return model.narrative.endIndent() } - return model.regionFor.ruleCallTo(EOLRule) + return model.regionFor.ruleCallTo(NLRule) } - /** - * Returns the semantic element that closes an Example - * - * Delegates to: - * 1. The EOL rule after the Table - * 1. The EOL rule after the title - */ - protected def ISemanticRegion endRegionFor(Example model, extension IFormattableDocument doc) { + def ISemanticRegion endIndent(Example model) { if (model.table !== null) { - return endRegionFor(model.table, doc) + return model.table.endIndent() + } else if (Strings.isEmpty(model.narrative)) { + return model.regionFor.ruleCall(exampleAccess.NLTerminalRuleCall_5_2) } - return model.regionFor.ruleCallTo(EOLRule) + return model.regionFor.ruleCall(exampleAccess.NLTerminalRuleCall_4) } - /** - * Returns the semantic element that closes a Step - * - * Delegates to: - * 1. Either to code or table if present - * 2. The EOL rule after `description` - */ - protected def ISemanticRegion endRegionFor(Step model, extension IFormattableDocument doc) { - if (model.code !== null) { - return endRegionFor(model.code, doc) + def ISemanticRegion endIndent(Step model) { + if (model.text !== null) { + return model.text.endIndent() } else if (model.table !== null) { - return endRegionFor(model.table, doc) + return model.table.endIndent() } - return model.regionFor.ruleCallTo(EOLRule) + return model.regionFor.ruleCallTo(NLRule) } - /** - * Returns the semantic element that closes a DocString - * - * Delegates to: - * 1. The EOLRule at the end of the element - */ - protected def ISemanticRegion endRegionFor(DocString model, extension IFormattableDocument doc) { - return model.regionFor.ruleCall(docStringAccess.EOLTerminalRuleCall_2) + def ISemanticRegion endIndent(DocString model) { + return model.regionFor.ruleCall(docStringAccess.NLTerminalRuleCall_2) } - /** - * Returns the semantic element that closes a Table - * - * Delegates to: - * 1. The EOLRule at the end of the table - */ - protected def ISemanticRegion endRegionFor(Table model, extension IFormattableDocument doc) { - return model.rows.last.regionFor.ruleCallTo(EOLRule) + def ISemanticRegion endIndent(Table model) { + return model.rows.last.regionFor.ruleCallTo(NLRule) } } diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/serializer/CucumberSerializer.xtend b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/serializer/CucumberSerializer.xtend index 44f79376..38772bc5 100644 --- a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/serializer/CucumberSerializer.xtend +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/serializer/CucumberSerializer.xtend @@ -1,42 +1,52 @@ package org.agileware.natural.cucumber.serializer +import com.google.inject.Inject +import org.agileware.natural.cucumber.cucumber.AbstractScenario import org.agileware.natural.cucumber.cucumber.Background -import org.agileware.natural.cucumber.cucumber.DocString +import org.agileware.natural.cucumber.cucumber.CucumberModel import org.agileware.natural.cucumber.cucumber.Example import org.agileware.natural.cucumber.cucumber.Feature import org.agileware.natural.cucumber.cucumber.Scenario import org.agileware.natural.cucumber.cucumber.ScenarioOutline import org.agileware.natural.cucumber.cucumber.Step -import org.agileware.natural.cucumber.cucumber.Table -import org.agileware.natural.cucumber.cucumber.TableCol -import org.agileware.natural.cucumber.cucumber.TableRow -import org.agileware.natural.cucumber.cucumber.Tag +import org.agileware.natural.lang.serializer.NaturalSerializer import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.agileware.natural.cucumber.cucumber.Text class CucumberSerializer { + @Inject extension NaturalSerializer + + def String serialize(CucumberModel model) ''' + # language: en + «IF model.document !== null» + «serialize(model.document)» + «ENDIF» + ''' + def String serialize(Feature model) ''' - «FOR t : model.tags» - «serialize(t)» - «ENDFOR» + «serialize(model.meta)» Feature: «model.title» «model.narrative» - - «IF model.background !== null» - «serialize(model.background)» - «ENDIF» «FOR s : model.scenarios» - - «IF s instanceof Scenario» - «serialize(s)» - «ELSEIF s instanceof ScenarioOutline» - «serialize(s)» - «ENDIF» + + «serialize(s)» «ENDFOR» ''' + def String serialize(AbstractScenario model) { + if (model instanceof Background) { + return serialize(model as Background) + } else if (model instanceof Scenario) { + return serialize(model as Scenario) + } else if (model instanceof ScenarioOutline) { + return serialize(model as ScenarioOutline) + } + + return "\n" + } + def String serialize(Background model) ''' + «serialize(model.meta)» Background: «model.title» «model.narrative» «FOR s : model.steps» @@ -45,9 +55,7 @@ class CucumberSerializer { ''' def String serialize(Scenario model) ''' - «FOR t : model.tags» - «serialize(t)» - «ENDFOR» + «serialize(model.meta)» Scenario: «model.title» «model.narrative» «FOR s : model.steps» @@ -56,17 +64,12 @@ class CucumberSerializer { ''' def String serialize(ScenarioOutline model) ''' - «FOR t : model.tags» - «serialize(t)» - «ENDFOR» - Scenario Outline: «model.title» + «serialize(model.meta)» + Scenario Outline:«model.title» «model.narrative» - «FOR s : model.steps» - «serialize(s)» - «ENDFOR» «FOR e : model.examples» - - «serialize(e)» + + «serialize(e)» «ENDFOR» ''' @@ -80,43 +83,8 @@ class CucumberSerializer { «NodeModelUtils.getNode(model).getText()» «IF model.table !== null» «serialize(model.table)» - «ELSEIF model.code !== null» - «serialize(model.code)» + «ELSEIF model.text !== null» + «serialize(model.text)» «ENDIF» ''' - - def String serialize(Tag model) ''' - @«model.id» - ''' - - def String serialize(Table model) ''' - «FOR r : model.rows» - «serialize(r)» - «ENDFOR» - ''' - - - def String serialize(TableRow model) ''' - «model.cols.map[serialize].join()» | - ''' - - def String serialize(TableCol model) { - return model.cell - } - - def String serialize(DocString model) ''' - """ - «serialize(model.text)» - """ - ''' - - /** - * TODO this will most certainly break two-way serialization, - * as trailing line breaks are not assigned within the TEXT_VALUE. - */ - def String serialize(Text model) ''' - «FOR l : model.lines» - «l.value» - «ENDFOR» - ''' } diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberIssueCodes.java b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberIssueCodes.java new file mode 100644 index 00000000..c45ca54d --- /dev/null +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberIssueCodes.java @@ -0,0 +1,16 @@ +package org.agileware.natural.cucumber.validation; + +public interface CucumberIssueCodes { + + String PREFIX = "org.agileware.natural.cucumber."; + + String MISSING_FEATURE_TITLE = PREFIX + "MISSING_FEATURE_TITLE"; + + String MISSING_SCENARIOS = PREFIX + "MISSING_SCENARIOS"; + + String MISSING_SCENARIO_STEPS = PREFIX + "MISSING_SCENARIO_STEPS"; + + String MISSING_STEPDEFS = PREFIX + "MISSING_STEPDEFS"; + + String MULTIPLE_STEPDEFS = PREFIX + "MULTIPLE_STEPDEFS"; +} diff --git a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberValidator.java b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberValidator.java index 25a06b54..72a9c5cb 100644 --- a/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberValidator.java +++ b/org.agileware.natural.cucumber/src/org/agileware/natural/cucumber/validation/CucumberValidator.java @@ -1,40 +1,89 @@ package org.agileware.natural.cucumber.validation; -import org.agileware.natural.common.JavaAnnotationMatcher; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.ABSTRACT_SCENARIO__STEPS; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.FEATURE__SCENARIOS; +import static org.agileware.natural.cucumber.cucumber.CucumberPackage.Literals.FEATURE__TITLE; +import static org.agileware.natural.cucumber.validation.CucumberIssueCodes.MISSING_FEATURE_TITLE; +import static org.agileware.natural.cucumber.validation.CucumberIssueCodes.MISSING_SCENARIOS; +import static org.agileware.natural.cucumber.validation.CucumberIssueCodes.MISSING_SCENARIO_STEPS; + +import java.util.Collection; + +import org.agileware.natural.cucumber.cucumber.AbstractScenario; import org.agileware.natural.cucumber.cucumber.CucumberPackage; +import org.agileware.natural.cucumber.cucumber.Feature; import org.agileware.natural.cucumber.cucumber.Step; -import org.eclipse.jdt.core.IMethod; +import org.agileware.natural.stepmatcher.IStepMatcher; import org.eclipse.xtext.validation.Check; import org.eclipse.xtext.validation.CheckType; +import org.eclipse.xtext.validation.ValidationMessageAcceptor; import com.google.inject.Inject; public class CucumberValidator extends AbstractCucumberValidator { - + @Inject - private JavaAnnotationMatcher matcher; - - @Check(CheckType.EXPENSIVE) - public void checkStepMatching(Step step) { - final Counter counter = new Counter(); - String description = step.getDescription().trim(); - matcher.findMatches(description, counter); - if (counter.get() == 0) { - warning("No definition found for `" + description + "`", CucumberPackage.Literals.STEP__DESCRIPTION); - } else if (counter.get() > 1) { - warning("Multiple definitions found for `" + description + "`", CucumberPackage.Literals.STEP__DESCRIPTION); + private IStepMatcher matcher; + + /** + * Issue a warning if the Feature has no title + * + * @param model + */ + @Check(CheckType.FAST) + public void featureTitle(final Feature model) { + if (model.getTitle() == null) { + warning("Feature title is missing", FEATURE__TITLE, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, + MISSING_FEATURE_TITLE); + } + } + + /** + * Issue a warning if Feature has no scenarios defined. **note:** Do not depend + * on grammar rule validation + * + * @param model + */ + @Check(CheckType.FAST) + public void missingScenarios(final Feature model) { + if (model.getScenarios().isEmpty()) { + warning("Feature has no scenarios", model, FEATURE__SCENARIOS, MISSING_SCENARIOS); } } - private final static class Counter implements JavaAnnotationMatcher.Command { - private int count = 0; - - public int get() { - return count; + /** + * Issue a warning if AbstractScenario has no defined steps. **note:** Do not + * depend on grammar rule validation + * + * @param model + */ + @Check(CheckType.FAST) + public void missingScenarioSteps(final AbstractScenario model) { + if (model.getSteps().isEmpty()) { + warning("Scenario has no steps", model, ABSTRACT_SCENARIO__STEPS, MISSING_SCENARIO_STEPS); } - - public void match(String annotationValue, IMethod method) { - count++; + } + + /** + * Scan for matching java annotation if activated + * + * @param model + */ + @Check(CheckType.NORMAL) + public void checkStepMatching(final Step step) { + if (!matcher.isActivated()) + return; + + final String keyword = step.getKeyword(); + final String description = step.getDescription(); + if (keyword != null && description != null) { + final Collection matches = matcher.findMatches(keyword, description); + if (matches.size() == 0) { + warning("No definition found for `" + description + "`", CucumberPackage.Literals.STEP__DESCRIPTION); + } else if (matches.size() > 1) { + warning("Multiple definitions found for `" + description + "`", + CucumberPackage.Literals.STEP__DESCRIPTION); + } } } } diff --git a/org.agileware.natural.jbehave.feature/feature.xml b/org.agileware.natural.jbehave.feature/feature.xml index c5dbfa59..cd17db3e 100644 --- a/org.agileware.natural.jbehave.feature/feature.xml +++ b/org.agileware.natural.jbehave.feature/feature.xml @@ -66,7 +66,14 @@ to corresponding Java annotated classes. unpack="false"/> + + 4.0.0 @@ -19,11 +20,14 @@ org.eclipse.tycho tycho-surefire-plugin + false true + true + org.agileware.natural.jbehave.ui.tests + org.agileware.natural.jbehave.ui.tests.JbehaveUiTestSuite - 1.0.0-SNAPSHOT diff --git a/org.agileware.natural.jbehave.ui/META-INF/MANIFEST.MF b/org.agileware.natural.jbehave.ui/META-INF/MANIFEST.MF index 949b5077..4ebb6a0b 100644 --- a/org.agileware.natural.jbehave.ui/META-INF/MANIFEST.MF +++ b/org.agileware.natural.jbehave.ui/META-INF/MANIFEST.MF @@ -8,6 +8,8 @@ Bundle-SymbolicName: org.agileware.natural.jbehave.ui; singleton:=true Bundle-ActivationPolicy: lazy Require-Bundle: org.agileware.natural.jbehave, org.agileware.natural.jbehave.ide, + org.agileware.natural.lang.ui, + org.agileware.natural.stepmatcher.ui, org.eclipse.xtext.ui, org.eclipse.xtext.ui.shared, org.eclipse.xtext.ui.codetemplates.ui, @@ -18,9 +20,14 @@ Require-Bundle: org.agileware.natural.jbehave, org.eclipse.xtext.builder Import-Package: org.apache.log4j Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: org.agileware.natural.jbehave.ui.contentassist;uses:="org.eclipse.emf.ecore,org.eclipse.xtext.ui.editor.contentassist,org.eclipse.xtext", +Export-Package: org.agileware.natural.jbehave.ui.contentassist; + uses:="org.eclipse.emf.ecore, + org.agileware.natural.lang.ui.contentassist, + org.eclipse.xtext.ui.editor.contentassist, + org.eclipse.xtext", org.agileware.natural.jbehave.ui.internal;uses:="org.osgi.framework,com.google.inject,org.eclipse.ui.plugin", org.agileware.natural.jbehave.ui.labeling;uses:="org.eclipse.xtext.ui.label,org.eclipse.emf.edit.ui.provider", org.agileware.natural.jbehave.ui.outline;uses:="org.eclipse.xtext.ui.editor.outline.impl", - org.agileware.natural.jbehave.ui.quickfix;uses:="org.eclipse.xtext.ui.editor.quickfix" + org.agileware.natural.jbehave.ui.quickfix;uses:="org.eclipse.xtext.ui.editor.quickfix", + org.agileware.natural.jbehave.validation;uses:="org.eclipse.swt.widgets,org.eclipse.jface.dialogs,org.eclipse.xtext.ui.validation" Bundle-Activator: org.agileware.natural.jbehave.ui.internal.JbehaveActivator diff --git a/org.agileware.natural.jbehave.ui/build.properties b/org.agileware.natural.jbehave.ui/build.properties index 282ec075..0a12cca8 100644 --- a/org.agileware.natural.jbehave.ui/build.properties +++ b/org.agileware.natural.jbehave.ui/build.properties @@ -1,7 +1,9 @@ source.. = src/,\ - src-gen/ + src-gen/,\ + xtend-gen/ bin.includes = META-INF/,\ .,\ plugin.xml -additional.bundles = org.agileware.natural.common,\ +additional.bundles = org.agileware.natural.stepmatcher,\ + org.agileware.natural.stepmatcher.ui,\ org.agileware.natural.jbehave diff --git a/org.agileware.natural.jbehave.web/WebRoot/xtext-resources/generated/mode-story.js b/org.agileware.natural.jbehave.web/WebRoot/xtext-resources/generated/mode-story.js index d9d0c44c..f70d74cb 100644 --- a/org.agileware.natural.jbehave.web/WebRoot/xtext-resources/generated/mode-story.js +++ b/org.agileware.natural.jbehave.web/WebRoot/xtext-resources/generated/mode-story.js @@ -1,6 +1,10 @@ define(["ace/lib/oop", "ace/mode/text", "ace/mode/text_highlight_rules"], function(oop, mText, mTextHighlightRules) { var HighlightRules = function() { + var keywords = "ANY|FAILURE|SCENARIO|STEP|STORY|SUCCESS"; this.$rules = { + "start": [ + {token: "keyword", regex: "\\b(?:" + keywords + ")\\b"} + ] }; }; oop.inherits(HighlightRules, mTextHighlightRules.TextHighlightRules); diff --git a/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/JbehaveServlet.java b/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/JbehaveServlet.java new file mode 100644 index 00000000..0dcea3e0 --- /dev/null +++ b/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/JbehaveServlet.java @@ -0,0 +1,34 @@ +/* + * generated by Xtext 2.22.0 + */ +package org.agileware.natural.jbehave.web; + +import com.google.inject.Injector; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import org.eclipse.xtext.util.DisposableRegistry; +import org.eclipse.xtext.web.servlet.XtextServlet; + +/** + * Deploy this class into a servlet container to enable DSL-specific services. + */ +@WebServlet(name = "XtextServices", urlPatterns = "/xtext-service/*") +public class JbehaveServlet extends XtextServlet { + + DisposableRegistry disposableRegistry; + + public void init() throws ServletException { + super.init(); + Injector injector = new JbehaveWebSetup().createInjectorAndDoEMFRegistration(); + this.disposableRegistry = injector.getInstance(DisposableRegistry.class); + } + + public void destroy() { + if (disposableRegistry != null) { + disposableRegistry.dispose(); + disposableRegistry = null; + } + super.destroy(); + } + +} diff --git a/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/JbehaveServlet.xtend b/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/JbehaveServlet.xtend deleted file mode 100644 index d2637de2..00000000 --- a/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/JbehaveServlet.xtend +++ /dev/null @@ -1,32 +0,0 @@ -/* - * generated by Xtext 2.21.0 - */ -package org.agileware.natural.jbehave.web - -import javax.servlet.annotation.WebServlet -import org.eclipse.xtext.util.DisposableRegistry -import org.eclipse.xtext.web.servlet.XtextServlet - -/** - * Deploy this class into a servlet container to enable DSL-specific services. - */ -@WebServlet(name = 'XtextServices', urlPatterns = '/xtext-service/*') -class JbehaveServlet extends XtextServlet { - - DisposableRegistry disposableRegistry - - override init() { - super.init() - val injector = new JbehaveWebSetup().createInjectorAndDoEMFRegistration() - disposableRegistry = injector.getInstance(DisposableRegistry) - } - - override destroy() { - if (disposableRegistry !== null) { - disposableRegistry.dispose() - disposableRegistry = null - } - super.destroy() - } - -} diff --git a/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/ServerLauncher.java b/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/ServerLauncher.java new file mode 100644 index 00000000..a1f46e0e --- /dev/null +++ b/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/ServerLauncher.java @@ -0,0 +1,65 @@ +/* + * generated by Xtext 2.22.0 + */ +package org.agileware.natural.jbehave.web; + +import java.net.InetSocketAddress; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.log.Slf4jLog; +import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.MetaInfConfiguration; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.WebInfConfiguration; +import org.eclipse.jetty.webapp.WebXmlConfiguration; + +/** + * This program starts an HTTP server for testing the web integration of your DSL. + * Just execute it and point a web browser to http://localhost:8080/ + */ +public class ServerLauncher { + public static void main(String[] args) { + Server server = new Server(new InetSocketAddress("localhost", 8080)); + WebAppContext ctx = new WebAppContext(); + ctx.setResourceBase("WebRoot"); + ctx.setWelcomeFiles(new String[] {"index.html"}); + ctx.setContextPath("/"); + ctx.setConfigurations(new Configuration[] { + new AnnotationConfiguration(), + new WebXmlConfiguration(), + new WebInfConfiguration(), + new MetaInfConfiguration() + }); + ctx.setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, + ".*/org\\.agileware\\.natural\\.jbehave\\.web/.*,.*\\.jar"); + ctx.setInitParameter("org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false"); + server.setHandler(ctx); + Slf4jLog log = new Slf4jLog(ServerLauncher.class.getName()); + try { + server.start(); + log.info("Server started " + server.getURI() + "..."); + new Thread() { + + public void run() { + try { + log.info("Press enter to stop the server..."); + int key = System.in.read(); + if (key != -1) { + server.stop(); + } else { + log.warn( + "Console input is not available. In order to stop the server, you need to cancel process manually."); + } + } catch (Exception e) { + log.warn(e); + } + } + + }.start(); + server.join(); + } catch (Exception exception) { + log.warn(exception.getMessage()); + System.exit(1); + } + } +} diff --git a/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/ServerLauncher.xtend b/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/ServerLauncher.xtend deleted file mode 100644 index 5f478ebf..00000000 --- a/org.agileware.natural.jbehave.web/src/org/agileware/natural/jbehave/web/ServerLauncher.xtend +++ /dev/null @@ -1,54 +0,0 @@ -/* - * generated by Xtext 2.21.0 - */ -package org.agileware.natural.jbehave.web - -import java.net.InetSocketAddress -import org.eclipse.jetty.annotations.AnnotationConfiguration -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.util.log.Slf4jLog -import org.eclipse.jetty.webapp.MetaInfConfiguration -import org.eclipse.jetty.webapp.WebAppContext -import org.eclipse.jetty.webapp.WebInfConfiguration -import org.eclipse.jetty.webapp.WebXmlConfiguration - -/** - * This program starts an HTTP server for testing the web integration of your DSL. - * Just execute it and point a web browser to http://localhost:8080/ - */ -class ServerLauncher { - def static void main(String[] args) { - val server = new Server(new InetSocketAddress('localhost', 8080)) - server.handler = new WebAppContext => [ - resourceBase = 'WebRoot' - welcomeFiles = #["index.html"] - contextPath = "/" - configurations = #[ - new AnnotationConfiguration, - new WebXmlConfiguration, - new WebInfConfiguration, - new MetaInfConfiguration - ] - setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, '.*/org\\.agileware\\.natural\\.jbehave\\.web/.*,.*\\.jar') - setInitParameter("org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false") - ] - val log = new Slf4jLog(ServerLauncher.name) - try { - server.start - log.info('Server started ' + server.getURI + '...') - new Thread[ - log.info('Press enter to stop the server...') - val key = System.in.read - if (key != -1) { - server.stop - } else { - log.warn('Console input is not available. In order to stop the server, you need to cancel process manually.') - } - ].start - server.join - } catch (Exception exception) { - log.warn(exception.message) - System.exit(1) - } - } -} diff --git a/org.agileware.natural.jbehave/META-INF/MANIFEST.MF b/org.agileware.natural.jbehave/META-INF/MANIFEST.MF index 7b0b2a37..a704fa64 100644 --- a/org.agileware.natural.jbehave/META-INF/MANIFEST.MF +++ b/org.agileware.natural.jbehave/META-INF/MANIFEST.MF @@ -6,7 +6,8 @@ Bundle-Vendor: Roberto Lo Giacco Bundle-Version: 1.0.0.qualifier Bundle-SymbolicName: org.agileware.natural.jbehave; singleton:=true Bundle-ActivationPolicy: lazy -Require-Bundle: org.agileware.natural.common, +Require-Bundle: org.agileware.natural.lang;visibility:=reexport, + org.agileware.natural.stepmatcher;visibility:=reexport, org.eclipse.xtext, org.eclipse.xtext.xbase, org.eclipse.equinox.common;bundle-version="3.5.0", @@ -14,6 +15,7 @@ Require-Bundle: org.agileware.natural.common, org.eclipse.xtext.xbase.lib;bundle-version="2.14.0", org.eclipse.xtext.util, org.eclipse.emf.common, + org.eclipse.jdt.core, org.antlr.runtime;bundle-version="[3.2.0,3.2.1)" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: org.agileware.natural.jbehave;uses:="com.google.inject,org.eclipse.xtext,org.eclipse.xtext.service", diff --git a/org.agileware.natural.jbehave/model/generated/Jbehave.ecore b/org.agileware.natural.jbehave/model/generated/Jbehave.ecore index 6363e3a7..e67d7320 100644 --- a/org.agileware.natural.jbehave/model/generated/Jbehave.ecore +++ b/org.agileware.natural.jbehave/model/generated/Jbehave.ecore @@ -1,67 +1,104 @@ + + + - + - + - + + + + + + + + + - - + + - - - - + + + + + + + + + - - - - + + - - + + + - + + eType="#//LifecycleAfterElement" containment="true"/> - - - + + + - - + + - - + + + + - - + + - - + + + + + + + + + - - + + + - - + + diff --git a/org.agileware.natural.jbehave/model/generated/Jbehave.genmodel b/org.agileware.natural.jbehave/model/generated/Jbehave.genmodel index 28f85712..8885c96a 100644 --- a/org.agileware.natural.jbehave/model/generated/Jbehave.genmodel +++ b/org.agileware.natural.jbehave/model/generated/Jbehave.genmodel @@ -1,59 +1,90 @@ + complianceLevel="8.0" copyrightFields="false" runtimeVersion="2.20" usedGenPackages="platform:/resource/org.agileware.natural.lang/model/generated/Natural.genmodel#//model"> + + + + + + + + + + + + + - + + - - - - + + - - - - - - + + + + - - + + - - + + - - + + + - - - + + + + + + + - - + + - - + + + - - + + - - + + + + + + + - - + + - - + + + + + + + + + + + + diff --git a/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/GenerateJbehave.mwe2 b/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/GenerateJbehave.mwe2 index a503dad0..2d3a1e41 100644 --- a/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/GenerateJbehave.mwe2 +++ b/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/GenerateJbehave.mwe2 @@ -6,7 +6,7 @@ import org.eclipse.xtext.xtext.generator.model.project.* var rootPath = ".." Workflow { - + component = XtextGenerator { configuration = { project = StandardProjectConfig { @@ -36,13 +36,13 @@ Workflow { language = StandardLanguage { name = "org.agileware.natural.jbehave.Jbehave" fileExtensions = "story" - + referencedResource = "platform:/resource/org.agileware.natural.lang/model/generated/Natural.genmodel" serializer = { generateStub = true } validator = { - // composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator" - // Generates checks for @Deprecated grammar annotations, an IssueProvider and a corresponding PropertyPage + // composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator" + // Generates checks for @Deprecated grammar annotations, an IssueProvider and a corresponding PropertyPage generateDeprecationValidation = true } formatter = { @@ -55,6 +55,9 @@ Workflow { } parserGenerator = { debugGrammar = true + options = auto-inject { + classSplitting = true + } } projectWizard = { generate = false diff --git a/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/Jbehave.xtext b/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/Jbehave.xtext index 4a6223c8..ed0cdd25 100644 --- a/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/Jbehave.xtext +++ b/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/Jbehave.xtext @@ -1,102 +1,251 @@ -grammar org.agileware.natural.jbehave.Jbehave hidden(SPACE, SL_COMMENT) +grammar org.agileware.natural.jbehave.Jbehave with org.agileware.natural.lang.Natural import "http://www.eclipse.org/emf/2002/Ecore" as ecore generate jbehave "http://www.agileware.org/natural/jbehave" +/** + * + */ +JbehaveModel: + {JbehaveModel} + BLANK_SPACE? + document=Story?; + + /** + * The story describes a feature via description, narrative and a set of scenarios + * Story := Description? Meta? Narrative? GivenStories? Lifecycle? Scenario+ ; + */ Story: - description+=Description* - meta=Meta? - narrative=Narrative? - scenarios+=Scenario+; - -Narrative: - 'Narrative:' - inOrderTo=InOrderTo - asA=AsA - iWantTo=IWantTo; - -Scenario: - 'Scenario:' - description+=Description+ - meta=Meta? - given=GivenStories? - conditions+=Step+ - examples=Examples?; - -GivenStories: - 'GivenStories:' - resources+=StoryPath+; - -StoryPath: - path=PATH ','?; - -Meta: {Meta} - 'Meta:' - elements+=Tag*; - -Step: - description=StepDescription EOL* - table=Table?; - -InOrderTo: - IN_ORDER_TO content=NarrativeElementContent; - -AsA: - AS_A content=NarrativeElementContent; - -IWantTo: - I_WANT_TO content=NarrativeElementContent; - -Examples: - 'Examples:' - table=Table; - -Table: - rows+=TABLE_ROW+ EOL*; - + {Story} + (description=Description BLANK_SPACE?)? + (meta=Meta BLANK_SPACE?)? + (narrative=StoryNarrative BLANK_SPACE?) + (lifecycle=Lifecycle BLANK_SPACE?)? + scenarios+=Scenario + (BLANK_SPACE? scenarios+=Scenario)* +; + +/** + * The Description is expressed by any sequence of words that must not contain any keywords at start of lines. + * Description := (Word Space?)* ; + */ Description: - Content EOL+; - -StepDescription: - STEP_KEYWORD (STRING | WORD | INT | ANY | PLACEHOLDER)+; - -NarrativeElementContent: - (STRING | WORD | INT | ANY | PLACEHOLDER) (AT | STRING | WORD | INT | ANY | PLACEHOLDER | STEP_KEYWORD)*; - -Content: - (STRING | WORD | INT | ANY | PLACEHOLDER) (AT | STRING | WORD | INT | ANY | PLACEHOLDER | STEP_KEYWORD)*; - -Tag: AT name=WORD EOL?; - - -terminal STEP_KEYWORD: ('Given' | 'When' | 'Then' | 'And' | '!--'); + {Description} + value=RawTextBlock + NL +; + +/** + * The meta is identified by keyword "Meta:" (or equivalent in I18n-ed locale), + * It is followed by any number of meta elements + * Meta:= "Meta:" (MetaElement)* ; + */ +@Override +Meta: {Meta} + 'Meta:' NL + (BLANK_SPACE? tags+=MetaElement)+ + NL +; + +/** + * The narrative is identified by keyword "Narrative:" (or equivalent in I18n-ed locale), + * It is followed by the narrative elements + * Narrative:= "Narrative:" ( InOrderTo AsA IWantTo | AsA IWantTo SoThat ) ; + */ +StoryNarrative: StoryNarrativeA + | StoryNarrativeB +; + +StoryNarrativeA: {StoryNarrativeA} + 'Narrative:' NL + (BLANK_SPACE? inOrderTo=InOrderTo) + (BLANK_SPACE? asA=AsA) + (BLANK_SPACE? wantTo=IWantTo); + +StoryNarrativeB: {StoryNarrativeB} + 'Narrative:' NL + (BLANK_SPACE? asA=AsA) + (BLANK_SPACE? wantTo=IWantTo) + (BLANK_SPACE? soThat=SoThat); + +StoryNarrativeElement: InOrderTo + | AsA + | IWantTo + | SoThat +; + +InOrderTo: {InOrderTo} + keyword=IN_ORDER_TO value=RawText NL +; + +AsA: {AsA} + keyword=AS_A value=RawText NL +; + +IWantTo: {IWantTo} + keyword=I_WANT_TO value=RawText NL +; + +SoThat: {SoThat} + keyword=SO_THAT value=RawText NL +; terminal IN_ORDER_TO: 'In order to'; - terminal AS_A: 'As a'; - terminal I_WANT_TO: 'I want to'; - -terminal PLACEHOLDER: '<' ('a'..'z' | 'A'..'Z') !('>' | '\n' | '\r')* '>'; - -terminal TABLE_ROW: '|' (!('|' | '\n' | '\r')* '|')+ SPACE* EOL; - -terminal WORD: ('a'..'z' | 'A'..'Z') !(SPACE | '\n' | '\r')*; - -terminal PATH: '/' ('/' | '.' | '_' | 'a'..'z' | 'A'..'Z' | '0'..'9')+; - -terminal INT returns ecore::EDouble: '-'? ('0'..'9')+ ('.' ('0'..'9')+)?; - -terminal STRING: - '"' ('\\' ('b' | 't' | 'n' | 'f' | 'r' | 'u' | '"' | "'" | '\\') | !('\\' | '"'))* '"' | - "'" ('\\' ('b' | 't' | 'n' | 'f' | 'r' | 'u' | '"' | "'" | '\\') | !('\\' | "'"))* "'"; - -terminal SL_COMMENT: '#' !('\n' | '\r')* EOL; - -terminal SPACE: (' ' | '\t'); - -terminal EOL: '\r'? '\n'?; - -terminal AT: '@'; - -terminal ANY: .; \ No newline at end of file +terminal SO_THAT: 'So that'; + +/** + * The lifecycle is identified by keyword "Lifecycle:" (or equivalent in I18n-ed locale), + * It is followed by the lifecycle elements + * Lifecycle:= "Lifecycle:" LifecycleBefore? LifecycleAfter? + */ +Lifecycle: {Lifecycle} + 'Lifecycle:' NL + before=LifecycleBefore? + after=LifecycleAfter? +; + +/** + * The before lifecyle element identified by keyword "Before:" (or equivalent in I18n-ed locale), + * followed by one or more steps + * LifecycleBefore:= "Before:" (Scope? Step+)+ + */ +LifecycleBefore: {LifecycleBefore} + "Before:" NL + elements+=LifecycleBeforeElement+ +; + +LifecycleBeforeElement: {LifecycleBeforeElement} + scope=Scope NL + steps+=Step+ +; + +/** + * The after lifecyle element identified by keyword "After:" (or equivalent in I18n-ed locale), + * followed by one or more sets of scope, outcome, meta filter and steps + * LifecycleAfter:= "After:" (Scope? Outcome? MetaFilter? Step+)+ + */ +LifecycleAfter: {LifecycleAfter} + "After:" NL + elements+=LifecycleAfterElement+ +; + +LifecycleAfterElement: {LifecycleAfterElement} + scope=Scope NL + (outcome=Outcome NL)? + steps+=Step+ +; + +/** + * The scope element identified by keyword "Scope:" (or equivalent in I18n-ed locale), + * Scope:= "Scope:" "STEP" | "SCENARIO" | "STORY" + */ +Scope: {Scope} + "Scope:" + type=ScopeType +; + +enum ScopeType: STEP + | SCENARIO + | STORY +; + +/** + * The outcome element identified by keyword "Outcome:" (or equivalent in I18n-ed locale), + * Outcome:= "Outcome:" "ANY" | "SUCCESS" | "FAILURE" ; + */ +Outcome: {Outcome} + "Outcome:" + type=OutcomeType +; + +enum OutcomeType: ANY + | SUCCESS + | FAILURE +; + +/** + * The scenario is identified by keyword "Scenario:" (or equivalent in I18n-ed locale), + * which is optional in the case of a single scenario. + * It can optionally be followed by a title, which is expressed by any sequence of words + * that must not contain any keywords at start of lines. + * It is followed by one or more Steps. + * Scenarios can optionally contain comments (which are not part of the scenarios) after examples using "!--" keyword + * Scenario := "Scenario:"? Title? Meta? GivenStories? Step+ Examples? (Examples Comment+)? ; + */ +Scenario: {Scenario} + 'Scenario:' title=RawText? NL + (meta=Meta BLANK_SPACE?)? +// (given=GivenStories BLANK_SPACE?)? + steps+=Step + (BLANK_SPACE? steps+=Step)* + (BLANK_SPACE? examples=Examples)? +; + +/** + * The comma-separated list of story resources that specify the stories to be run before a story or a scenario + * GivenStories:= "GivenStories:" (StoryPath ','?)+ ; + */ +//GivenStories: {GivenStories} +// 'GivenStories:' +// resources+=StoryPath +// (-> ',' BLANK_SPACE? resources+=StoryPath)* +// NL +//; + +//StoryPath: {StoryPath} +// value=PATH +//; + +/** + * The scenario step is a step starting work followed by any number of characters + * Step := StepStartingWord StepContent ; + */ +Step: {Step} + keyword=STEP_KEYWORD description=RawText NL + // ((BLANK_SPACE? table=Table) | (BLANK_SPACE? text=DocString))? +; + +terminal STEP_KEYWORD: + ('Given' | 'When' | 'Then' | 'And' | 'But' | '*') +; + +terminal PLACEHOLDER: + ('<' !('>' | ' ' | '\t' | '\n' | '\r')+ '>') + | ('[' !(']' | ' ' | '\t' | '\n' | '\r')+ ']') +; + +/** + * The examples table + * Examples := "Examples:" ExamplesTable ; + */ +Examples: {Examples} + 'Examples:' NL + (BLANK_SPACE? table=Table) +; + + +@Override +StartLiteral: ID + | NUMBER + | STRING + | PLACEHOLDER + | GLOB + | ANY_OTHER +; + +@Override +Literal: ID + | NUMBER + | STRING + | PLACEHOLDER + | STEP_KEYWORD + | GLOB + | ANY_OTHER +; + +//PATH returns ecore::EString: +// ('./' | '../' | '/') +// (GLOB ('/' GLOB)* ('.' GLOB)?)? +//; diff --git a/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/formatting2/JbehaveFormatter.xtend b/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/formatting2/JbehaveFormatter.xtend index be356e00..ac0642fc 100644 --- a/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/formatting2/JbehaveFormatter.xtend +++ b/org.agileware.natural.jbehave/src/org/agileware/natural/jbehave/formatting2/JbehaveFormatter.xtend @@ -4,31 +4,156 @@ package org.agileware.natural.jbehave.formatting2 import com.google.inject.Inject -import org.agileware.natural.jbehave.jbehave.Narrative +import org.agileware.natural.jbehave.jbehave.Examples +import org.agileware.natural.jbehave.jbehave.JbehaveModel +import org.agileware.natural.jbehave.jbehave.Lifecycle +import org.agileware.natural.jbehave.jbehave.LifecycleAfter +import org.agileware.natural.jbehave.jbehave.LifecycleAfterElement +import org.agileware.natural.jbehave.jbehave.LifecycleBefore +import org.agileware.natural.jbehave.jbehave.LifecycleBeforeElement +import org.agileware.natural.jbehave.jbehave.Meta +import org.agileware.natural.jbehave.jbehave.Scenario +import org.agileware.natural.jbehave.jbehave.Step import org.agileware.natural.jbehave.jbehave.Story +import org.agileware.natural.jbehave.jbehave.StoryNarrativeA +import org.agileware.natural.jbehave.jbehave.StoryNarrativeB +import org.agileware.natural.jbehave.jbehave.StoryNarrativeElement import org.agileware.natural.jbehave.services.JbehaveGrammarAccess +import org.agileware.natural.lang.formatting2.NaturalFormatHelper +import org.agileware.natural.lang.model.DocString +import org.agileware.natural.lang.model.MetaElement +import org.agileware.natural.lang.model.Table import org.eclipse.xtext.formatting2.AbstractFormatter2 +import org.eclipse.xtext.formatting2.FormatterRequest import org.eclipse.xtext.formatting2.IFormattableDocument class JbehaveFormatter extends AbstractFormatter2 { + + @Inject extension JbehaveGrammarAccess jbehaveGrammerAccess + + @Inject NaturalFormatHelper.Factory formatHelperFactory + + // TODO there must be a better way to get the current indentation level! + var int indentationLevel = -1 + + var extension NaturalFormatHelper _formatHelper = null + + override protected initialize(FormatterRequest request) { + _formatHelper = formatHelperFactory.create(request.textRegionAccess, naturalGrammarAccess) + _formatHelper.initialize(request) + + super.initialize(request) + } + + def dispatch void format(JbehaveModel model, extension IFormattableDocument doc) { + // println(textRegionAccess) + model.document.format() + // println(doc) + } + + def dispatch void format(Story model, extension IFormattableDocument document) { + model.description.format() + model.meta.format() + model.narrative.format() + model.lifecycle.format() + model.scenarios.forEach[format] + } + + def dispatch void format(StoryNarrativeA model, extension IFormattableDocument doc) { + model.inOrderTo.format() + model.asA.format() + model.wantTo.format() + } + + def dispatch void format(StoryNarrativeB model, extension IFormattableDocument doc) { + model.asA.format() + model.wantTo.format() + model.soThat.format() + } + + def dispatch void format(StoryNarrativeElement model, extension IFormattableDocument doc) { + // TODO... + } + + def dispatch void format(Lifecycle model, extension IFormattableDocument doc) { + model.before.format() + model.after.format() + } + + def dispatch void format(LifecycleBefore model, extension IFormattableDocument doc) { + model.elements.forEach[format] + } + + def dispatch void format(LifecycleBeforeElement model, extension IFormattableDocument doc) { + model.steps.forEach[format] + } + + def dispatch void format(LifecycleAfter model, extension IFormattableDocument doc) { + model.elements.forEach[format] + } + + def dispatch void format(LifecycleAfterElement model, extension IFormattableDocument doc) { + model.steps.forEach[format] + } + + def dispatch void format(Scenario model, extension IFormattableDocument doc) { + model.meta.format() + // model.given.format() + model.steps.forEach[format] + model.examples.format() + } + +// def dispatch void format(GivenStories model, extension IFormattableDocument doc) { +// model.resources.forEach[format] +// } + +// def dispatch void format(StoryPath model, extension IFormattableDocument doc) { +// // TODO... +// } - @Inject extension JbehaveGrammarAccess - - def dispatch void format(Story story, extension IFormattableDocument document) { - // TODO: format HiddenRegions around keywords, attributes, cross references, etc. - story.meta.format - story.narrative.format - for (scenario : story.scenarios) { - scenario.format + def dispatch void format(Step model, extension IFormattableDocument doc) { + // TODO... + } + + def dispatch void format(Examples model, extension IFormattableDocument doc) { + model.table.format() + } + + + + + def dispatch void format(Meta model, extension IFormattableDocument doc) { + model.tags.forEach[format] + } + + def dispatch void format(MetaElement model, extension IFormattableDocument doc) { + // Trim leading/trailing whitespace + model.surround[noSpace] + +// if (model.value !== null) { +// // Cleanup whitespace around value assignment +// model.regionFor.keyword(':').prepend[noSpace].append[oneSpace] +// model.regionFor.assignment(metaElementAccess.valueAssignment_2_1).prepend[oneSpace].append[noSpace] +// } + + // Insert newline if not present from BLANK_SPACE + if (model.isLast()) { + model.append[setNewLines(0)] + } else if (!model.hasTrailingBlankSpace) { + model.append[newLine] } } - def dispatch void format(Narrative narrative, extension IFormattableDocument document) { - // TODO: format HiddenRegions around keywords, attributes, cross references, etc. - narrative.inOrderTo.format - narrative.asA.format - narrative.IWantTo.format + def dispatch void format(Table model, extension IFormattableDocument doc) { + model.rows.forEach[prepend[indent]] + } + + def dispatch void format(DocString model, extension IFormattableDocument doc) { + formatMultilineText(model, docStringAccess.valueAssignment_1, indentationLevel, doc) + } + + def dispatch boolean isLast(MetaElement model) { + val meta = model.eContainer as Meta + model == meta.tags.last } - - // TODO: implement for Scenario, GivenStories, Meta, Step, Examples } diff --git a/org.agileware.natural.common/.classpath b/org.agileware.natural.lang.ide/.classpath similarity index 69% rename from org.agileware.natural.common/.classpath rename to org.agileware.natural.lang.ide/.classpath index e7847821..8d26fa59 100644 --- a/org.agileware.natural.common/.classpath +++ b/org.agileware.natural.lang.ide/.classpath @@ -1,12 +1,9 @@ - - - - - - + - + + + diff --git a/org.agileware.natural.lang.ide/.project b/org.agileware.natural.lang.ide/.project new file mode 100644 index 00000000..b95bdf0d --- /dev/null +++ b/org.agileware.natural.lang.ide/.project @@ -0,0 +1,40 @@ + + + org.agileware.natural.lang.ide + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.m2e.core.maven2Nature + + diff --git a/org.agileware.natural.common/.settings/org.eclipse.core.resources.prefs b/org.agileware.natural.lang.ide/.settings/org.eclipse.core.resources.prefs similarity index 100% rename from org.agileware.natural.common/.settings/org.eclipse.core.resources.prefs rename to org.agileware.natural.lang.ide/.settings/org.eclipse.core.resources.prefs diff --git a/org.agileware.natural.common/.settings/org.eclipse.jdt.core.prefs b/org.agileware.natural.lang.ide/.settings/org.eclipse.jdt.core.prefs similarity index 74% rename from org.agileware.natural.common/.settings/org.eclipse.jdt.core.prefs rename to org.agileware.natural.lang.ide/.settings/org.eclipse.jdt.core.prefs index c872bd03..9f6ece88 100644 --- a/org.agileware.natural.common/.settings/org.eclipse.jdt.core.prefs +++ b/org.agileware.natural.lang.ide/.settings/org.eclipse.jdt.core.prefs @@ -3,8 +3,6 @@ org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.agileware.natural.common/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.agileware.natural.lang.ide/.settings/org.eclipse.xtend.core.Xtend.prefs similarity index 100% rename from org.agileware.natural.common/.settings/org.eclipse.xtend.core.Xtend.prefs rename to org.agileware.natural.lang.ide/.settings/org.eclipse.xtend.core.Xtend.prefs diff --git a/org.agileware.natural.lang.ide/META-INF/MANIFEST.MF b/org.agileware.natural.lang.ide/META-INF/MANIFEST.MF new file mode 100644 index 00000000..bd51a4b2 --- /dev/null +++ b/org.agileware.natural.lang.ide/META-INF/MANIFEST.MF @@ -0,0 +1,26 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.agileware.natural.lang.ide +Bundle-ManifestVersion: 2 +Bundle-Name: org.agileware.natural.lang.ide +Bundle-Vendor: Roberto Lo Giacco +Bundle-Version: 1.0.0.qualifier +Bundle-SymbolicName: org.agileware.natural.lang.ide; singleton:=true +Bundle-ActivationPolicy: lazy +Require-Bundle: org.agileware.natural.lang, + org.eclipse.xtext.ide, + org.eclipse.xtext.xbase.ide, + org.antlr.runtime;bundle-version="[3.2.0,3.2.1)" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.agileware.natural.lang.ide;uses:="com.google.inject,org.eclipse.xtext.ide", + org.agileware.natural.lang.ide.contentassist.antlr; + uses:="org.eclipse.xtext.ide.editor.contentassist.antlr, + org.eclipse.xtext.ide.editor.contentassist.antlr.internal, + org.eclipse.xtext.ide.editor.partialEditing, + org.agileware.natural.lang.ide.contentassist.antlr.internal, + org.agileware.natural.lang.services, + org.eclipse.xtext", + org.agileware.natural.lang.ide.contentassist.antlr.internal; + uses:="org.eclipse.xtext.ide.editor.contentassist.antlr.internal, + org.agileware.natural.lang.services, + org.antlr.runtime, + org.eclipse.xtext" diff --git a/org.agileware.natural.lang.ide/build.properties b/org.agileware.natural.lang.ide/build.properties new file mode 100644 index 00000000..5c6bbf99 --- /dev/null +++ b/org.agileware.natural.lang.ide/build.properties @@ -0,0 +1,6 @@ +source.. = src/,\ + src-gen/,\ + xtend-gen/ +bin.includes = .,\ + META-INF/ +bin.excludes = **/*.xtend diff --git a/org.agileware.natural.lang.ide/pom.xml b/org.agileware.natural.lang.ide/pom.xml new file mode 100644 index 00000000..4ccc54fd --- /dev/null +++ b/org.agileware.natural.lang.ide/pom.xml @@ -0,0 +1,155 @@ + + 4.0.0 + + org.agileware + natural + 1.0.0-SNAPSHOT + + org.agileware.natural.lang.ide + eclipse-plugin + + + + log4j + log4j + + + org.eclipse.lsp4j + org.eclipse.lsp4j + + + org.ow2.asm + asm + + + org.ow2.asm + asm-commons + + + org.ow2.asm + asm-tree + + + + + + org.eclipse.xtend + xtend-maven-plugin + + + org.eclipse.tycho + target-platform-configuration + + consider + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + copy-dependencies + package + + copy-dependencies + + + p2.eclipse-feature + ${project.build.directory}/libs + false + false + true + true + + com.ibm.icu, + org.apache.ant, + org.apache.commons.lang, + org.apache.commons.logging, + org.eclipse.core.commands, + org.eclipse.core.contenttype, + org.eclipse.core.expressions, + org.eclipse.core.filesystem, + org.eclipse.core.jobs, + org.eclipse.core.resources, + org.eclipse.core.runtime, + org.eclipse.core.variables, + org.eclipse.debug.core, + org.eclipse.emf.codegen.ecore, + org.eclipse.emf.codegen, + org.eclipse.emf.mwe.core, + org.eclipse.emf.mwe.utils, + org.eclipse.emf.mwe2.lib, + org.eclipse.emf.mwe2.runtime, + org.eclipse.equinox.app, + org.eclipse.equinox.preferences, + org.eclipse.equinox.registry, + org.eclipse.jdt.core, + org.eclipse.jdt.debug, + org.eclipse.jdt.launching, + org.eclipse.text, + + + + + + + com.googlecode.addjars-maven-plugin + addjars-maven-plugin + 1.0.5 + + + package + + add-jars + + + + + ${project.build.directory}/libs + + + + + + + + org.codehaus.mojo + appassembler-maven-plugin + 2.1.0 + + + package + + assemble + + + ${project.build.directory}/languageserver + flat + true + + + + mydsl-ls + org.eclipse.xtext.ide.server.ServerLauncher + + + + + + + + + + + 1.0.0-SNAPSHOT + diff --git a/org.agileware.natural.lang.ide/src/org/agileware/natural/lang/ide/NaturalIdeModule.java b/org.agileware.natural.lang.ide/src/org/agileware/natural/lang/ide/NaturalIdeModule.java new file mode 100644 index 00000000..9e144eda --- /dev/null +++ b/org.agileware.natural.lang.ide/src/org/agileware/natural/lang/ide/NaturalIdeModule.java @@ -0,0 +1,11 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ide; + + +/** + * Use this class to register ide components. + */ +public class NaturalIdeModule extends AbstractNaturalIdeModule { +} diff --git a/org.agileware.natural.lang.ide/src/org/agileware/natural/lang/ide/NaturalIdeSetup.java b/org.agileware.natural.lang.ide/src/org/agileware/natural/lang/ide/NaturalIdeSetup.java new file mode 100644 index 00000000..ec68a58f --- /dev/null +++ b/org.agileware.natural.lang.ide/src/org/agileware/natural/lang/ide/NaturalIdeSetup.java @@ -0,0 +1,22 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ide; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.agileware.natural.lang.NaturalRuntimeModule; +import org.agileware.natural.lang.NaturalStandaloneSetup; +import org.eclipse.xtext.util.Modules2; + +/** + * Initialization support for running Xtext languages as language servers. + */ +public class NaturalIdeSetup extends NaturalStandaloneSetup { + + @Override + public Injector createInjector() { + return Guice.createInjector(Modules2.mixin(new NaturalRuntimeModule(), new NaturalIdeModule())); + } + +} diff --git a/org.agileware.natural.lang.tests/.classpath b/org.agileware.natural.lang.tests/.classpath new file mode 100644 index 00000000..3c559cfb --- /dev/null +++ b/org.agileware.natural.lang.tests/.classpath @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang.tests/.gitignore b/org.agileware.natural.lang.tests/.gitignore new file mode 100644 index 00000000..e75a396d --- /dev/null +++ b/org.agileware.natural.lang.tests/.gitignore @@ -0,0 +1 @@ +/test-bin/ diff --git a/org.agileware.natural.lang.tests/.project b/org.agileware.natural.lang.tests/.project new file mode 100644 index 00000000..960c8127 --- /dev/null +++ b/org.agileware.natural.lang.tests/.project @@ -0,0 +1,40 @@ + + + org.agileware.natural.lang.tests + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.m2e.core.maven2Nature + + diff --git a/org.agileware.natural.stepmatcher.feature/.settings/org.eclipse.core.resources.prefs b/org.agileware.natural.lang.tests/.settings/org.eclipse.core.resources.prefs similarity index 100% rename from org.agileware.natural.stepmatcher.feature/.settings/org.eclipse.core.resources.prefs rename to org.agileware.natural.lang.tests/.settings/org.eclipse.core.resources.prefs diff --git a/org.agileware.natural.lang.tests/.settings/org.eclipse.jdt.core.prefs b/org.agileware.natural.lang.tests/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..9f6ece88 --- /dev/null +++ b/org.agileware.natural.lang.tests/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.agileware.natural.lang.tests/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.agileware.natural.lang.tests/.settings/org.eclipse.xtend.core.Xtend.prefs new file mode 100644 index 00000000..fdf3191a --- /dev/null +++ b/org.agileware.natural.lang.tests/.settings/org.eclipse.xtend.core.Xtend.prefs @@ -0,0 +1,7 @@ +//outlet.DEFAULT_OUTPUT.sourceFolder.src/main/java.directory=xtend-gen +//outlet.DEFAULT_OUTPUT.sourceFolder.src/test/java.directory=xtend-gen +BuilderConfiguration.is_project_specific=true +eclipse.preferences.version=1 +outlet.DEFAULT_OUTPUT.hideLocalSyntheticVariables=true +outlet.DEFAULT_OUTPUT.installDslAsPrimarySource=false +outlet.DEFAULT_OUTPUT.userOutputPerSourceFolder=true diff --git a/org.agileware.natural.lang.tests/META-INF/MANIFEST.MF b/org.agileware.natural.lang.tests/META-INF/MANIFEST.MF new file mode 100644 index 00000000..f9e80353 --- /dev/null +++ b/org.agileware.natural.lang.tests/META-INF/MANIFEST.MF @@ -0,0 +1,23 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.agileware.natural.lang.tests +Bundle-ManifestVersion: 2 +Bundle-Name: org.agileware.natural.lang.tests +Bundle-Vendor: Roberto Lo Giacco +Bundle-Version: 1.0.0.qualifier +Bundle-SymbolicName: org.agileware.natural.lang.tests; singleton:=true +Bundle-ActivationPolicy: lazy +Require-Bundle: org.agileware.natural.lang, + org.agileware.natural.testing, + org.eclipse.xtext.testing, + org.eclipse.xtext.xbase.testing, + org.eclipse.xtext.xbase.lib;bundle-version="2.14.0" +Import-Package: org.apache.commons.logging, + org.apache.log4j, + org.junit, + org.junit.runner, + org.junit.runner.manipulation, + org.junit.runner.notification, + org.junit.runners, + org.junit.runners.model +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.agileware.natural.lang.tests;x-internal=true diff --git a/org.agileware.natural.lang.tests/build.properties b/org.agileware.natural.lang.tests/build.properties new file mode 100644 index 00000000..5c6bbf99 --- /dev/null +++ b/org.agileware.natural.lang.tests/build.properties @@ -0,0 +1,6 @@ +source.. = src/,\ + src-gen/,\ + xtend-gen/ +bin.includes = .,\ + META-INF/ +bin.excludes = **/*.xtend diff --git a/org.agileware.natural.lang.tests/org.agileware.natural.lang.tests.launch b/org.agileware.natural.lang.tests/org.agileware.natural.lang.tests.launch new file mode 100644 index 00000000..0bdf6da2 --- /dev/null +++ b/org.agileware.natural.lang.tests/org.agileware.natural.lang.tests.launch @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang.tests/pom.xml b/org.agileware.natural.lang.tests/pom.xml new file mode 100644 index 00000000..51914d85 --- /dev/null +++ b/org.agileware.natural.lang.tests/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + org.agileware + natural + 1.0.0-SNAPSHOT + + org.agileware.natural.lang.tests + eclipse-test-plugin + + + + + org.eclipse.xtend + xtend-maven-plugin + + + org.eclipse.tycho + tycho-surefire-plugin + + org.agileware.natural.lang.tests + org.agileware.natural.lang.tests.NaturalTestSuite + + + + + + diff --git a/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalExamplesTest.xtend b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalExamplesTest.xtend new file mode 100644 index 00000000..082027cf --- /dev/null +++ b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalExamplesTest.xtend @@ -0,0 +1,134 @@ +package org.agileware.natural.lang.tests + +import org.agileware.natural.lang.model.NaturalModel +import org.agileware.natural.testing.AbstractExamplesTest +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.XtextRunner +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(XtextRunner) +@InjectWith(NaturalInjectorProvider) +class NaturalExamplesTest extends AbstractExamplesTest { + + @Test + def void example_01() { + assertExampleParses(''' + Document: Hello, Natural! + ''') + } + + @Test + def void example_02() { + assertExampleParses(''' + # language: en + Document: Hello, Natural! + ''') + } + + @Test + def void example_03() { + assertExampleParses(''' + Document: + Section: B + + Section: + ''') + } + + @Test + def void example_04() { + assertExampleParses(''' + Document: Hello, Natural! + + The quick brown fox + Jumps over the lazy dog + ''') + } + + @Test + def void example_05() { + assertExampleParses(''' + Document: Hello, Natural! + + The quick brown fox + Jumps over the lazy dog + ''') + } + + @Test + def void example_06() { + assertExampleParses(''' + Document: + The quick brown fox + + Jumps over the lazy dog + ''') + } + + @Test + def void example_07() { + assertExampleParses(''' + Document: Hello, Natural! + + The quick brown fox + Jumps over the lazy dog + + ''') + } + + @Test + def void example_08() { + assertExampleParses(''' + # language: en + + Document: Hello, Natural! + + Section: A + + The quick brown fox + + Jumps over the lazy dog + + + Section: B + + The quick brown fox + + Jumps over the lazy dog + + Section: A B + The quick brown fox + + Jumps over the lazy dog + + ''') + } + + @Test + def void example_09() { + assertExampleParses(''' + # language: en + @version:1 + Document: Hello, Natural! + + @foo + @bar + Section: A + + @foo @bar + + Section: B + ''') + } + + @Test + def void example_10() { + assertExampleParses(''' + Document: Hello, ASCII Punctuation! + ,./;'[]\-= + <>?:"{}|_+ + !@$%^&*()`~ + ''') + } +} diff --git a/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalFormatterTest.xtend b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalFormatterTest.xtend new file mode 100644 index 00000000..1c5ceebb --- /dev/null +++ b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalFormatterTest.xtend @@ -0,0 +1,402 @@ +package org.agileware.natural.lang.tests + +import org.agileware.natural.lang.model.NaturalModel +import org.agileware.natural.testing.AbstractFormatterTest +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.XtextRunner +import org.junit.Test +import org.junit.runner.RunWith +import org.eclipse.xtext.formatting2.FormatterPreferenceKeys + +@RunWith(XtextRunner) +@InjectWith(NaturalInjectorProvider) +class NaturalFormatterTest extends AbstractFormatterTest { + + @Test + def void cleanupTitleText() { + val toBeFormatted = ''' + # language: en + Document: Hello, Natural Formatter ! + + Section: A + + Section: + The quick brown fox + Jumps over the lazy dog + ''' + val expectation = ''' + # language: en + Document: Hello, Natural Formatter ! + + Section: A + + Section: + The quick brown fox + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void indentNarrative_01() { + val toBeFormatted = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted) + } + + @Test + def void indentNarrative_02() { + val toBeFormatted = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + ''' + val expectation = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void indentNarrative_03() { + val toBeFormatted = ''' + # language: en + Document: + + * 1 + * 1.1 + * 1.2 + * 2 + * 2.1 + ''' + assertFormatted(toBeFormatted) + } + + @Test + def void indentNarrative_04() { + val toBeFormatted = ''' + # language: en + Document: + + * 1 + * 1.1 + * 1.2 + * 2 + * 2.1 + ''' + val expectation = ''' + # language: en + Document: + + * 1 + * 1.1 + * 1.2 + * 2 + * 2.1 + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void indentNarrative_05() { + val toBeFormatted = ''' + # language: en + Document: + + 1) + * 1.1 + * 1.2 + + 2) + * 2.1 + * 2.2 + + 3) + * 3.1 + * 3.2 + ''' + val expectation = ''' + # language: en + Document: + + 1) + * 1.1 + * 1.2 + + 2) + * 2.1 + * 2.2 + + 3) + * 3.1 + * 3.2 + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void indentNarrative_06() { + val toBeFormatted = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + | a | 0 | + | b | 1 | + + """ + ,./;'[]\-= + <>?:"{}|_+ + !@#$%^&*()`~ + """ + ''' + val expectation = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + | a | 0 | + | b | 1 | + + """ + ,./;'[]\-= + <>?:"{}|_+ + !@#$%^&*()`~ + """ + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void indentSections_01() { + val toBeFormatted = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + Section: + The quick brown fox + + Section: + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted) + } + + @Test + def void indentSections_02() { + val toBeFormatted = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + Section: + The quick brown fox + + Section: + Jumps over the lazy dog + ''' + val expectation = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + Section: + The quick brown fox + + Section: + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void indentSections_03() { + val toBeFormatted = ''' + # language: en + Document: + + @foo + Section: A + + @foo + @bar + Section: B + The quick brown fox + Jumps over the lazy dog + ''' + val expectation = ''' + # language: en + Document: + + @foo + Section: A + + @foo + @bar + Section: B + The quick brown fox + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void adjustBlockSpacing_01() { + val toBeFormatted = ''' + # language: en + Document: + The quick brown fox + Jumps over the lazy dog + Section: + The quick brown fox + + Section: + Jumps over the lazy dog + ''' + val expectation = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + Section: + The quick brown fox + + Section: + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void adjustBlockSpacing_02() { + val toBeFormatted = ''' + # language: en + Document: + + + + The quick brown fox + Jumps over the lazy dog + + + + + Section: + + + The quick brown fox + + Section: + + + + Jumps over the lazy dog + ''' + val expectation = ''' + # language: en + Document: + + The quick brown fox + Jumps over the lazy dog + + Section: + + The quick brown fox + + Section: + + Jumps over the lazy dog + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void cleanupMetaTags_01() { + val toBeFormatted = ''' + # language: en + @foo @bar + + @foobar + Document: + ''' + val expectation = ''' + # language: en + @foo + @bar + @foobar + Document: + ''' + assertFormatted(toBeFormatted, expectation) + } + + @Test + def void cleanupMetaTags_02() { + val toBeFormatted = ''' + # language: en + Document: + + @foo @bar + + @foobar + Section: + The quick brown fox + ''' + val expectation = ''' + # language: en + Document: + + @foo + @bar + @foobar + Section: + The quick brown fox + ''' + assertFormatted(toBeFormatted, expectation) + } + +// TODO add support for user indentation preferences +// @Test +// def void indentWithSpaces_01() { +// formatterTestHelper.assertFormatted[ +// preferences[ put(FormatterPreferenceKeys.indentation, " ") ] +// toBeFormatted = ''' +// # language: en +// Document: +// +// The quick brown fox +// Jumps over the lazy dog +// ''' +// expectation = ''' +// # language: en +// Document: +// +// The quick brown fox +// Jumps over the lazy dog +// ''' +// ] +// } +} diff --git a/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalParsingTest.xtend b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalParsingTest.xtend new file mode 100644 index 00000000..20c4890f --- /dev/null +++ b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalParsingTest.xtend @@ -0,0 +1,174 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.tests + +import com.google.inject.Inject +import org.agileware.natural.lang.model.DocString +import org.agileware.natural.lang.model.NaturalModel +import org.agileware.natural.lang.model.Table +import org.agileware.natural.lang.model.Paragraph +import org.agileware.natural.lang.serializer.NaturalSerializer +import org.agileware.natural.testing.AbstractExamplesTest +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.XtextRunner +import org.junit.Test +import org.junit.runner.RunWith + +import static org.hamcrest.MatcherAssert.* +import static org.hamcrest.Matchers.* + +@RunWith(XtextRunner) +@InjectWith(NaturalInjectorProvider) +class NaturalParsingTest extends AbstractExamplesTest { + + @Inject extension NaturalSerializer + + @Test + def void documentWithTitle() { + + val model = parse(''' + # language: en + Document: Hello, Natural! + ''') + + assertThat(model, notNullValue()) + assertThat(validate(model), empty()) + + val doc = model.document + assertThat(doc.sections, hasSize(0)) + assertThat(doc, notNullValue()) + assertThat(doc.title, equalTo("Hello, Natural!")) + } + + @Test + def void simpleDocumentNarrative() { + + val model = parse(''' + # language: en + Document: + The quick brown fox + Jumps over the lazy dog + ''') + + assertThat(model, notNullValue()) + assertThat(validate(model), empty()) + + val doc = model.document + assertThat(doc, notNullValue()) + assertThat(doc.narrative, notNullValue()) + assertThat(doc.narrative.sections, hasSize(1)) + + val p1 = doc.narrative.sections.get(0) as Paragraph + assertThat(serialize(p1), equalToIgnoringWhiteSpace(''' + The quick brown fox + Jumps over the lazy dog + ''')) + } + + @Test + def void complexDocumentNarrative() { + val model = parse(''' + # language: en + Document: + """ + At -9.8 m/s^2 + """ + + | x | y | + | a | 0 | + | b | 1 | + + 田中さんにあげて下さい + パーティーへ行かないか + ''') + + assertThat(model, notNullValue()) + assertThat(validate(model), empty()) + + val doc = model.document + assertThat(doc, notNullValue()) + + assertThat(doc.narrative, notNullValue()) + assertThat(doc.narrative.sections, hasSize(3)) + + val b1 = doc.narrative.sections.get(0) as DocString + assertThat(b1.value, equalToIgnoringWhiteSpace(''' + """ + At -9.8 m/s^2 + """ + ''')) + + val b2 = doc.narrative.sections.get(1) as Table + assertThat(serialize(b2), equalToIgnoringWhiteSpace(''' + | x | y | + | a | 0 | + | b | 1 | + ''')) + + val b3 = doc.narrative.sections.get(2) as Paragraph + assertThat(serialize(b3), equalToIgnoringWhiteSpace(''' + 田中さんにあげて下さい + パーティーへ行かないか + ''')) + } + + @Test + def void parseMetaData() { + val model = parse(''' + # language: en + @foo @bar + @version:v1 + @release:2.0 + Document: + ''') + + assertThat(model, notNullValue()) + assertThat(validate(model), empty()) + + val doc = model.document + assertThat(doc, notNullValue()) + assertThat(doc.meta, notNullValue()) + assertThat(doc.meta.tags, hasSize(4)) + } + + @Test + def void multipleSectionsWithMetaTags() { + val model = parse(''' + # language: en + # @title: Hello, World! + Document: + The quick brown fox + Jumps over the lazy dog + + @foo + @bar + Section: A + + @foo @bar + + Section: B + ''') + + assertThat(model, notNullValue()) + assertThat(validate(model), empty()) + + val doc = model.document + assertThat(doc, notNullValue()) + assertThat(doc.title, nullValue()) + assertThat(doc.sections, hasSize(2)) + // TODO + // assertThat(doc.meta.tags, hasSize(1)) + // assertThat(doc.meta.tags.get(0).value, equalTo("@title: Hello, Meta Tags!")) + assertThat(doc.narrative, notNullValue()) + assertThat(doc.narrative.sections, hasSize(1)) + + val p1 = doc.narrative.sections.get(0) as Paragraph + assertThat(serialize(p1), equalToIgnoringWhiteSpace(''' + The quick brown fox + Jumps over the lazy dog + ''')) + assertThat(doc.sections.get(0).meta.tags, hasSize(2)) + assertThat(doc.sections.get(1).meta.tags, hasSize(2)) + } +} diff --git a/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalTestSuite.java b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalTestSuite.java new file mode 100644 index 00000000..52b63398 --- /dev/null +++ b/org.agileware.natural.lang.tests/src/org/agileware/natural/lang/tests/NaturalTestSuite.java @@ -0,0 +1,8 @@ +package org.agileware.natural.lang.tests; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ NaturalExamplesTest.class, NaturalParsingTest.class, NaturalFormatterTest.class }) +public class NaturalTestSuite {} diff --git a/org.agileware.natural.lang.ui.tests/.classpath b/org.agileware.natural.lang.ui.tests/.classpath new file mode 100644 index 00000000..5cc10f4e --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/.classpath @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang.ui.tests/.project b/org.agileware.natural.lang.ui.tests/.project new file mode 100644 index 00000000..5d05dc79 --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/.project @@ -0,0 +1,40 @@ + + + org.agileware.natural.lang.ui.tests + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.m2e.core.maven2Nature + + diff --git a/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.core.resources.prefs b/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..99f26c02 --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.jdt.core.prefs b/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..9f6ece88 --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.xtend.core.Xtend.prefs new file mode 100644 index 00000000..fdf3191a --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/.settings/org.eclipse.xtend.core.Xtend.prefs @@ -0,0 +1,7 @@ +//outlet.DEFAULT_OUTPUT.sourceFolder.src/main/java.directory=xtend-gen +//outlet.DEFAULT_OUTPUT.sourceFolder.src/test/java.directory=xtend-gen +BuilderConfiguration.is_project_specific=true +eclipse.preferences.version=1 +outlet.DEFAULT_OUTPUT.hideLocalSyntheticVariables=true +outlet.DEFAULT_OUTPUT.installDslAsPrimarySource=false +outlet.DEFAULT_OUTPUT.userOutputPerSourceFolder=true diff --git a/org.agileware.natural.lang.ui.tests/META-INF/MANIFEST.MF b/org.agileware.natural.lang.ui.tests/META-INF/MANIFEST.MF new file mode 100644 index 00000000..40d4784c --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.agileware.natural.lang.ui.tests +Bundle-ManifestVersion: 2 +Bundle-Name: org.agileware.natural.lang.ui.tests +Bundle-Vendor: Roberto Lo Giacco +Bundle-Version: 1.0.0.qualifier +Bundle-SymbolicName: org.agileware.natural.lang.ui.tests; singleton:=true +Bundle-ActivationPolicy: lazy +Require-Bundle: org.agileware.natural.lang.ui, + org.junit;bundle-version="4.12.0", + org.eclipse.xtext.testing, + org.eclipse.xtext.xbase.testing, + org.eclipse.xtext.junit4, + org.eclipse.xtext.xbase.junit, + org.eclipse.core.runtime, + org.eclipse.xtext.ui.testing, + org.eclipse.ui.workbench;resolution:=optional +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.agileware.natural.lang.ui.tests;x-internal=true diff --git a/org.agileware.natural.lang.ui.tests/build.properties b/org.agileware.natural.lang.ui.tests/build.properties new file mode 100644 index 00000000..5c6bbf99 --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/build.properties @@ -0,0 +1,6 @@ +source.. = src/,\ + src-gen/,\ + xtend-gen/ +bin.includes = .,\ + META-INF/ +bin.excludes = **/*.xtend diff --git a/org.agileware.natural.lang.ui.tests/pom.xml b/org.agileware.natural.lang.ui.tests/pom.xml new file mode 100644 index 00000000..5f1b881a --- /dev/null +++ b/org.agileware.natural.lang.ui.tests/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + + org.agileware + natural + 1.0.0-SNAPSHOT + + org.agileware.natural.lang.ui.tests + eclipse-test-plugin + + + + + org.eclipse.xtend + xtend-maven-plugin + + + org.eclipse.tycho + tycho-surefire-plugin + + true + + + + + + 1.0.0-SNAPSHOT + diff --git a/org.agileware.natural.lang.ui/.classpath b/org.agileware.natural.lang.ui/.classpath new file mode 100644 index 00000000..8d26fa59 --- /dev/null +++ b/org.agileware.natural.lang.ui/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/org.agileware.natural.lang.ui/.project b/org.agileware.natural.lang.ui/.project new file mode 100644 index 00000000..2da58228 --- /dev/null +++ b/org.agileware.natural.lang.ui/.project @@ -0,0 +1,40 @@ + + + org.agileware.natural.lang.ui + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.m2e.core.maven2Nature + + diff --git a/org.agileware.natural.lang.ui/.settings/org.eclipse.core.resources.prefs b/org.agileware.natural.lang.ui/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..99f26c02 --- /dev/null +++ b/org.agileware.natural.lang.ui/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/org.agileware.natural.lang.ui/.settings/org.eclipse.jdt.core.prefs b/org.agileware.natural.lang.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..9f6ece88 --- /dev/null +++ b/org.agileware.natural.lang.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.agileware.natural.lang.ui/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.agileware.natural.lang.ui/.settings/org.eclipse.xtend.core.Xtend.prefs new file mode 100644 index 00000000..fdf3191a --- /dev/null +++ b/org.agileware.natural.lang.ui/.settings/org.eclipse.xtend.core.Xtend.prefs @@ -0,0 +1,7 @@ +//outlet.DEFAULT_OUTPUT.sourceFolder.src/main/java.directory=xtend-gen +//outlet.DEFAULT_OUTPUT.sourceFolder.src/test/java.directory=xtend-gen +BuilderConfiguration.is_project_specific=true +eclipse.preferences.version=1 +outlet.DEFAULT_OUTPUT.hideLocalSyntheticVariables=true +outlet.DEFAULT_OUTPUT.installDslAsPrimarySource=false +outlet.DEFAULT_OUTPUT.userOutputPerSourceFolder=true diff --git a/org.agileware.natural.lang.ui/META-INF/MANIFEST.MF b/org.agileware.natural.lang.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000..78ef215e --- /dev/null +++ b/org.agileware.natural.lang.ui/META-INF/MANIFEST.MF @@ -0,0 +1,24 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.agileware.natural.lang.ui +Bundle-ManifestVersion: 2 +Bundle-Name: org.agileware.natural.lang.ui +Bundle-Vendor: Roberto Lo Giacco +Bundle-Version: 1.0.0.qualifier +Bundle-SymbolicName: org.agileware.natural.lang.ui; singleton:=true +Bundle-ActivationPolicy: lazy +Require-Bundle: org.agileware.natural.lang, + org.agileware.natural.lang.ide, + org.eclipse.xtext.ui, + org.eclipse.xtext.ui.shared, + org.eclipse.xtext.ui.codetemplates.ui, + org.eclipse.ui.editors;bundle-version="3.5.0", + org.eclipse.ui.ide;bundle-version="3.5.0", + org.eclipse.ui, + org.eclipse.compare, + org.eclipse.xtext.builder +Import-Package: org.apache.log4j +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.agileware.natural.lang.ui.quickfix, + org.agileware.natural.lang.ui.contentassist, + org.agileware.natural.lang.ui.internal +Bundle-Activator: org.agileware.natural.lang.ui.internal.LangActivator diff --git a/org.agileware.natural.lang.ui/build.properties b/org.agileware.natural.lang.ui/build.properties new file mode 100644 index 00000000..323f56c5 --- /dev/null +++ b/org.agileware.natural.lang.ui/build.properties @@ -0,0 +1,7 @@ +source.. = src/,\ + src-gen/,\ + xtend-gen/ +bin.includes = .,\ + META-INF/,\ + plugin.xml +bin.excludes = **/*.xtend diff --git a/org.agileware.natural.lang.ui/plugin.xml b/org.agileware.natural.lang.ui/plugin.xml new file mode 100644 index 00000000..20dc2e02 --- /dev/null +++ b/org.agileware.natural.lang.ui/plugin.xml @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.stepmatcher.feature/pom.xml b/org.agileware.natural.lang.ui/pom.xml similarity index 56% rename from org.agileware.natural.stepmatcher.feature/pom.xml rename to org.agileware.natural.lang.ui/pom.xml index 5f317f56..5a31cca6 100644 --- a/org.agileware.natural.stepmatcher.feature/pom.xml +++ b/org.agileware.natural.lang.ui/pom.xml @@ -6,10 +6,18 @@ natural 1.0.0-SNAPSHOT - org.agileware.natural.stepmatcher.feature - eclipse-feature + org.agileware.natural.lang.ui + eclipse-plugin + 1.0.0-SNAPSHOT + + + + org.eclipse.xtend + xtend-maven-plugin + + + - - + diff --git a/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/NaturalUiModule.java b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/NaturalUiModule.java new file mode 100644 index 00000000..99d95ffc --- /dev/null +++ b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/NaturalUiModule.java @@ -0,0 +1,16 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ui; + +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * Use this class to register components to be used within the Eclipse IDE. + */ +public class NaturalUiModule extends AbstractNaturalUiModule { + + public NaturalUiModule(AbstractUIPlugin plugin) { + super(plugin); + } +} diff --git a/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/contentassist/NaturalProposalProvider.java b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/contentassist/NaturalProposalProvider.java new file mode 100644 index 00000000..f6d8fa55 --- /dev/null +++ b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/contentassist/NaturalProposalProvider.java @@ -0,0 +1,12 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ui.contentassist; + + +/** + * See https://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#content-assist + * on how to customize the content assistant. + */ +public class NaturalProposalProvider extends AbstractNaturalProposalProvider { +} diff --git a/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/labeling/NaturalDescriptionLabelProvider.java b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/labeling/NaturalDescriptionLabelProvider.java new file mode 100644 index 00000000..0f84e3f6 --- /dev/null +++ b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/labeling/NaturalDescriptionLabelProvider.java @@ -0,0 +1,25 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ui.labeling; + +import org.eclipse.xtext.ui.label.DefaultDescriptionLabelProvider; + +/** + * Provides labels for IEObjectDescriptions and IResourceDescriptions. + * + * See https://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#label-provider + */ +public class NaturalDescriptionLabelProvider extends DefaultDescriptionLabelProvider { + + // Labels and icons can be computed like this: +// @Override +// public String text(IEObjectDescription ele) { +// return ele.getName().toString(); +// } +// +// @Override +// public String image(IEObjectDescription ele) { +// return ele.getEClass().getName() + ".gif"; +// } +} diff --git a/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/labeling/NaturalLabelProvider.java b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/labeling/NaturalLabelProvider.java new file mode 100644 index 00000000..320ff093 --- /dev/null +++ b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/labeling/NaturalLabelProvider.java @@ -0,0 +1,31 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ui.labeling; + +import com.google.inject.Inject; +import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; +import org.eclipse.xtext.ui.label.DefaultEObjectLabelProvider; + +/** + * Provides labels for EObjects. + * + * See https://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#label-provider + */ +public class NaturalLabelProvider extends DefaultEObjectLabelProvider { + + @Inject + public NaturalLabelProvider(AdapterFactoryLabelProvider delegate) { + super(delegate); + } + + // Labels and icons can be computed like this: + +// String text(Greeting ele) { +// return "A greeting to " + ele.getName(); +// } +// +// String image(Greeting ele) { +// return "Greeting.gif"; +// } +} diff --git a/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/outline/NaturalOutlineTreeProvider.java b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/outline/NaturalOutlineTreeProvider.java new file mode 100644 index 00000000..9688fdd6 --- /dev/null +++ b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/outline/NaturalOutlineTreeProvider.java @@ -0,0 +1,15 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ui.outline; + +import org.eclipse.xtext.ui.editor.outline.impl.DefaultOutlineTreeProvider; + +/** + * Customization of the default outline structure. + * + * See https://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#outline + */ +public class NaturalOutlineTreeProvider extends DefaultOutlineTreeProvider { + +} diff --git a/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/quickfix/NaturalQuickfixProvider.java b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/quickfix/NaturalQuickfixProvider.java new file mode 100644 index 00000000..1e189ab9 --- /dev/null +++ b/org.agileware.natural.lang.ui/src/org/agileware/natural/lang/ui/quickfix/NaturalQuickfixProvider.java @@ -0,0 +1,26 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.ui.quickfix; + +import org.eclipse.xtext.ui.editor.quickfix.DefaultQuickfixProvider; + +/** + * Custom quickfixes. + * + * See https://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#quick-fixes + */ +public class NaturalQuickfixProvider extends DefaultQuickfixProvider { + +// @Fix(NaturalValidator.INVALID_NAME) +// public void capitalizeName(final Issue issue, IssueResolutionAcceptor acceptor) { +// acceptor.accept(issue, "Capitalize name", "Capitalize the name.", "upcase.png", new IModification() { +// public void apply(IModificationContext context) throws BadLocationException { +// IXtextDocument xtextDocument = context.getXtextDocument(); +// String firstLetter = xtextDocument.get(issue.getOffset(), 1); +// xtextDocument.replace(issue.getOffset(), 1, firstLetter.toUpperCase()); +// } +// }); +// } + +} diff --git a/org.agileware.natural.lang/.classpath b/org.agileware.natural.lang/.classpath new file mode 100644 index 00000000..8d26fa59 --- /dev/null +++ b/org.agileware.natural.lang/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/org.agileware.natural.common/.project b/org.agileware.natural.lang/.project similarity index 95% rename from org.agileware.natural.common/.project rename to org.agileware.natural.lang/.project index 4cad5653..dfb2951c 100644 --- a/org.agileware.natural.common/.project +++ b/org.agileware.natural.lang/.project @@ -1,6 +1,6 @@ - org.agileware.natural.common + org.agileware.natural.lang @@ -32,9 +32,9 @@ - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature org.eclipse.m2e.core.maven2Nature diff --git a/org.agileware.natural.lang/.settings/org.eclipse.core.resources.prefs b/org.agileware.natural.lang/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..99f26c02 --- /dev/null +++ b/org.agileware.natural.lang/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/org.agileware.natural.lang/.settings/org.eclipse.jdt.core.prefs b/org.agileware.natural.lang/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..9f6ece88 --- /dev/null +++ b/org.agileware.natural.lang/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.agileware.natural.lang/.settings/org.eclipse.pde.core.prefs b/org.agileware.natural.lang/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 00000000..923c37fb --- /dev/null +++ b/org.agileware.natural.lang/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +resolve.requirebundle=false diff --git a/org.agileware.natural.lang/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.agileware.natural.lang/.settings/org.eclipse.xtend.core.Xtend.prefs new file mode 100644 index 00000000..fdf3191a --- /dev/null +++ b/org.agileware.natural.lang/.settings/org.eclipse.xtend.core.Xtend.prefs @@ -0,0 +1,7 @@ +//outlet.DEFAULT_OUTPUT.sourceFolder.src/main/java.directory=xtend-gen +//outlet.DEFAULT_OUTPUT.sourceFolder.src/test/java.directory=xtend-gen +BuilderConfiguration.is_project_specific=true +eclipse.preferences.version=1 +outlet.DEFAULT_OUTPUT.hideLocalSyntheticVariables=true +outlet.DEFAULT_OUTPUT.installDslAsPrimarySource=false +outlet.DEFAULT_OUTPUT.userOutputPerSourceFolder=true diff --git a/org.agileware.natural.lang/Generate Natural Language Infrastructure.launch b/org.agileware.natural.lang/Generate Natural Language Infrastructure.launch new file mode 100644 index 00000000..b16ec578 --- /dev/null +++ b/org.agileware.natural.lang/Generate Natural Language Infrastructure.launch @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang/META-INF/MANIFEST.MF b/org.agileware.natural.lang/META-INF/MANIFEST.MF new file mode 100644 index 00000000..7985be1d --- /dev/null +++ b/org.agileware.natural.lang/META-INF/MANIFEST.MF @@ -0,0 +1,54 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.agileware.natural.lang +Bundle-ManifestVersion: 2 +Bundle-Name: org.agileware.natural.lang +Bundle-Vendor: Roberto Lo Giacco +Bundle-Version: 1.0.0.qualifier +Bundle-SymbolicName: org.agileware.natural.lang; singleton:=true +Bundle-ActivationPolicy: lazy +Require-Bundle: org.eclipse.xtext, + org.eclipse.xtext.xbase, + org.eclipse.equinox.common;bundle-version="3.5.0", + org.eclipse.emf.ecore, + org.eclipse.xtext.xbase.lib;bundle-version="2.14.0", + org.eclipse.xtext.util, + org.eclipse.emf.common, + org.antlr.runtime;bundle-version="[3.2.0,3.2.1)" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.agileware.natural.lang;uses:="com.google.inject,org.eclipse.xtext.service,org.eclipse.xtext", + org.agileware.natural.lang.formatting2; + uses:="org.eclipse.emf.ecore, + org.agileware.natural.lang.model, + org.eclipse.xtext.formatting2.regionaccess, + org.eclipse.xtext.formatting2.regionaccess.internal, + org.agileware.natural.lang.services, + org.eclipse.xtext.xbase.lib, + org.eclipse.xtext.formatting2, + org.eclipse.xtext", + org.agileware.natural.lang.generator;uses:="org.eclipse.xtext.generator,org.eclipse.emf.ecore.resource", + org.agileware.natural.lang.model;uses:="org.eclipse.emf.ecore,org.eclipse.emf.common.util", + org.agileware.natural.lang.model.impl; + uses:="org.eclipse.emf.ecore, + org.eclipse.emf.ecore.impl, + org.eclipse.emf.common.util, + org.agileware.natural.lang.model, + org.eclipse.emf.common.notify", + org.agileware.natural.lang.model.util; + uses:="org.eclipse.emf.ecore, + org.agileware.natural.lang.model, + org.eclipse.emf.common.notify, + org.eclipse.emf.common.notify.impl, + org.eclipse.emf.ecore.util", + org.agileware.natural.lang.parser.antlr;uses:="org.eclipse.xtext.parser.antlr,org.agileware.natural.lang.services,org.agileware.natural.lang.parser.antlr.internal", + org.agileware.natural.lang.parser.antlr.internal; + uses:="org.eclipse.emf.ecore, + org.eclipse.xtext.parser.antlr, + org.agileware.natural.lang.services, + org.antlr.runtime", + org.agileware.natural.lang.scoping;uses:="org.eclipse.xtext.scoping.impl", + org.agileware.natural.lang.serializer;uses:="org.agileware.natural.lang.model", + org.agileware.natural.lang.services;uses:="org.eclipse.xtext.service,org.eclipse.xtext", + org.agileware.natural.lang.text;uses:="org.eclipse.xtext.conversion,org.eclipse.xtext.nodemodel,org.eclipse.xtext.conversion.impl", + org.agileware.natural.lang.validation;uses:="org.eclipse.xtext.util,org.eclipse.xtext.validation" +Import-Package: org.apache.commons.lang;version="2.6.0", + org.apache.log4j diff --git a/org.agileware.natural.lang/README.md b/org.agileware.natural.lang/README.md new file mode 100644 index 00000000..65b887de --- /dev/null +++ b/org.agileware.natural.lang/README.md @@ -0,0 +1,5 @@ +# org.agileware.natural.lang + +Core language support for Cucumber and Jbehave modules + +![Natural AST](natural.model.jpg) diff --git a/org.agileware.natural.lang/build.properties b/org.agileware.natural.lang/build.properties new file mode 100644 index 00000000..18d540bf --- /dev/null +++ b/org.agileware.natural.lang/build.properties @@ -0,0 +1,20 @@ +source.. = src/,\ + src-gen/,\ + xtend-gen/ +bin.includes = model/generated/,\ + .,\ + META-INF/,\ + plugin.xml +bin.excludes = **/*.mwe2,\ + **/*.xtend +additional.bundles = org.eclipse.xtext.xbase,\ + org.eclipse.xtext.common.types,\ + org.eclipse.xtext.xtext.generator,\ + org.eclipse.emf.codegen.ecore,\ + org.eclipse.emf.mwe.utils,\ + org.eclipse.emf.mwe2.launch,\ + org.eclipse.emf.mwe2.lib,\ + org.objectweb.asm,\ + org.apache.commons.logging,\ + org.apache.log4j,\ + com.ibm.icu diff --git a/org.agileware.natural.lang/model/generated/Natural.ecore b/org.agileware.natural.lang/model/generated/Natural.ecore new file mode 100644 index 00000000..5501b9f1 --- /dev/null +++ b/org.agileware.natural.lang/model/generated/Natural.ecore @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang/model/generated/Natural.genmodel b/org.agileware.natural.lang/model/generated/Natural.genmodel new file mode 100644 index 00000000..7db260e0 --- /dev/null +++ b/org.agileware.natural.lang/model/generated/Natural.genmodel @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang/model/representations.aird b/org.agileware.natural.lang/model/representations.aird new file mode 100644 index 00000000..e07bebbd --- /dev/null +++ b/org.agileware.natural.lang/model/representations.aird @@ -0,0 +1,722 @@ + + + + generated/Natural.ecore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + labelSize + + + labelSize + + + + + + + + + + + italic + + + + + + + + + + + + italic + + + + + + + + + + + + italic + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang/natural.model.jpg b/org.agileware.natural.lang/natural.model.jpg new file mode 100644 index 00000000..9bb1fe04 Binary files /dev/null and b/org.agileware.natural.lang/natural.model.jpg differ diff --git a/org.agileware.natural.lang/plugin.xml b/org.agileware.natural.lang/plugin.xml new file mode 100644 index 00000000..96a07481 --- /dev/null +++ b/org.agileware.natural.lang/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/org.agileware.natural.lang/pom.xml b/org.agileware.natural.lang/pom.xml new file mode 100644 index 00000000..dba27879 --- /dev/null +++ b/org.agileware.natural.lang/pom.xml @@ -0,0 +1,136 @@ + + 4.0.0 + + org.agileware + natural + 1.0.0-SNAPSHOT + + org.agileware.natural.lang + eclipse-plugin + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + mwe2Launcher + generate-sources + + java + + + + + org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher + + /${project.basedir}/src/org/agileware/natural/lang/GenerateNatural.mwe2 + -p + rootPath=/${project.basedir}/.. + + compile + true + false + + + + org.eclipse.emf + org.eclipse.emf.mwe2.launch + ${mwe2.version} + + + org.eclipse.xtext + org.eclipse.xtext.common.types + ${xtext.version} + + + org.eclipse.xtext + org.eclipse.xtext.xtext.generator + ${xtext.version} + + + org.eclipse.xtext + org.eclipse.xtext.xbase + ${xtext.version} + + + org.eclipse.xtext + xtext-antlr-generator + 2.1.1 + + + + + org.eclipse.xtend + xtend-maven-plugin + + + + org.apache.maven.plugins + maven-clean-plugin + + + + ${basedir}/../org.agileware.natural.lang/src-gen/ + + **/* + + + + ${basedir}/../org.agileware.natural.lang.tests/src-gen/ + + **/* + + + + ${basedir}/../org.agileware.natural.lang.ide/src-gen/ + + **/* + + + + ${basedir}/model/generated/ + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.codehaus.mojo + + + exec-maven-plugin + + + [1.2.1,) + + + java + + + + + + + + + + + + + + diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/GenerateNatural.mwe2 b/org.agileware.natural.lang/src/org/agileware/natural/lang/GenerateNatural.mwe2 new file mode 100644 index 00000000..0f9e05b8 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/GenerateNatural.mwe2 @@ -0,0 +1,62 @@ +module org.agileware.natural.lang.GenerateNatural + +import org.eclipse.xtext.xtext.generator.* +import org.eclipse.xtext.xtext.generator.model.project.* +import org.eclipse.emf.mwe.utils.* + +var rootPath = ".." + +Workflow { + + component = XtextGenerator { + configuration = { + project = StandardProjectConfig { + baseName = "org.agileware.natural.lang" + rootPath = rootPath + runtimeTest = { + enabled = true + } + eclipsePlugin = { + enabled = true + } + eclipsePluginTest = { + enabled = true + } + createEclipseMetaData = true + } + code = { + encoding = "UTF-8" + lineDelimiter = "\n" + fileHeader = "/*\n * generated by Xtext \${version}\n */" + preferXtendStubs = false + } + } + language = StandardLanguage { + name = "org.agileware.natural.lang.Natural" + fileExtensions = "natural" + serializer = { + generateStub = true + } + validator = { + // composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator" + // Generates checks for @Deprecated grammar annotations, an IssueProvider and a corresponding PropertyPage + generateDeprecationValidation = true + } + formatter = { + generateStub = true + generateXtendStub = true + } + generator = { + generateStub = true + generateXtendStub = true + } + parserGenerator = { + debugGrammar = true + options = auto-inject { + classSplitting = true + } + } + } + + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/Natural.xtext b/org.agileware.natural.lang/src/org/agileware/natural/lang/Natural.xtext new file mode 100644 index 00000000..8ec5cefa --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/Natural.xtext @@ -0,0 +1,246 @@ +grammar org.agileware.natural.lang.Natural hidden(WS, SL_COMMENT) + +import "http://www.eclipse.org/emf/2002/Ecore" as ecore +generate model "http://www.agileware.org/natural/lang" + +/** + * + */ +NaturalModel: {NaturalModel} + document=Document? +; + +/** + * + */ +Document: {Document} + BLANK_SPACE? + (meta=Meta BLANK_SPACE?)? + 'Document:' title=RawText? NL + (BLANK_SPACE? narrative=Narrative)? + (BLANK_SPACE? sections+=Section)* + BLANK_SPACE? +; + +/** + * + */ +Section: {Section} + (meta=Meta BLANK_SPACE?)? + 'Section:' title=RawText? NL + (BLANK_SPACE? narrative=Narrative)? +; + +// -------------------------------------- +// Meta Tags +// -------------------------------------- + +/** + * + */ +Meta: {Meta} + tags+=MetaElement + (BLANK_SPACE? tags+=MetaElement)* + NL +; + +/** + * + */ +MetaElement: {MetaElement} + id=TAG +// '@' => id=ID => (':' value=RawText)? +; + +terminal TAG: + '@' !(' ' | '\t' | '\n' | '\r')+ +; + + +/** + * + */ +Narrative: {Narrative} + sections+=Block + (NL BLANK_SPACE? sections+=Block)* +; + +// -------------------------------------- +// Text Blocks +// -------------------------------------- + +/** + * + */ +Block: Paragraph + | DocString + | Table +; + +/** + * + */ +Paragraph: {Paragraph} + value=RawTextBlock + NL +; + +/** + * + */ +DocString: {DocString} + value=DOC_STRING_LITERAL + NL +; + +terminal DOC_STRING_LITERAL: + ('"""'->'"""') + | ("'''"->"'''") +; + +/** + * + */ +Table: {Table} + rows+=TableRow+ +; + +/** + * + */ +TableRow: {TableRow} + cols+=TableCol+ '|' NL +; + +/** + * + */ +TableCol: {TableCol} + value=TABLE_CELL +; + +terminal TABLE_CELL: + '|' !('|' | '\n' | '\r')* +; + +// -------------------------------------- +// Text Literals +// -------------------------------------- + +/** + * Consumes all text literals. This SHOULD be assigned after a start literal or keyword + * In the production rules. + */ +RawText returns ecore::EString: + Literal+ +; + +/** + * Consumes all text literals, including a single newline prior to another line + * of text. Each line MUST not start with any special keywords. All text literals + * are consumed until multiple newlines, or a keyword starting a new Block is + * encoutered in the model rules. + */ +RawTextBlock returns ecore::EString: + StartLiteral Literal* + (NL StartLiteral Literal*)* +; + +StartLiteral: ID + | NUMBER + | STRING + | GLOB + | ANY_OTHER +; + +Literal: ID + | NUMBER + | STRING + | GLOB + | ANY_OTHER +; + +terminal ID: + (LETTER | DIGIT) + (LETTER | DIGIT | '_' | '-')* +; + +terminal fragment LETTER: ARABIC_ALPHABET + | ARMENIAN_ALPHABET + | GREEK_ALPHABET + | CYRILLIC_ALPHABET + | HEBREW_ALPHABET + | LATIN_ALPHABET + | THAI_ALPHABET + | TAGALOG_ALPHABET + | DIACRITICAL_MARKS +; + + +// Reference: https://jrgraphix.net/r/Unicode/ +//// + +terminal fragment ARABIC_ALPHABET: ('\u0600'..'\u06FF'); +terminal fragment ARMENIAN_ALPHABET: ('\u0530'..'\u058F'); +terminal fragment GREEK_ALPHABET: ('\u0370'..'\u03FF'); +terminal fragment CYRILLIC_ALPHABET: ('\u0400'..'\u04FF'); +terminal fragment HEBREW_ALPHABET: ('\u0590'..'\u05FF'); +terminal fragment LATIN_ALPHABET: ('\u0041'..'\u005A') + | ('\u0061'..'\u007A') +; +terminal fragment THAI_ALPHABET: ('\u0E00'..'\u0E7F'); +terminal fragment TAGALOG_ALPHABET: ('\u0400'..'\u04FF'); +terminal fragment DIACRITICAL_MARKS: ('\u0300'..'\u036F'); + +terminal STRING returns ecore::EString: + '"' ('\\' ('b' | 't' | 'n' | 'f' | 'r' | 'u' | '"' | '\\') | !('\\' | '"' | '\r' | '\n'))* '"' | + "'" ('\\' ('b' | 't' | 'n' | 'f' | 'r' | 'u' | "'" | '\\') | !('\\' | "'" | '\r' | '\n'))* "'" +; + +terminal NUMBER returns ecore::EBigInteger: + '-'? DIGIT+ '.' DIGIT* EXPONENT_PART? + | '-'? '.' DIGIT+ EXPONENT_PART? + | '-'? DIGIT+ EXPONENT_PART + | '-'? DECIMAL_NUMBER + | '-'? HEX_NUMBER + | '-'? OCTAL_NUMBER +; + +terminal fragment DECIMAL_NUMBER: + NON_ZERO_DIGIT DIGIT* +; + +terminal fragment DIGIT: + '0' | NON_ZERO_DIGIT +; + +terminal fragment NON_ZERO_DIGIT: + '1'..'9' +; + +terminal fragment HEX_NUMBER: + '0' ('x' | 'X') ('0'..'9' | 'a'..'f' | 'A'..'F')+ +; + +terminal fragment OCTAL_NUMBER: + '0' '0'..'7'* +; + +terminal fragment EXPONENT_PART: + ('e' | 'E') ('+' | '-')? DIGIT+ +; + +terminal SL_COMMENT: + '#' !('\n' | '\r')* NL +; + +terminal GLOB: + !('@' | '|' | ' ' | '\t' | '\n' | '\r') + !(' ' | '\t' | '\n' | '\r')* +; + +BLANK_SPACE: NL+; +terminal NL: ('\r'? '\n') | EOF; +terminal WS: (' ' | '\t'); + +terminal ANY_OTHER: .; diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/NaturalRuntimeModule.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/NaturalRuntimeModule.java new file mode 100644 index 00000000..89f2ac59 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/NaturalRuntimeModule.java @@ -0,0 +1,17 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang; + +import org.agileware.natural.lang.text.TextValueConverterService; +import org.eclipse.xtext.conversion.IValueConverterService; + +/** + * Use this class to register components to be used at runtime / without the Equinox extension registry. + */ +public class NaturalRuntimeModule extends AbstractNaturalRuntimeModule { + @Override + public Class bindIValueConverterService() { + return TextValueConverterService.class; + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/NaturalStandaloneSetup.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/NaturalStandaloneSetup.java new file mode 100644 index 00000000..806fd1ae --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/NaturalStandaloneSetup.java @@ -0,0 +1,15 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang; + + +/** + * Initialization support for running Xtext languages without Equinox extension registry. + */ +public class NaturalStandaloneSetup extends NaturalStandaloneSetupGenerated { + + public static void doSetup() { + new NaturalStandaloneSetup().createInjectorAndDoEMFRegistration(); + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/formatting2/NaturalFormatHelper.xtend b/org.agileware.natural.lang/src/org/agileware/natural/lang/formatting2/NaturalFormatHelper.xtend new file mode 100644 index 00000000..9c24e8d5 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/formatting2/NaturalFormatHelper.xtend @@ -0,0 +1,216 @@ +package org.agileware.natural.lang.formatting2 + +import com.google.inject.Inject +import java.util.List +import org.agileware.natural.lang.model.Meta +import org.agileware.natural.lang.model.MetaElement +import org.agileware.natural.lang.services.NaturalGrammarAccess +import org.agileware.natural.lang.text.TextLine +import org.agileware.natural.lang.text.TextModel +import org.apache.commons.lang.StringUtils +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor +import org.eclipse.xtext.Assignment +import org.eclipse.xtext.RuleCall +import org.eclipse.xtext.formatting.IIndentationInformation +import org.eclipse.xtext.formatting2.FormatterPreferenceKeys +import org.eclipse.xtext.formatting2.FormatterRequest +import org.eclipse.xtext.formatting2.IFormattableDocument +import org.eclipse.xtext.formatting2.ITextReplacer +import org.eclipse.xtext.formatting2.ITextReplacerContext +import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion +import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess +import org.eclipse.xtext.formatting2.regionaccess.ITextRegionExtensions +import org.eclipse.xtext.formatting2.regionaccess.ITextSegment +import org.eclipse.xtext.formatting2.regionaccess.internal.NodeSemanticRegion +import org.eclipse.xtext.nodemodel.util.NodeModelUtils +import org.eclipse.xtext.preferences.MapBasedPreferenceValues + +@FinalFieldsConstructor +class NaturalFormatHelper { + + static class Factory { + @Inject IIndentationInformation indentationInformation + + def NaturalFormatHelper create(ITextRegionAccess regionAccess, NaturalGrammarAccess grammerAccess) { + new NaturalFormatHelper(this, regionAccess.extensions, grammerAccess) + } + } + + val Factory factory + + val extension ITextRegionExtensions + + val extension NaturalGrammarAccess + + var int _indentationLevel = 0 + + def int getIndentationLevel() { + return _indentationLevel + } + + def void resetIndentation() { + _indentationLevel = 0 + } + + def void increaseIndent() { + _indentationLevel += 1 + } + + def void decreaseIndent() { + _indentationLevel -= 1 + } + + def void initialize(FormatterRequest request) { + val preferences = request.preferences + if (preferences instanceof MapBasedPreferenceValues) { + preferences.put(FormatterPreferenceKeys.indentation, factory.indentationInformation.indentString) + } + + _indentationLevel = 0 + } + + def void formatMultilineText(EObject owner, Assignment assignment, int indentationLevel, + extension IFormattableDocument doc) { + val region = owner.regionFor.assignment(assignment) + if (region instanceof NodeSemanticRegion) { + addReplacer(new MultilineTextReplacer(region, indentationLevel)) + } + } + + def void trimBlankSpace(EObject owner, RuleCall rule, extension IFormattableDocument doc) { + trimBlankSpace(owner, rule, 0, doc) + } + + def void trimBlankSpace(EObject owner, RuleCall rule, int newLines, extension IFormattableDocument doc) { + val region = owner.regionFor.ruleCall(rule) + if (region instanceof NodeSemanticRegion) { + addReplacer(new BlankSpaceReplacer(region, newLines)) + } + } + + def void indentBlock(ISemanticRegion start, ISemanticRegion end, extension IFormattableDocument doc) { + // println(''' + // ========= Indent Block («indentationLevel») ========= + // start=[«start.offset», «start.length»] «start.grammarElement» + // end=[«end.offset», «end.length»] «end.grammarElement» + // ''') + interior(start, end)[indent] + } + + def boolean hasLeadingBlankSpace(EObject model) { + immediatelyPreceding(model).ruleCallTo(BLANK_SPACERule) !== null + } + + def boolean hasTrailingBlankSpace(EObject model) { + immediatelyFollowing(model).ruleCallTo(BLANK_SPACERule) !== null + } + + def trimBlankSpace(ISemanticRegion region, int newLines, extension IFormattableDocument doc) { + if (region instanceof NodeSemanticRegion) { + addReplacer(new BlankSpaceReplacer(region, newLines)) + } + } + + def dispatch boolean isLast(MetaElement model) { + val meta = model.eContainer as Meta + model == meta.tags.last + } +} + +@FinalFieldsConstructor +public class BlankSpaceReplacer implements ITextReplacer { + val NodeSemanticRegion region + + val int newLines + + override ITextSegment getRegion() { + region + } + + override createReplacements(ITextReplacerContext context) { + val newText = (newLines === 0) ? "" : StringUtils.repeat(System.lineSeparator, newLines) + + context.addReplacement(region.replaceWith(newText)) + + return context + } +} + +@FinalFieldsConstructor +class MultilineTextReplacer implements ITextReplacer { + + static def indentToRemove(List lines, int originalStartColumn) { + var count = lines.length - 1 + if (count < 1) { + return 0 + } + + val (TextLine)=>Integer countLeadingWS = [leadingWhiteSpace.length()] + val minCountLeadingWS = lines.tail.take(count).map[countLeadingWS.apply(it)].min + + return Math.min(minCountLeadingWS, originalStartColumn) + } + + val NodeSemanticRegion region + + val int indentationLevel + + override ITextSegment getRegion() { + region + } + + override createReplacements(ITextReplacerContext context) { + val indentationString = context.getIndentationString(1) + val originalStartColumn = NodeModelUtils.getLineAndColumn(region.node, region.offset).column + + val model = TextModel.build(region.text) + if (model.lines.size > 1) { + val indentToRemove = indentToRemove(model.lines, originalStartColumn) + // println('''======= Processng Text Indentation (indentationLevel: «indentationLevel», originalStartColumn: «originalStartColumn», indentToRemove: «indentToRemove») =======''') + context.addReplacement( + region.replaceWith( + toIndentedString(model.lines, indentationString, indentationLevel, originalStartColumn, + indentToRemove))) + } + + return context + } + + def String toIndentedString(List lines, String indentationString, int indentationLevel, + int originalStartColumn, int indentToRemove) { + val result = new StringBuilder() + val length = lines.size() + + for (var i = 0; i < length; i++) { + val line = lines.get(i) + // println('''[offset: «line.relativeOffset», length: «line.length», leadingWhiteSpace: «line.leadingWhiteSpace.length»] «line»''') + val toColumn = indentationLevel + 1 + if (i == 0) { + // append first line as is (will be indented by formatter rules) + result.append(line) + } else { + if (originalStartColumn < toColumn) { + // increase indent + val indentIncrease = StringUtils.repeat(indentationString, toColumn - originalStartColumn) + result.append(indentIncrease).append(line) + } else if (originalStartColumn > toColumn) { + // decrease indent + val leadingWs = line.leadingWhiteSpace.toString() + val newIndent = StringUtils.replace(leadingWs, indentationString, "", + originalStartColumn - toColumn) + result.append(newIndent).append(line.semanticText) + } else { + // no adjustment needed + result.append(line) + } + } + + if (i < length - 1) { + result.append(System.lineSeparator()); + } + } + + return result.toString(); + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/formatting2/NaturalFormatter.xtend b/org.agileware.natural.lang/src/org/agileware/natural/lang/formatting2/NaturalFormatter.xtend new file mode 100644 index 00000000..de4f433b --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/formatting2/NaturalFormatter.xtend @@ -0,0 +1,198 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.formatting2 + +import com.google.inject.Inject +import org.agileware.natural.lang.model.Block +import org.agileware.natural.lang.model.DocString +import org.agileware.natural.lang.model.Document +import org.agileware.natural.lang.model.Meta +import org.agileware.natural.lang.model.MetaElement +import org.agileware.natural.lang.model.Narrative +import org.agileware.natural.lang.model.NaturalModel +import org.agileware.natural.lang.model.Paragraph +import org.agileware.natural.lang.model.Section +import org.agileware.natural.lang.model.Table +import org.agileware.natural.lang.services.NaturalGrammarAccess +import org.eclipse.xtext.formatting2.AbstractFormatter2 +import org.eclipse.xtext.formatting2.FormatterRequest +import org.eclipse.xtext.formatting2.IFormattableDocument +import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion + +class NaturalFormatter extends AbstractFormatter2 { + + @Inject extension NaturalGrammarAccess naturalGrammarAccess + + @Inject NaturalFormatHelper.Factory formatHelperFactory + + var extension NaturalFormatHelper _formatHelper = null + + override protected initialize(FormatterRequest request) { + _formatHelper = formatHelperFactory.create(request.textRegionAccess, naturalGrammarAccess) + _formatHelper.initialize(request) + + super.initialize(request) + } + + def dispatch void format(NaturalModel model, extension IFormattableDocument doc) { + // println(textRegionAccess) + model.document.format() + // println(doc) + } + + def dispatch void format(Document model, extension IFormattableDocument doc) { + resetIndentation() + + // Condense all BLANK_SPACE regions into single line break + model.allRegionsFor.ruleCallsTo(BLANK_SPACERule).forEach [ region | + // println('''Trimming BLANK_SPACE: «region.offset» «region.length»''') + trimBlankSpace(region, 1, doc) + ] + + // Format meta tags + model.meta.format() + + // Cleanup whitespace around keyword/title + if (model.title === null) { + model.regionFor.keyword(documentAccess.documentKeyword_3).append[noSpace] + } else { + model.regionFor.assignment(documentAccess.titleAssignment_4).prepend[oneSpace].append[noSpace] + } + + increaseIndent() + indentBlock(model.startIndent, model.endIndent, doc) + + // Format narrative + if (model.narrative !== null) { + model.narrative.format().prepend[indent] + if (!model.narrative.hasLeadingBlankSpace) { + model.narrative.prepend[setNewLines(2)] + } + } + + // Format sections + model.sections.forEach[format().prepend[indent]] + + decreaseIndent() + } + + def dispatch void format(Section model, extension IFormattableDocument doc) { + + // Set block spacing + if (!model.hasLeadingBlankSpace) { + model.prepend[setNewLines(2)] + } + + // Format meta tags + if (model.meta !== null) { + model.meta.format() + + // Work-around for strange keyword placement when tags are present + model.regionFor.keyword(sectionAccess.sectionKeyword_2).prepend[indent] + } + + // Cleanup whitespace around keyword/title + if (model.title === null) { + model.regionFor.keyword(sectionAccess.sectionKeyword_2).append[noSpace] + } else { + model.regionFor.assignment(sectionAccess.titleAssignment_3).prepend[oneSpace].append[noSpace] + } + + increaseIndent() + indentBlock(model.startIndent, model.endIndent, doc) + + // Format narrative + if (model.narrative !== null) { + model.narrative.format().prepend[indent] + // TODO (opinionated) should we increase spacing here? + // if(!model.narrative.hasLeadingBlankSpace) { + // model.narrative.prepend[setNewLines(2)] + // } + } + + decreaseIndent() + } + + def dispatch void format(Meta model, extension IFormattableDocument doc) { + model.tags.forEach[format] + } + + def dispatch void format(MetaElement model, extension IFormattableDocument doc) { + + // Trim leading/trailing whitespace + model.surround[noSpace] + +// if (model.value !== null) { +// // Cleanup whitespace around value assignment +// model.regionFor.keyword(':').prepend[noSpace].append[oneSpace] +// model.regionFor.assignment(metaElementAccess.valueAssignment_2_1).prepend[oneSpace].append[noSpace] +// } + + // Insert newline if not present from BLANK_SPACE + if (model.isLast()) { + model.append[setNewLines(0)] + } else if (!model.hasTrailingBlankSpace) { + model.append[newLine] + } + } + + def dispatch void format(Narrative model, extension IFormattableDocument doc) { + model.sections.forEach[format().prepend[indent]] + } + + def dispatch void format(Paragraph model, extension IFormattableDocument doc) { + formatMultilineText(model, paragraphAccess.valueAssignment_1, indentationLevel, doc) + } + + def dispatch void format(Table model, extension IFormattableDocument doc) { + model.rows.forEach[prepend[indent]] + } + + def dispatch void format(DocString model, extension IFormattableDocument doc) { + formatMultilineText(model, docStringAccess.valueAssignment_1, indentationLevel, doc) + } + + def ISemanticRegion startIndent(Document model) { + return model.regionFor.ruleCallTo(NLRule) + } + + def ISemanticRegion endIndent(Document model) { + if (!model.sections.isEmpty()) { + return model.sections.last.endIndent() + } else if (model.narrative !== null) { + return model.narrative.endIndent() + } + + return model.regionFor.ruleCall(documentAccess.BLANK_SPACEParserRuleCall_8) + } + + def ISemanticRegion startIndent(Section model) { + return model.regionFor.ruleCallTo(NLRule) + } + + def ISemanticRegion endIndent(Section model) { + if (model.narrative !== null) { + return model.narrative.endIndent() + } + + return model.regionFor.ruleCallTo(NLRule) + } + + def ISemanticRegion endIndent(Narrative model) { + return model.sections.last.endIndent() + } + + def ISemanticRegion endIndent(Block model) { + if(model instanceof Table) { + return model.rows.last.regionFor.ruleCallTo(NLRule) + } + + return model.regionFor.ruleCallTo(NLRule) + } + + def boolean isLast(Section model) { + val document = model.eContainer as Document + model == document.sections.last + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/generator/NaturalGenerator.xtend b/org.agileware.natural.lang/src/org/agileware/natural/lang/generator/NaturalGenerator.xtend new file mode 100644 index 00000000..a0d3a30f --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/generator/NaturalGenerator.xtend @@ -0,0 +1,25 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.generator + +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.generator.AbstractGenerator +import org.eclipse.xtext.generator.IFileSystemAccess2 +import org.eclipse.xtext.generator.IGeneratorContext + +/** + * Generates code from your model files on save. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation + */ +class NaturalGenerator extends AbstractGenerator { + + override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { +// fsa.generateFile('greetings.txt', 'People to greet: ' + +// resource.allContents +// .filter(Greeting) +// .map[name] +// .join(', ')) + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/scoping/NaturalScopeProvider.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/scoping/NaturalScopeProvider.java new file mode 100644 index 00000000..cb9c3e84 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/scoping/NaturalScopeProvider.java @@ -0,0 +1,15 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.scoping; + + +/** + * This class contains custom scoping description. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping + * on how and when to use it. + */ +public class NaturalScopeProvider extends AbstractNaturalScopeProvider { + +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSemanticSequencer.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSemanticSequencer.java new file mode 100644 index 00000000..fc117309 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSemanticSequencer.java @@ -0,0 +1,8 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.serializer; + + +public class NaturalSemanticSequencer extends AbstractNaturalSemanticSequencer { +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSerializer.xtend b/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSerializer.xtend new file mode 100644 index 00000000..ce26c4fa --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSerializer.xtend @@ -0,0 +1,98 @@ +package org.agileware.natural.lang.serializer + +import org.agileware.natural.lang.model.Block +import org.agileware.natural.lang.model.DocString +import org.agileware.natural.lang.model.Document +import org.agileware.natural.lang.model.Meta +import org.agileware.natural.lang.model.Narrative +import org.agileware.natural.lang.model.NaturalModel +import org.agileware.natural.lang.model.Paragraph +import org.agileware.natural.lang.model.Section +import org.agileware.natural.lang.model.Table +import org.agileware.natural.lang.model.TableCol +import org.agileware.natural.lang.model.TableRow + +class NaturalSerializer { + + def String serialize(NaturalModel model) { + return (model.document === null) ? "\n" : serialize(model.document) + } + + def String serialize(Document model) ''' + # language: en + «serialize(model.meta)» + Document: «model.title» + «serialize(model.narrative)» + «FOR s : model.sections» + «serialize(s)» + «ENDFOR» + ''' + + def String serialize(Section model) ''' + Section: «model.title» + «serialize(model.narrative)» + ''' + + def String serialize(Meta model) { + if(model === null) return "" + + return ''' + «FOR t : model.tags» + «t.id» + «ENDFOR» + ''' + } + + def String serialize(Narrative model) { + if(model === null) return "" + return ''' + «FOR s : model.sections» + «serialize(s)» + «ENDFOR» + ''' + } + + def String serialize(Block model) { + if(model instanceof Paragraph) { + return serialize(model as Paragraph) + } else if(model instanceof Table) { + return serialize(model as Table) + } else if(model instanceof DocString) { + return serialize(model as DocString) + } + + return "" + } + + def String serialize(Paragraph model) { + if(model === null) return "" + + return model.value + } + + def String serialize(Table model) { + if(model === null) return "" + + return ''' + «FOR r : model.rows» + «serialize(r)» + «ENDFOR» + ''' + } + + def String serialize(TableRow model) ''' + «model.cols.map[serialize].join()» | + ''' + + def String serialize(TableCol model) { + return model.value + } + + def String serialize(DocString model) { + if(model === null) return "" + + return ''' + «model.value» + ''' + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSyntacticSequencer.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSyntacticSequencer.java new file mode 100644 index 00000000..b892f1d6 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/serializer/NaturalSyntacticSequencer.java @@ -0,0 +1,8 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.serializer; + + +public class NaturalSyntacticSequencer extends AbstractNaturalSyntacticSequencer { +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/text/NUMBERValueConverter.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/NUMBERValueConverter.java new file mode 100644 index 00000000..de58bf43 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/NUMBERValueConverter.java @@ -0,0 +1,32 @@ +package org.agileware.natural.lang.text; + +import java.math.BigInteger; + +import org.eclipse.xtext.conversion.ValueConverterException; +import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter; +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.util.Strings; + +public class NUMBERValueConverter extends AbstractLexerBasedConverter { + + public NUMBERValueConverter() { + super(); + } + + @Override + protected String toEscapedString(final BigInteger value) { + return value.toString(); + } + + @Override + public BigInteger toValue(final String string, final INode node) throws ValueConverterException { + if (Strings.isEmpty(string)) + throw new ValueConverterException("Couldn't convert empty string to an int value.", node, null); + try { + return new BigInteger(string); + } catch (final NumberFormatException e) { + throw new ValueConverterException("Couldn't convert '" + string + "' to an int value.", node, e); + } + } + +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextLine.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextLine.java new file mode 100644 index 00000000..94a89281 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextLine.java @@ -0,0 +1,158 @@ +package org.agileware.natural.lang.text; + +/** + * Implementation {@link CharSequence} that is tailored to line-per-line + * processing of text and supports special handling of whitespace. + * + * @author Sebastian Zarnekow - Initial contribution and API + */ +public class TextLine implements CharSequence { + + protected static class LeadingWSTextLinePart extends TextLine { + + public LeadingWSTextLinePart(final String completeText, final int offset, final int length) { + super(completeText, offset, length, 0); + } + + @Override + public CharSequence getLeadingWhiteSpace() { + return this; + } + + @Override + public boolean hasLeadingWhiteSpace() { + return length() > 0; + } + + @Override + public boolean containsOnlyWhitespace() { + return true; + } + } + + private final String completeText; + private final int offset; + private final int length; + private final int delimiterLength; + + public TextLine(final String completeText, final int offset, final int length, final int delimiterLength) { + this.completeText = completeText; + this.offset = offset; + this.length = length; + this.delimiterLength = delimiterLength; + } + + public String getCompleteText() { + return completeText; + } + + public boolean hasLeadingWhiteSpace() { + if (length == 0) + return false; + final boolean result = Character.isWhitespace(charAt(0)); + return result; + } + + public boolean containsOnlyWhitespace() { + for (int i = 0; i < length(); i++) { + if (!Character.isWhitespace(charAt(i))) { + return false; + } + } + return true; + } + + public CharSequence getLeadingWhiteSpace() { + for (int i = 0; i < length(); i++) { + if (!Character.isWhitespace(charAt(i))) { + if (i == 0) + return ""; + return new LeadingWSTextLinePart(completeText, offset, i); + } + } + return new LeadingWSTextLinePart(completeText, offset, length); + } + + public boolean hasTrailingLineBreak() { + return delimiterLength > 0; + } + + public int getRelativeOffset() { + return offset; + } + + @Override + public int length() { + return length; + } + + @Override + public char charAt(final int index) { + return completeText.charAt(index + offset); + } + + public int getDelimiterLength() { + return delimiterLength; + } + + public CharSequence getSemanticText() { + return subSequence(getLeadingWhiteSpace().length(), length); + } + + @Override + public String toString() { + return completeText.substring(offset, offset + length); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + toString().hashCode(); + result = prime * result + delimiterLength; + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final TextLine other = (TextLine) obj; + if (length != other.length) + return false; + if (delimiterLength != other.delimiterLength) + return false; + if (!completeText.regionMatches(offset, other.completeText, other.offset, length)) + return false; + return true; + } + + /** + * @throws IndexOutOfBoundsException if start or end are + * negative, if end is greater than + * length(), or if start is + * greater than end + */ + @Override + public CharSequence subSequence(final int start, final int end) { + if (start < 0 || start > end) { + throwIndexOutOfBounds(start); + } + if (end < 0 || end > length) { + throwIndexOutOfBounds(end); + } + if (start > end) { + throwIndexOutOfBounds(end - start); + } + return completeText.subSequence(start + offset, end + offset); + } + + protected void throwIndexOutOfBounds(final int offset) { + throw new IndexOutOfBoundsException(("Index out of range: " + offset)); + } + +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextLiterals.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextLiterals.java new file mode 100644 index 00000000..46638f75 --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextLiterals.java @@ -0,0 +1,13 @@ +package org.agileware.natural.lang.text; + +public interface TextLiterals { + String DOC_STRING_LITERAL = "DOC_STRING_LITERAL"; + String TABLE_CELL = "TABLE_CELL"; + String STRING = "STRING"; + String NUMBER = "NUMBER"; + String WORD = "WORD"; + String NL = "NL"; + String WS = "WS"; + String SL_COMMENT = "SL_COMMENT"; + String ANY_OTHER = "ANY_OTHER"; +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextModel.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextModel.java new file mode 100644 index 00000000..fc5c01ec --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextModel.java @@ -0,0 +1,74 @@ +package org.agileware.natural.lang.text; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * + * TODO This was originally designed for individual replacers per Literal, And + * is probably a bit overkill for the simple whole-region-replacer that is + * currently implemented in MultilineTextFormatter + * + */ +public class TextModel { + + /** + * adapted from + * org.eclipse.jface.text.DefaultLineTracker.nextDelimiterInfo(String, int) + */ + public static TextModel build(final String text) { + final List lines = new ArrayList(); + if (text == null) + return new TextModel(lines); + + final int length = text.length(); + int nextLineOffset = 0; + int idx = 0; + while (idx < length) { + final char currentChar = text.charAt(idx); + // check for \r or \r\n + if (currentChar == '\r') { + int delimiterLength = 1; + if (idx + 1 < length && text.charAt(idx + 1) == '\n') { + delimiterLength++; + idx++; + } + final int lineLength = idx - delimiterLength - nextLineOffset + 1; + final TextLine line = new TextLine(text, nextLineOffset, lineLength, delimiterLength); + lines.add(line); + nextLineOffset = idx + 1; + } else if (currentChar == '\n') { + final int lineLength = idx - nextLineOffset; + final TextLine line = new TextLine(text, nextLineOffset, lineLength, 1); + lines.add(line); + nextLineOffset = idx + 1; + } + idx++; + } + if (nextLineOffset != length) { + final int lineLength = length - nextLineOffset; + final TextLine line = new TextLine(text, nextLineOffset, lineLength, 0); + lines.add(line); + } + + return new TextModel(lines); + } + + private final List lines; + + public List getLines() { + return lines; + } + + public TextModel(final List lines) { + super(); + + this.lines = lines; + } + + @Override + public String toString() { + return lines.stream().collect(Collectors.joining(System.lineSeparator())).toString(); + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextValueConverterService.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextValueConverterService.java new file mode 100644 index 00000000..3441069c --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/text/TextValueConverterService.java @@ -0,0 +1,29 @@ +package org.agileware.natural.lang.text; + +import java.math.BigInteger; + +import org.eclipse.xtext.conversion.IValueConverter; +import org.eclipse.xtext.conversion.ValueConverter; +import org.eclipse.xtext.conversion.impl.AbstractDeclarativeValueConverterService; +import org.eclipse.xtext.conversion.impl.STRINGValueConverter; + +import com.google.inject.Inject; + +public class TextValueConverterService extends AbstractDeclarativeValueConverterService { + + @Inject + private STRINGValueConverter stringValueConverter; + + @ValueConverter(rule = TextLiterals.STRING) + public IValueConverter STRING_LITERAL() { + return stringValueConverter; + } + + @Inject + private NUMBERValueConverter numberValueConverter; + + @ValueConverter(rule = TextLiterals.NUMBER) + public IValueConverter NUMBER_LITERAL() { + return numberValueConverter; + } +} diff --git a/org.agileware.natural.lang/src/org/agileware/natural/lang/validation/NaturalValidator.java b/org.agileware.natural.lang/src/org/agileware/natural/lang/validation/NaturalValidator.java new file mode 100644 index 00000000..0d00851d --- /dev/null +++ b/org.agileware.natural.lang/src/org/agileware/natural/lang/validation/NaturalValidator.java @@ -0,0 +1,25 @@ +/* + * generated by Xtext 2.23.0-SNAPSHOT + */ +package org.agileware.natural.lang.validation; + + +/** + * This class contains custom validation rules. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation + */ +public class NaturalValidator extends AbstractNaturalValidator { + +// public static final String INVALID_NAME = "invalidName"; +// +// @Check +// public void checkGreetingStartsWithCapital(Greeting greeting) { +// if (!Character.isUpperCase(greeting.getName().charAt(0))) { +// warning("Name should start with a capital", +// NaturalPackage.Literals.GREETING__NAME, +// INVALID_NAME); +// } +// } + +} diff --git a/org.agileware.natural.stepmatcher.feature/.project b/org.agileware.natural.stepmatcher.feature/.project deleted file mode 100644 index 49ca92e2..00000000 --- a/org.agileware.natural.stepmatcher.feature/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - org.agileware.natural.stepmatcher.feature - - - - - - org.eclipse.pde.FeatureBuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.pde.FeatureNature - org.eclipse.m2e.core.maven2Nature - - diff --git a/org.agileware.natural.stepmatcher.feature/build.properties b/org.agileware.natural.stepmatcher.feature/build.properties deleted file mode 100644 index 64f93a9f..00000000 --- a/org.agileware.natural.stepmatcher.feature/build.properties +++ /dev/null @@ -1 +0,0 @@ -bin.includes = feature.xml diff --git a/org.agileware.natural.stepmatcher.feature/feature.xml b/org.agileware.natural.stepmatcher.feature/feature.xml deleted file mode 100644 index 177b9d65..00000000 --- a/org.agileware.natural.stepmatcher.feature/feature.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - [Enter Feature Description here.] - - - - [Enter Copyright Description here.] - - - - [Enter License Description here.] - - - - - diff --git a/org.agileware.natural.stepmatcher.ui/.classpath b/org.agileware.natural.stepmatcher.ui/.classpath new file mode 100644 index 00000000..eca7bdba --- /dev/null +++ b/org.agileware.natural.stepmatcher.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.agileware.natural.stepmatcher.ui/.project b/org.agileware.natural.stepmatcher.ui/.project new file mode 100644 index 00000000..32974d7a --- /dev/null +++ b/org.agileware.natural.stepmatcher.ui/.project @@ -0,0 +1,28 @@ + + + org.agileware.natural.stepmatcher.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.agileware.natural.stepmatcher.ui/.settings/org.eclipse.jdt.core.prefs b/org.agileware.natural.stepmatcher.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..9f6ece88 --- /dev/null +++ b/org.agileware.natural.stepmatcher.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.agileware.natural.stepmatcher.ui/META-INF/MANIFEST.MF b/org.agileware.natural.stepmatcher.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000..b91167e4 --- /dev/null +++ b/org.agileware.natural.stepmatcher.ui/META-INF/MANIFEST.MF @@ -0,0 +1,28 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Eclipse JDT StepMatcher +Bundle-SymbolicName: org.agileware.natural.stepmatcher.ui +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.agileware.natural.stepmatcher.ui.Activator +Bundle-Vendor: Roberto Lo Giacco +Require-Bundle: org.agileware.natural.stepmatcher, + org.eclipse.ui, + org.eclipse.ui.ide, + org.eclipse.core.runtime, + org.eclipse.core.resources, + org.eclipse.jdt.core, + org.eclipse.jdt.ui, + org.eclipse.jface.text, + com.google.guava;bundle-version="27.1.0", + com.google.inject;bundle-version="3.0.0" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Automatic-Module-Name: org.agileware.natural.stepmatcher.ui +Bundle-ActivationPolicy: lazy +Export-Package: org.agileware.natural.stepmatcher.ui; + uses:="org.osgi.framework, + org.eclipse.jdt.core, + org.eclipse.jface.text, + org.agileware.natural.stepmatcher, + org.eclipse.jface.text.hyperlink, + org.eclipse.jdt.core.search, + org.eclipse.ui.plugin" diff --git a/org.agileware.natural.common/build.properties b/org.agileware.natural.stepmatcher.ui/build.properties similarity index 56% rename from org.agileware.natural.common/build.properties rename to org.agileware.natural.stepmatcher.ui/build.properties index aa70d440..34d2e4d2 100644 --- a/org.agileware.natural.common/build.properties +++ b/org.agileware.natural.stepmatcher.ui/build.properties @@ -1,2 +1,4 @@ source.. = src/ +output.. = bin/ bin.includes = META-INF/,\ + . diff --git a/org.agileware.natural.common/pom.xml b/org.agileware.natural.stepmatcher.ui/pom.xml similarity index 86% rename from org.agileware.natural.common/pom.xml rename to org.agileware.natural.stepmatcher.ui/pom.xml index 755b6ba0..1b335e49 100644 --- a/org.agileware.natural.common/pom.xml +++ b/org.agileware.natural.stepmatcher.ui/pom.xml @@ -6,9 +6,8 @@ natural 1.0.0-SNAPSHOT - org.agileware.natural.common + org.agileware.natural.stepmatcher.ui eclipse-plugin - 1.3.0-SNAPSHOT diff --git a/org.agileware.natural.common/src/org/agileware/natural/common/AbstractAnnotationDescriptor.java b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/AbstractAnnotationDescriptor.java similarity index 92% rename from org.agileware.natural.common/src/org/agileware/natural/common/AbstractAnnotationDescriptor.java rename to org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/AbstractAnnotationDescriptor.java index b24616e1..7e9b7b95 100644 --- a/org.agileware.natural.common/src/org/agileware/natural/common/AbstractAnnotationDescriptor.java +++ b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/AbstractAnnotationDescriptor.java @@ -1,4 +1,4 @@ -package org.agileware.natural.common; +package org.agileware.natural.stepmatcher.ui; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; diff --git a/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/Activator.java b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/Activator.java new file mode 100644 index 00000000..cbfcb4eb --- /dev/null +++ b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/Activator.java @@ -0,0 +1,44 @@ +package org.agileware.natural.stepmatcher.ui; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.agileware.natural.stepmatcher.ui"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaAnnotationMatcher.java b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaAnnotationMatcher.java new file mode 100644 index 00000000..e4ea5e39 --- /dev/null +++ b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaAnnotationMatcher.java @@ -0,0 +1,218 @@ +package org.agileware.natural.stepmatcher.ui; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.agileware.natural.stepmatcher.AnnotationMacthEntry; +import org.agileware.natural.stepmatcher.IStepMatcher; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.search.SearchRequestor; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +@Singleton +public class JavaAnnotationMatcher implements IStepMatcher { + + private static boolean isMatchingAnnotationValue(final String annotationValue, final String description) { + try { + return description.matches(Pattern.quote(annotationValue)); + } catch (final PatternSyntaxException e) { + e.printStackTrace(); + } + + return false; + } + + @Inject + private AbstractAnnotationDescriptor descriptor; + + private final Map> cache = new HashMap>(); + + @Override + public boolean isActivated() { + return true; + } + + @Override + public Collection findMatches(final String keyword, final String description) { + final MatchCollector command = new MatchCollector(keyword, description); + findMatches(description, command); + + return command.get(); + } + + protected IJavaSearchScope getScope(final String filter) { + if (filter == null) + return SearchEngine.createWorkspaceScope(); + + final String[] names = filter.split(","); + final List packages = new ArrayList(); + + SearchPattern pattern = null; + for (final String name : names) { + final SearchPattern current = SearchPattern.createPattern(name.trim(), IJavaSearchConstants.PACKAGE, + IJavaSearchConstants.ALL_OCCURRENCES, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); + if (pattern == null) { + pattern = current; + } else { + pattern = SearchPattern.createOrPattern(pattern, current); + } + } + try { + new SearchEngine().search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, + SearchEngine.createWorkspaceScope(), new SearchRequestor() { + @Override + public void acceptSearchMatch(final SearchMatch match) throws CoreException { + packages.add((IJavaElement) match.getElement()); + } + }, null); + } catch (final CoreException e) { + e.printStackTrace(); + } + return SearchEngine.createJavaSearchScope(packages.toArray(new IJavaElement[0])); + } + + public void findMatches(final String description, final Command command) { + final long time = System.currentTimeMillis(); + if (!cache.isEmpty()) { + for (final List entries : cache.values()) { + for (final Entry entry : entries) { + if (isMatchingAnnotationValue(entry.annotationValue, description)) { + command.match(entry.annotationValue, entry.method); + } + } + } + return; + } + + // combine search patterns + SearchPattern pattern = null; + for (final String annotationName : descriptor.getNames()) { + final SearchPattern current = SearchPattern.createPattern(annotationName, + IJavaSearchConstants.ANNOTATION_TYPE, IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE, + SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); + if (pattern == null) { + pattern = current; + } else { + pattern = SearchPattern.createOrPattern(pattern, current); + } + } + // execute search + try { + new SearchEngine().search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, + this.getScope(null), new SearchRequestor() { + @Override + public void acceptSearchMatch(final SearchMatch match) throws CoreException { + if (match.getElement() instanceof IMethod) { + final IMethod method = (IMethod) match.getElement(); + final IAnnotation[] annotations = method.getAnnotations(); + for (final IAnnotation type : annotations) { + // check annotation package + if (AbstractAnnotationDescriptor.checkPackage(type, descriptor.getPackage())) { + // verify pattern + final String annotationValue = (String) type.getMemberValuePairs()[0] + .getValue(); + List entries = cache.get(method.getCompilationUnit()); + if (entries == null) { + entries = new ArrayList(); + cache.put(method.getCompilationUnit(), entries); + } + entries.add(new Entry(annotationValue, method)); + if (isMatchingAnnotationValue(annotationValue, description)) { + command.match(annotationValue, method); + } + } + } + } + } + }, null); + } catch (final CoreException e) { + e.printStackTrace(); + } + System.out.println("stepdef match lookup completed in " + (System.currentTimeMillis() - time) + "ms"); + } + + public Collection findProposals() { + if (cache.isEmpty()) { + findMatches("", (annotationValue, method) -> { + }); + } + + final Collection proposals = new TreeSet(); + for (final List entries : cache.values()) { + for (final Entry entry : entries) { + proposals.add(entry.getAnnotationValue()); + } + } + return proposals; + } + + public void evict(final ICompilationUnit element) { + cache.clear(); + System.out.println(">>> cache cleared"); + } + + public static interface Command { + void match(String annotationValue, IMethod method); + } + + private final static class MatchCollector implements JavaAnnotationMatcher.Command { + private final String keyword; + private final String description; + + private final List entries = new ArrayList(); + + public MatchCollector(final String keyword, final String description) { + super(); + this.keyword = keyword; + this.description = description; + } + + public List get() { + return Collections.unmodifiableList(entries); + } + + @Override + public void match(final String annotationValue, final IMethod method) { + try { + entries.add(new AnnotationMacthEntry(keyword, description, method.getClass().getCanonicalName(), + method.getSignature())); + } catch (final JavaModelException e) { + e.printStackTrace(); + } + } + } + + public static class Entry { + private final String annotationValue; + private final IMethod method; + + private Entry(final String annotationValue, final IMethod method) { + this.annotationValue = annotationValue; + this.method = method; + } + + public String getAnnotationValue() { + return annotationValue; + } + } +} diff --git a/org.agileware.natural.common/src/org/agileware/natural/common/JavaElementChangeReporter.java b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaElementChangeReporter.java similarity index 67% rename from org.agileware.natural.common/src/org/agileware/natural/common/JavaElementChangeReporter.java rename to org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaElementChangeReporter.java index 8a23deba..fa92bda9 100644 --- a/org.agileware.natural.common/src/org/agileware/natural/common/JavaElementChangeReporter.java +++ b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaElementChangeReporter.java @@ -1,4 +1,4 @@ -package org.agileware.natural.common; +package org.agileware.natural.stepmatcher.ui; import java.util.List; @@ -11,22 +11,22 @@ import com.google.inject.Inject; public class JavaElementChangeReporter implements IElementChangedListener { - + @Inject private JavaAnnotationMatcher matcher; - + @Override - public void elementChanged(ElementChangedEvent event) { + public void elementChanged(final ElementChangedEvent event) { traverse(event.getDelta(), null); } - void traverse(IJavaElementDelta delta, List hyperlinks) { + void traverse(final IJavaElementDelta delta, final List hyperlinks) { if (delta.getElement().getElementType() == IJavaElement.COMPILATION_UNIT) { if ((delta.getFlags() & IJavaElementDelta.F_PRIMARY_RESOURCE) > 0) { - matcher.evict((ICompilationUnit)delta.getElement()); + matcher.evict((ICompilationUnit) delta.getElement()); } } - for (IJavaElementDelta child : delta.getAffectedChildren()) { + for (final IJavaElementDelta child : delta.getAffectedChildren()) { this.traverse(child, hyperlinks); } } diff --git a/org.agileware.natural.common/src/org/agileware/natural/common/JavaHyperlink.java b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaHyperlink.java similarity index 96% rename from org.agileware.natural.common/src/org/agileware/natural/common/JavaHyperlink.java rename to org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaHyperlink.java index b813655a..1830c3fb 100644 --- a/org.agileware.natural.common/src/org/agileware/natural/common/JavaHyperlink.java +++ b/org.agileware.natural.stepmatcher.ui/src/org/agileware/natural/stepmatcher/ui/JavaHyperlink.java @@ -1,4 +1,4 @@ -package org.agileware.natural.common; +package org.agileware.natural.stepmatcher.ui; import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.IJavaElement; diff --git a/org.agileware.natural.stepmatcher/META-INF/MANIFEST.MF b/org.agileware.natural.stepmatcher/META-INF/MANIFEST.MF index 162be3e4..f567ec8d 100644 --- a/org.agileware.natural.stepmatcher/META-INF/MANIFEST.MF +++ b/org.agileware.natural.stepmatcher/META-INF/MANIFEST.MF @@ -1,10 +1,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 -Bundle-Name: Stepmatcher +Bundle-Name: Cucumber JVM Annotation Matcher Bundle-SymbolicName: org.agileware.natural.stepmatcher Bundle-Version: 1.0.0.qualifier -Bundle-Activator: org.agileware.natural.stepmatcher.Activator -Require-Bundle: org.eclipse.core.runtime -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-Vendor: Roberto Lo Giacco Automatic-Module-Name: org.agileware.natural.stepmatcher -Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Import-Package: org.osgi.framework;resolution:=optional, + org.osgi.service.component.annotations;resolution:=optional +Export-Package: org.agileware.natural.stepmatcher diff --git a/org.agileware.natural.stepmatcher/build.properties b/org.agileware.natural.stepmatcher/build.properties index 56d77655..34d2e4d2 100644 --- a/org.agileware.natural.stepmatcher/build.properties +++ b/org.agileware.natural.stepmatcher/build.properties @@ -1,4 +1,4 @@ source.. = src/ -output.. = target/classes/ +output.. = bin/ bin.includes = META-INF/,\ . diff --git a/org.agileware.natural.stepmatcher/pom.xml b/org.agileware.natural.stepmatcher/pom.xml index e215b770..ca59f73a 100644 --- a/org.agileware.natural.stepmatcher/pom.xml +++ b/org.agileware.natural.stepmatcher/pom.xml @@ -11,12 +11,10 @@ - org.eclipse.xtend xtend-maven-plugin - diff --git a/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/Activator.java b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/Activator.java deleted file mode 100644 index 135c81ab..00000000 --- a/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/Activator.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.agileware.natural.stepmatcher; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -public class Activator implements BundleActivator { - - private static BundleContext context; - - static BundleContext getContext() { - return context; - } - - public void start(BundleContext bundleContext) throws Exception { - Activator.context = bundleContext; - } - - public void stop(BundleContext bundleContext) throws Exception { - Activator.context = null; - } - -} diff --git a/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/AnnotationMacthEntry.java b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/AnnotationMacthEntry.java new file mode 100644 index 00000000..afc59ae5 --- /dev/null +++ b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/AnnotationMacthEntry.java @@ -0,0 +1,34 @@ +package org.agileware.natural.stepmatcher; + +public class AnnotationMacthEntry { + + private final String keyword; + private final String description; + private final String typeName; + private final String methodSignature; + + public String getKeyword() { + return keyword; + } + + public String getDescription() { + return description; + } + + public String getTypeName() { + return typeName; + } + + public String getMethodSignature() { + return methodSignature; + } + + public AnnotationMacthEntry(final String keyword, final String description, final String typeName, + final String methodSignature) { + super(); + this.keyword = keyword; + this.description = description; + this.typeName = typeName; + this.methodSignature = methodSignature; + } +} diff --git a/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/DefaultStepMatcher.java b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/DefaultStepMatcher.java new file mode 100644 index 00000000..37c6ad29 --- /dev/null +++ b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/DefaultStepMatcher.java @@ -0,0 +1,17 @@ +package org.agileware.natural.stepmatcher; + +import java.util.Collection; + +public class DefaultStepMatcher implements IStepMatcher { + + @Override + public boolean isActivated() { + return false; + } + + @Override + public Collection findMatches(final String keyword, final String description) { + return null; + } + +} diff --git a/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/IStepMatcher.java b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/IStepMatcher.java new file mode 100644 index 00000000..b99e012d --- /dev/null +++ b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/IStepMatcher.java @@ -0,0 +1,20 @@ +package org.agileware.natural.stepmatcher; + +import java.util.Collection; + +public interface IStepMatcher { + + /** + * @return Returns true if step matcher is activated at runtime + */ + public boolean isActivated(); + + /** + * Search for matching annotations given a Step keyword and description + * + * @param keyword + * @param description + * @return + */ + public Collection findMatches(final String keyword, final String description); +} diff --git a/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/package-info.java b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/package-info.java new file mode 100644 index 00000000..103e6d8c --- /dev/null +++ b/org.agileware.natural.stepmatcher/src/org/agileware/natural/stepmatcher/package-info.java @@ -0,0 +1 @@ +package org.agileware.natural.stepmatcher; \ No newline at end of file diff --git a/org.agileware.natural.target/org.agileware.natural.target.target b/org.agileware.natural.target/org.agileware.natural.target.target index 95ea50f8..f7183ad8 100644 --- a/org.agileware.natural.target/org.agileware.natural.target.target +++ b/org.agileware.natural.target/org.agileware.natural.target.target @@ -8,31 +8,26 @@ - + - + - + - - - - - - - - - + + - + + + - + \ No newline at end of file diff --git a/org.agileware.natural.testing/META-INF/MANIFEST.MF b/org.agileware.natural.testing/META-INF/MANIFEST.MF index 36f095a0..314d4c94 100644 --- a/org.agileware.natural.testing/META-INF/MANIFEST.MF +++ b/org.agileware.natural.testing/META-INF/MANIFEST.MF @@ -10,21 +10,7 @@ Export-Package: org.agileware.natural.testing; com.google.inject, org.eclipse.xtext.testing.formatter, org.eclipse.xtext.testing.validation, - org.hamcrest", - org.hamcrest; - uses:="org.hamcrest.internal, - new org.hamcrest, - javax.xml.namespace, - org.hamcrest.core, - org.hamcrest.collection", - org.hamcrest.beans;uses:="org.hamcrest", - org.hamcrest.collection;uses:="org.hamcrest", - org.hamcrest.comparator;uses:="org.hamcrest", - org.hamcrest.core;uses:="org.hamcrest", - org.hamcrest.internal;uses:="org.hamcrest", - org.hamcrest.io;uses:="org.hamcrest", - org.hamcrest.number;uses:="org.hamcrest", - org.hamcrest.object;uses:="org.hamcrest", - org.hamcrest.text;uses:="org.hamcrest", - org.hamcrest.xml;uses:="org.w3c.dom,javax.xml.namespace,org.hamcrest" -Require-Bundle: org.eclipse.xtext.testing + org.hamcrest" +Require-Bundle: org.eclipse.xtext.testing, + org.hamcrest.core;visibility:=reexport, + org.hamcrest.library;visibility:=reexport diff --git a/org.agileware.natural.testing/antlrworks.launch b/org.agileware.natural.testing/antlrworks.launch new file mode 100644 index 00000000..361fbfc9 --- /dev/null +++ b/org.agileware.natural.testing/antlrworks.launch @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.agileware.natural.testing/build.properties b/org.agileware.natural.testing/build.properties index a55b80b0..718c03ee 100644 --- a/org.agileware.natural.testing/build.properties +++ b/org.agileware.natural.testing/build.properties @@ -1,13 +1,7 @@ -source.. = src/ +source.. = src/,\ + xtend-gen/ output.. = bin/ bin.includes = META-INF/,\ - .,\ - lib/hamcrest-2.2-javadoc.jar -src.includes = src/,\ - pom.xml,\ - build.properties,\ - META-INF/,\ + . +src.includes = pom.xml,\ lib/ -jars.extra.classpath = lib/hamcrest-2.2-javadoc.jar,\ - lib/hamcrest-2.2-sources.jar,\ - lib/hamcrest-2.2.jar diff --git a/org.agileware.natural.testing/lib/antlrworks-1.5.2-complete.jar b/org.agileware.natural.testing/lib/antlrworks-1.5.2-complete.jar new file mode 100755 index 00000000..9b71ab0c Binary files /dev/null and b/org.agileware.natural.testing/lib/antlrworks-1.5.2-complete.jar differ diff --git a/org.agileware.natural.testing/lib/hamcrest-2.2-javadoc.jar b/org.agileware.natural.testing/lib/hamcrest-2.2-javadoc.jar deleted file mode 100644 index f57125c0..00000000 Binary files a/org.agileware.natural.testing/lib/hamcrest-2.2-javadoc.jar and /dev/null differ diff --git a/org.agileware.natural.testing/lib/hamcrest-2.2-sources.jar b/org.agileware.natural.testing/lib/hamcrest-2.2-sources.jar deleted file mode 100644 index 61242118..00000000 Binary files a/org.agileware.natural.testing/lib/hamcrest-2.2-sources.jar and /dev/null differ diff --git a/org.agileware.natural.testing/lib/hamcrest-2.2.jar b/org.agileware.natural.testing/lib/hamcrest-2.2.jar deleted file mode 100644 index 71065788..00000000 Binary files a/org.agileware.natural.testing/lib/hamcrest-2.2.jar and /dev/null differ diff --git a/org.agileware.natural.testing/pom.xml b/org.agileware.natural.testing/pom.xml index 23efcd03..b7e7f6ba 100644 --- a/org.agileware.natural.testing/pom.xml +++ b/org.agileware.natural.testing/pom.xml @@ -19,11 +19,4 @@ - - - org.hamcrest - hamcrest - 2.2 - - diff --git a/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractExamplesTest.java b/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractExamplesTest.java index e91973f9..4993a695 100644 --- a/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractExamplesTest.java +++ b/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractExamplesTest.java @@ -5,14 +5,9 @@ import static org.hamcrest.Matchers.notNullValue; import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.testing.util.ParseHelper; -import com.google.inject.Inject; +public class AbstractExamplesTest extends AbstractParserTest { -public class AbstractExamplesTest { - - @Inject ParseHelper parseHelper; - public void assertExampleParses(String content) throws Exception { T model = parseHelper.parse(content); assertThat(model, notNullValue()); diff --git a/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractFormatterTest.java b/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractFormatterTest.java index f586c84f..74d3c3c5 100644 --- a/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractFormatterTest.java +++ b/org.agileware.natural.testing/src/org/agileware/natural/testing/AbstractFormatterTest.java @@ -10,21 +10,21 @@ public class AbstractFormatterTest { @Inject - FormatterTestHelper formatterTestHelper; + protected FormatterTestHelper formatterTestHelper; @Inject - Provider formatterRequestProvider; + protected Provider formatterRequestProvider; - public void assertFormatted(String toBeFormatted, String expectation) { + public void assertFormatted(final String toBeFormatted, final String expectation) { formatterTestHelper.assertFormatted( formatterRequestProvider.get() - .setToBeFormatted(toBeFormatted) - .setExpectation(expectation)); + .setToBeFormatted(toBeFormatted) + .setExpectation(expectation)); } - public void assertFormatted(String toBeFormatted) { + public void assertFormatted(final String toBeFormatted) { formatterTestHelper.assertFormatted( formatterRequestProvider.get() - .setToBeFormatted(toBeFormatted)); + .setToBeFormatted(toBeFormatted)); } } diff --git a/pom.xml b/pom.xml index f36414ad..7e80da1d 100644 --- a/pom.xml +++ b/pom.xml @@ -8,8 +8,8 @@ 1.0.0-SNAPSHOT - 2.21.0 - 2.11.2 + 2.22.0 + 2.11.3 UTF-8 1.8 1.8 @@ -22,8 +22,13 @@ - org.agileware.natural.common - org.agileware.natural.common.feature + org.agileware.natural.stepmatcher + org.agileware.natural.stepmatcher.ui + org.agileware.natural.lang + org.agileware.natural.lang.ide + org.agileware.natural.lang.tests + org.agileware.natural.lang.ui + org.agileware.natural.lang.ui.tests org.agileware.natural.cucumber org.agileware.natural.cucumber.feature org.agileware.natural.cucumber.ide @@ -39,8 +44,6 @@ org.agileware.natural.jbehave.tests org.agileware.natural.jbehave.ui.tests org.agileware.natural.repository - org.agileware.natural.stepmatcher - org.agileware.natural.stepmatcher.feature org.agileware.natural.testing org.agileware.natural.target diff --git a/runtime-workspace/cucumber-jvm5/src/test/java/org/example/cucumber/jvm5/StepDefinitions.java b/runtime-workspace/cucumber-jvm5/src/test/java/org/example/cucumber/jvm5/StepDefinitions.java index 339cf5ee..811b9ac4 100644 --- a/runtime-workspace/cucumber-jvm5/src/test/java/org/example/cucumber/jvm5/StepDefinitions.java +++ b/runtime-workspace/cucumber-jvm5/src/test/java/org/example/cucumber/jvm5/StepDefinitions.java @@ -1,12 +1,11 @@ package org.example.cucumber.jvm5; -import static org.junit.Assert.assertTrue; - import io.cucumber.java.en.Given; public class StepDefinitions { @Given("I have a matching step definition") - public void i_have_a_matching_step_definition() { - assertTrue("I have a matching step definition", true); - } + public void i_have_a_matching_step_definition() {} + + @Given("{string} has {int} matching step definitions") + public void actor_has_matching_step_definitions(String actor, Integer amount) {} } diff --git a/runtime-workspace/cucumber-jvm5/src/test/resources/org/example/cucumber/jvm5/hello_stepmatcher.feature b/runtime-workspace/cucumber-jvm5/src/test/resources/org/example/cucumber/jvm5/hello_stepmatcher.feature index 7b937fe0..ba3ffdc2 100644 --- a/runtime-workspace/cucumber-jvm5/src/test/resources/org/example/cucumber/jvm5/hello_stepmatcher.feature +++ b/runtime-workspace/cucumber-jvm5/src/test/resources/org/example/cucumber/jvm5/hello_stepmatcher.feature @@ -1,4 +1,6 @@ # language: en Feature: Hello, StepMatcher! - Scenario: - Given I have a matching step definition + + Scenario: + Given I have a matching step definition + And "Bob" has 0 matching step definitions diff --git a/runtime-workspace/example/features/full_example.feature b/runtime-workspace/example/features/full_example.feature index 02a8efa4..00f256ce 100644 --- a/runtime-workspace/example/features/full_example.feature +++ b/runtime-workspace/example/features/full_example.feature @@ -3,48 +3,64 @@ @version:1.0.0 @pet_store Feature: Add a new pet + + """ In order to sell a pet As a store owner I want to add a new pet to the catalog + """ + + ,./;'[]\-= + <>?:"{}|_+ + !@$%^&*()`~ + + | x | y | + | a | 0 | + | b | 1 | + + 田中さんにあげて下さい + パーティーへ行かないか -Background: Add a dog - Given I have the following pet - | name | status | - | Fido | available | - And I add the pet to the store - But the pet is not yet mine + Background: Add a dog + Given I have the following pet + | name | status | + | Fido | available | + And I add the pet to the store + But the pet is not yet mine -@add @fido -Scenario: Add another dog - Then the should be available in the store + @add + @fido + Scenario: Add another dog + Then the should be available in the store -@update @fido -Scenario: - Given the pet is available in the store -9.8 - """ - The quick brown fox - Jumps over the lazy dog - """ - When I update the pet with - | name | status | - | Fido | unavailable | - Then the pet should be "unavailable" in the store + @update + @fido + Scenario: + Given the pet is available in the store -9.8 + """ + The quick brown fox + Jumps over the lazy dog + """ + When I update the pet with + | name | status | + | Fido | unavailable | + Then the pet should be "unavailable" in the store -@eat-pickles -Scenario Outline: Eating pickles - Given there are pickles - When I eat pickles - Then I should have pickles + @eat-pickles + Scenario Outline: Eating pickles + Given there are pickles + When I eat pickles + Then I should have pickles - @hungry - Examples: - | start | eat | left | - | 12 | 10 | 2 | - | 20 | 15 | 5 | + @hungry + Examples: + | start | eat | left | + | 12 | 10 | 2 | + | 20 | 15 | 5 | - @full - Examples: With a title - | start | eat | left | - | 12 | 2 | 10 | - | 20 | 5 | 15 | + @full + Examples: With a title + | start | eat | left | + | 12 | 2 | 10 | + | 20 | 5 | 15 | diff --git a/runtime-workspace/serenity-cucumber-bdd-screenplay b/runtime-workspace/serenity-cucumber-bdd-screenplay new file mode 160000 index 00000000..abfc5130 --- /dev/null +++ b/runtime-workspace/serenity-cucumber-bdd-screenplay @@ -0,0 +1 @@ +Subproject commit abfc5130e7599006586c80c4c35a2cc2fe297d2a