Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Gherkin support #3643

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGES.md
Expand Up @@ -2,7 +2,6 @@

New Grammars:

- Added 3rd party LookML grammar to SUPPORTED_LANGUAGES [Josh Temple][]
- added 3rd party LookML grammar to SUPPORTED_LANGUAGES [Josh Temple][]
- added 3rd party FunC grammar to SUPPORTED_LANGUAGES [Nikita Sobolev][]

Expand All @@ -14,12 +13,16 @@ Grammars:
- fix(js) do not flag `import()` as a function, rather a keyword [nathnolt][]
- fix(bash) recognize the `((` keyword [Nick Chambers][]
- fix(nix) support escaped dollar signs in strings [h7x4][]
- fix(gherkin) update keyword list [Hirse][]
- fix(gherkin) variables can't contain whitespace [Hirse][]
- ehn(gherkin) docstrings can use backticks [Hirse][]

[Josh Goebel]: https://github.com/joshgoebel
[Josh Temple]: https://github.com/joshtemple
[nathnolt]: https://github.com/nathnolt
[Nick Chambers]: https://github.com/uplime
[h7x4]: https://github.com/h7x4
[Hirse]: https://github.com/Hirse


## Version 11.6.0
Expand Down
143 changes: 123 additions & 20 deletions src/languages/gherkin.js
Expand Up @@ -5,43 +5,146 @@
Website: https://cucumber.io/docs/gherkin/
*/

export default function(hljs) {
export default function() {
return {
name: 'Gherkin',
aliases: [ 'feature' ],
keywords: 'Feature Background Ability Business\ Need Scenario Scenarios Scenario\ Outline Scenario\ Template Examples Given And Then But When',
contains: [
{
className: 'symbol',
begin: '\\*',
relevance: 0
begin: [
/^(Feature|Rule|Examples?|Scenario(?:s| Outline| Template)?|Background)/,
/:/
],
beginScope: {
1: 'keyword',
2: 'punctuation',
},
end: /$/,
contains: [
{
scope: 'variable',
begin: /<[^>\s]+>/
},
]
},
{
className: 'meta',
begin: '@[^@\\s]+'
begin: /^(?:Given|When|Then|And|But)\b/,
beginScope: 'keyword',
end: /$/,
contains: [
{
scope: 'variable',
begin: /<[^>\s]+>/
},
]
},
{
begin: '\\|',
end: '\\|\\w*$',
begin: /^\*(?=[ \t])/,
relevance: 0,
beginScope: 'keyword',
end: /$/,
contains: [
{
className: 'string',
begin: '[^|]+'
}
scope: 'variable',
begin: /<[^>\s]+>/
},
]
},
{
className: 'variable',
begin: '<',
end: '>'
scope: 'meta',
begin: '@[^@\\s]+'
},
{
scope: 'comment',
begin: /^#/,
end: /$/
},
{
scope: 'string',
variants: [
{
begin: /^"""/,
end: /"""/
},
{
begin: /^```/,
end: /```/
}
],
},
hljs.HASH_COMMENT_MODE,
{
className: 'string',
begin: '"""',
end: '"""'
begin: /^\|.*\|$/,
scope: 'string'
},
{
begin: /^[ \t]+/,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repetition is frustrating. Could a single top-level rule to eat tabs at the start of lines accomplish this same thing (probably would leave to false positives)? If not why not add it to each multi-match rule as an optional first component:

        begin: [
          LINE_BEGINS_WS,
          /(Feature|Rule|Examples?|Scenario(?:s| Outline| Template)?|Background)/,
          /:/
        ],
        beginScope: {
          2: 'keyword',
          3: 'punctuation',
        },

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, eating whitespace of the beginning of the line with an empty mode seems to work. Is that what you had in mind? 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multi-match method I show above is what I had in mind, yes. That's the best way I know to handle this kind of thing (without putting the whitespace inside the highlight zone).

relevance: 0,
contains: [
{
begin: [
/(Feature|Rule|Examples?|Scenario(?:s| Outline| Template)?|Background)/,
/:/
],
beginScope: {
1: 'keyword',
2: 'punctuation',
},
end: /$/,
relevance: 10,
contains: [
{
scope: 'variable',
begin: /<[^>\s]+>/
},
]
},
{
begin: /(?:Given|When|Then|And|But)\b/,
beginScope: 'keyword',
end: /$/,
contains: [
{
scope: 'variable',
begin: /<[^>\s]+>/
},
]
},
{
begin: /\*(?=[ \t])/,
relevance: 0,
beginScope: 'keyword',
end: /$/,
contains: [
{
scope: 'variable',
begin: /<[^>\s]+>/
},
]
},
{
scope: 'comment',
begin: /#/,
end: /$/
},
{
scope: 'string',
variants: [
{
begin: /"""/,
end: /"""/
},
{
begin: /```/,
end: /```/
}
]
},
{
begin: /\|.*\|$/,
scope: 'string'
},
]
},
hljs.QUOTE_STRING_MODE
]
};
}
16 changes: 8 additions & 8 deletions test/markup/gherkin/default.expect.txt
@@ -1,12 +1,12 @@
<span class="hljs-comment"># language: en</span>
<span class="hljs-keyword">Feature</span>: Addition
<span class="hljs-keyword">Feature</span><span class="hljs-punctuation">:</span> Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers

