Skip to content

Commit 38970e0

Browse files
authored
javascript: Prefer the longest keywords (#407)
1 parent fc9b7d7 commit 38970e0

File tree

4 files changed

+27
-7
lines changed

4 files changed

+27
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
1414
- [.NET] Prefer the longest step keyword ([#405](https://github.com/cucumber/gherkin/pull/405))
1515
- [PHP] Prefer the longest step keyword ([#403](https://github.com/cucumber/gherkin/pull/403))
1616
- [Go] Prefer the longest step keyword ([#403](https://github.com/cucumber/gherkin/pull/404))
17+
- [JavaScript] Prefer the longest step keyword ([#407](https://github.com/cucumber/gherkin/pull/407))
1718

1819
## [32.1.1] - 2025-04-11
1920
### Fixed

javascript/src/GherkinClassicTokenMatcher.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as messages from '@cucumber/messages'
66
import { TokenType } from './Parser'
77
import ITokenMatcher from './ITokenMatcher'
88
import countSymbols from './countSymbols'
9+
import { compareStepKeywords } from './compareStepKeywords'
910

1011
const DIALECT_DICT: { [key: string]: Dialect } = DIALECTS
1112
const LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$/
@@ -25,6 +26,7 @@ export default class GherkinClassicTokenMatcher implements ITokenMatcher<TokenTy
2526
private activeDocStringSeparator: string
2627
private indentToRemove: number
2728
private keywordTypesMap: { [key: string]: messages.StepKeywordType[] }
29+
private sortedStepKeywords: readonly string[]
2830

2931
constructor(private readonly defaultDialectName: string = 'en') {
3032
this.reset()
@@ -39,6 +41,7 @@ export default class GherkinClassicTokenMatcher implements ITokenMatcher<TokenTy
3941
this.dialectName = newDialectName
4042
this.dialect = newDialect
4143
this.initializeKeywordTypes()
44+
this.initializeSortedStepKeywords()
4245
}
4346

4447
reset() {
@@ -59,6 +62,16 @@ export default class GherkinClassicTokenMatcher implements ITokenMatcher<TokenTy
5962
messages.StepKeywordType.CONJUNCTION)
6063
}
6164

65+
initializeSortedStepKeywords() {
66+
this.sortedStepKeywords = []
67+
.concat(this.dialect.given)
68+
.concat(this.dialect.when)
69+
.concat(this.dialect.then)
70+
.concat(this.dialect.and)
71+
.concat(this.dialect.but)
72+
.sort(compareStepKeywords)
73+
}
74+
6275
match_TagLine(token: IToken<TokenType>) {
6376
if (token.line.startsWith('@')) {
6477
this.setTokenMatched(token, TokenType.TagLine, null, null, null, null, this.getTags(token.line))
@@ -164,13 +177,7 @@ export default class GherkinClassicTokenMatcher implements ITokenMatcher<TokenTy
164177
}
165178

166179
match_StepLine(token: IToken<TokenType>) {
167-
const keywords = []
168-
.concat(this.dialect.given)
169-
.concat(this.dialect.when)
170-
.concat(this.dialect.then)
171-
.concat(this.dialect.and)
172-
.concat(this.dialect.but)
173-
for (const keyword of keywords) {
180+
for (const keyword of this.sortedStepKeywords) {
174181
if (token.line.startsWith(keyword)) {
175182
const title = token.line.getRestTrimmed(keyword.length)
176183
const keywordTypes = this.keywordTypesMap[keyword]

javascript/src/GherkinInMarkdownTokenMatcher.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import DIALECTS from './gherkin-languages.json'
55
import { Item } from './IToken'
66
import * as messages from '@cucumber/messages'
77
import { NoSuchLanguageException } from './Errors'
8+
import { compareStepKeywords } from './compareStepKeywords'
89

910
const DIALECT_DICT: { [key: string]: Dialect } = DIALECTS
1011
const DEFAULT_DOC_STRING_SEPARATOR = /^(```[`]*)(.*)/
@@ -38,6 +39,7 @@ export default class GherkinInMarkdownTokenMatcher implements ITokenMatcher<Toke
3839
.concat(this.dialect.and)
3940
.concat(this.dialect.but)
4041
.filter((value, index, self) => value !== '* ' && self.indexOf(value) === index)
42+
.sort(compareStepKeywords)
4143
this.initializeKeywordTypes()
4244

4345
this.stepRegexp = new RegExp(

javascript/src/compareStepKeywords.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Compares two step keywords based on length. Can be used with Array.sort to
3+
* sort keywords by length, descending.
4+
*
5+
* @param a - the first step keyword
6+
* @param b - the second step keyword
7+
*/
8+
export function compareStepKeywords(a: string, b: string): number {
9+
return b.length - a.length
10+
}

0 commit comments

Comments
 (0)