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 1 commit
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
152 changes: 116 additions & 36 deletions src/languages/gherkin.js
Expand Up @@ -5,66 +5,146 @@
Website: https://cucumber.io/docs/gherkin/
*/

export default function(hljs) {
export default function() {
return {
name: 'Gherkin',
aliases: [ 'feature' ],
keywords: {
// Custom pattern to support keywords with spaces and those ending with a colon
$pattern: /[A-Z][a-z]+(?: [A-Z][a-z]+)?:?/,
// Add positive lookbehind to ensure keywords are at the beginning of a line
// $pattern: /(?<=^[ \t]*)[A-Z][a-z]+(?: [A-Z][a-z]+)?:?/,
keyword: [
'Feature:',
'Rule:',
'Example:', 'Scenario:',
'Given', 'When', 'Then', 'And', 'But',
'Background:',
'Scenario Outline:', 'Scenario Template:',
'Examples:', 'Scenarios:'
]
},
contains: [
{
className: 'keyword',
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: /<[^>\s]+>/
scope: 'meta',
begin: '@[^@\\s]+'
},
{
scope: 'comment',
begin: /^#/,
end: /$/
},
// Comments can only start at the beginning of a line
hljs.COMMENT(/^[ \t]*#/, /$/),
// Use positive lookbehind (once available) to exclude leading spaces from comment
// hljs.COMMENT(/(?<=^[ \t]*)#/, /$/),
{
className: 'string',
scope: 'string',
variants: [
{
begin: /"""/,
begin: /^"""/,
end: /"""/
},
{
begin: /```/,
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'
},
]
}
},
]
};
}
14 changes: 7 additions & 7 deletions test/markup/gherkin/default.expect.txt
@@ -1,11 +1,11 @@
<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 Outline:</span> Add two numbers
<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
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>
8 changes: 4 additions & 4 deletions test/markup/gherkin/docstrings.expect.txt
Expand Up @@ -15,7 +15,7 @@
```</span>

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

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

<span class="hljs-keyword">Background:</span>
<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> an example
<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
Expand All @@ -14,7 +14,7 @@
<span class="hljs-keyword">And</span> something else
<span class="hljs-keyword">Then</span> I do this

<span class="hljs-keyword">Scenario:</span> same as example
<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
Expand All @@ -23,12 +23,12 @@
<span class="hljs-keyword">And</span> something else
<span class="hljs-keyword">Then</span> I do this

<span class="hljs-keyword">Scenario Outline:</span> some outline
<span class="hljs-keyword">Examples:</span> part of outline
<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> same as scenario outline
<span class="hljs-keyword">Scenarios:</span> same as examples
<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
2 changes: 1 addition & 1 deletion test/markup/gherkin/variables.expect.txt
@@ -1,4 +1,4 @@
<span class="hljs-keyword">Scenario Outline:</span> Add two numbers
<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
Expand Down