<span class="hljs-meta">@this_is_a_tag</span>
<span class="hljs-keyword">Scenario</span> <span class="hljs-keyword">Outline</span>: Add two numbers
<span class="hljs-symbol">*</span> I have a calculator
<span class="hljs-keyword">Scenario Outline</span><span class="hljs-punctuation">:</span> Add two numbers
<span class="hljs-keyword">*</span> I have a calculator
<span class="hljs-keyword">Given</span> I have entered <span class="hljs-variable">&lt;input_1&gt;</span> into the calculator
<span class="hljs-keyword">And</span> I have entered <span class="hljs-variable">&lt;input_2&gt;</span> into the calculator
<span class="hljs-keyword">When</span> I press <span class="hljs-variable">&lt;button&gt;</span>
Expand All @@ -18,8 +18,8 @@
multiline text
&quot;&quot;&quot;</span>

<span class="hljs-keyword">Examples</span>:
|<span class="hljs-string"> input_1 </span>|<span class="hljs-string"> input_2 </span>|<span class="hljs-string"> button </span>|<span class="hljs-string"> output </span>|
|<span class="hljs-string"> 20 </span>|<span class="hljs-string"> 30 </span>|<span class="hljs-string"> add </span>|<span class="hljs-string"> 50 </span>|
|<span class="hljs-string"> 2 </span>|<span class="hljs-string"> 5 </span>|<span class="hljs-string"> add </span>|<span class="hljs-string"> 7 </span>|
|<span class="hljs-string"> 0 </span>|<span class="hljs-string"> 40 </span>|<span class="hljs-string"> add </span>|<span class="hljs-string"> 40 </span>|
<span class="hljs-keyword">Examples</span><span class="hljs-punctuation">:</span>
<span class="hljs-string">| input_1 | input_2 | button | output |</span>
<span class="hljs-string">| 20 | 30 | add | 50 |</span>
<span class="hljs-string">| 2 | 5 | add | 7 |</span>
<span class="hljs-string">| 0 | 40 | add | 40 |</span>
21 changes: 21 additions & 0 deletions test/markup/gherkin/docstrings.expect.txt
@@ -0,0 +1,21 @@
<span class="hljs-keyword">Given</span> a blog post named &quot;Random&quot; with Markdown body
<span class="hljs-string">&quot;&quot;&quot;
Some Title, Eh?
===============
Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
&quot;&quot;&quot;</span>

<span class="hljs-keyword">Given</span> a blog post named &quot;Random&quot; with Markdown body
<span class="hljs-string">```
Some Title, Eh?
===============
Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
```</span>

<span class="hljs-keyword">Given</span> the following users exist:
<span class="hljs-string">| name | email | twitter |</span>
<span class="hljs-string">| Aslak | aslak@cucumber.io | @aslak_hellesoy |</span>
<span class="hljs-string">| Julien | julien@cucumber.io | @jbpros |</span>
<span class="hljs-string">| Matt | matt@cucumber.io | @mattwynne |</span>
21 changes: 21 additions & 0 deletions test/markup/gherkin/docstrings.txt
@@ -0,0 +1,21 @@
Given a blog post named "Random" with Markdown body
"""
Some Title, Eh?
===============
Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
"""

Given a blog post named "Random" with Markdown body
```
Some Title, Eh?
===============
Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
```

Given the following users exist:
| name | email | twitter |
| Aslak | aslak@cucumber.io | @aslak_hellesoy |
| Julien | julien@cucumber.io | @jbpros |
| Matt | matt@cucumber.io | @mattwynne |
34 changes: 34 additions & 0 deletions test/markup/gherkin/keywords.expect.txt
@@ -0,0 +1,34 @@
<span class="hljs-keyword">Feature</span><span class="hljs-punctuation">:</span> some feature

<span class="hljs-keyword">Rule</span><span class="hljs-punctuation">:</span> some rule

<span class="hljs-keyword">Background</span><span class="hljs-punctuation">:</span>
<span class="hljs-keyword">Given</span> some background

<span class="hljs-keyword">Example</span><span class="hljs-punctuation">:</span> an example
<span class="hljs-keyword">Given</span> the conditions
<span class="hljs-keyword">And</span> some other conditions
<span class="hljs-keyword">*</span> same as and
<span class="hljs-keyword">But</span> not these conditions
<span class="hljs-keyword">When</span> something happens
<span class="hljs-keyword">And</span> something else
<span class="hljs-keyword">Then</span> I do this

<span class="hljs-keyword">Scenario</span><span class="hljs-punctuation">:</span> same as example
<span class="hljs-keyword">Given</span> the conditions
<span class="hljs-keyword">And</span> some other conditions
<span class="hljs-keyword">*</span> same as and
<span class="hljs-keyword">But</span> not these conditions
<span class="hljs-keyword">When</span> something happens
<span class="hljs-keyword">And</span> something else
<span class="hljs-keyword">Then</span> I do this

<span class="hljs-keyword">Scenario Outline</span><span class="hljs-punctuation">:</span> some outline
<span class="hljs-keyword">Examples</span><span class="hljs-punctuation">:</span> part of outline
<span class="hljs-keyword">When</span> a
<span class="hljs-keyword">Then</span> b

<span class="hljs-keyword">Scenario Template</span><span class="hljs-punctuation">:</span> same as scenario outline
<span class="hljs-keyword">Scenarios</span><span class="hljs-punctuation">:</span> same as examples
<span class="hljs-keyword">When</span> a
<span class="hljs-keyword">Then</span> b
34 changes: 34 additions & 0 deletions test/markup/gherkin/keywords.txt
@@ -0,0 +1,34 @@
Feature: some feature

Rule: some rule

Background:
Given some background

Example: an example
Given the conditions
And some other conditions
* same as and
But not these conditions
When something happens
And something else
Then I do this

Scenario: same as example
Given the conditions
And some other conditions
* same as and
But not these conditions
When something happens
And something else
Then I do this

Scenario Outline: some outline
Examples: part of outline
When a
Then b

Scenario Template: same as scenario outline
Scenarios: same as examples
When a
Then b
5 changes: 5 additions & 0 deletions test/markup/gherkin/variables.expect.txt
@@ -0,0 +1,5 @@
<span class="hljs-keyword">Scenario Outline</span><span class="hljs-punctuation">:</span> Add two numbers
<span class="hljs-keyword">Given</span> I have entered <span class="hljs-variable">&lt;input_1&gt;</span> into the calculator
<span class="hljs-keyword">And</span> I have entered <span class="hljs-variable">&lt;input_2&gt;</span> into the calculator
<span class="hljs-keyword">And</span> first &lt; second and second &gt; first
<span class="hljs-keyword">Then</span> second &lt;- first
5 changes: 5 additions & 0 deletions test/markup/gherkin/variables.txt
@@ -0,0 +1,5 @@
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
And first < second and second > first
Then second <- first