Skip to content

Commit

Permalink
Go step definitions support (#130)
Browse files Browse the repository at this point in the history
* Add support for go

- Support for Godog 'Given', 'When', 'Then' and 'Step' step definition keywords
- Support for 'interpreted_string_literal' and 'raw_string_literal' expressions
- Godog snippet implementation

---------

Co-authored-by: Aslak Hellesøy <1000+aslakhellesoy@users.noreply.github.com>
Co-authored-by: Kieran Ryan <kierankilkenny@gmail.com>
  • Loading branch information
3 people authored Apr 7, 2024
1 parent 5af1b59 commit 9c4cec5
Show file tree
Hide file tree
Showing 14 changed files with 2,472 additions and 8,952 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- (Python) Fix index failures from partial parameter type matches ([#196](https://github.com/cucumber/language-service/pull/196))
- (Rust) Support for r# raw strings with step definition patterns ([#176](https://github.com/cucumber/language-service/pull/176))
- (Rust) Line continuation characters in rust step definition patterns ([#179](https://github.com/cucumber/language-service/pull/179))
- (Python) Unexpected spaces and commas in generated step definitions [#160](https://github.com/cucumber/language-service/issues/160)
- (Python) Unexpected spaces and commas in generated step definitions ([#160](https://github.com/cucumber/language-service/issues/160))

### Added
- (Python) Support for u-strings with step definition patterns ([#173](https://github.com/cucumber/language-service/pull/173))
- (Go) Support for Godog step definitions ([#130](https://github.com/cucumber/language-service/pull/130))

## [1.4.1] - 2023-07-16
### Fixed
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Cucumber Language Service
</h1>
<p align="center">
<b>Cucumber language services used by <a href="https://github.com/cucumber/language-server#readme">Cucumber Language Server</a> and <a href="https://github.com/cucumber/monaco#readme">Cucumber Monaco</a>.</b>
<b>Cucumber language services used by <a href="https://github.com/cucumber/language-server#readme">Cucumber Language Server</a> and <a href="https://github.com/cucumber/monaco#readme">Cucumber Monaco</a></b>
</p>

<p align="center">
Expand Down Expand Up @@ -36,7 +36,7 @@
- 🌎 Gherkin localisation
- 📖 Language support
- C#
- [Go](https://github.com/cucumber/language-service/issues/72)
- Go
- Java
- JavaScript
- PHP
Expand Down
11,287 changes: 2,345 additions & 8,942 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"tree-sitter": "0.20.6",
"tree-sitter-c-sharp": "0.20.0",
"tree-sitter-cli": "0.20.8",
"tree-sitter-go": "0.20.0",
"tree-sitter-java": "0.20.2",
"tree-sitter-php": "0.20.0",
"tree-sitter-python": "0.20.4",
Expand Down
5 changes: 5 additions & 0 deletions scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const languages = [
dir: '',
wasm: 'rust',
},
{
npm: 'tree-sitter-go',
dir: '',
wasm: 'go',
},
]

// Build wasm parsers for supported languages
Expand Down
68 changes: 68 additions & 0 deletions src/language/goLanguage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { StringOrRegExp } from '@cucumber/cucumber-expressions'

import { unsupportedOperation } from './helpers.js'
import { Language, TreeSitterSyntaxNode } from './types.js'

export const goLanguage: Language = {
toParameterTypeName: unsupportedOperation,
toParameterTypeRegExps: unsupportedOperation,
toStepDefinitionExpression(node: TreeSitterSyntaxNode): StringOrRegExp {
const text = stringLiteral(node)
const hasRegExpAnchors = text[0] == '^' || text[text.length - 1] == '$'
return hasRegExpAnchors ? new RegExp(text) : text
},
// Empty array as Godog does not support Cucumber Expressions
defineParameterTypeQueries: [],
defineStepDefinitionQueries: [
`(function_declaration
body: (block
(expression_statement
(call_expression
function: (selector_expression
field: (field_identifier) @annotation-name
)
arguments: (argument_list
[
(raw_string_literal) @expression
]
)
(#match? @annotation-name "Given|When|Then|Step")))))@root
`,
`(function_declaration
body: (block
(expression_statement
(call_expression
function: (selector_expression
field: (field_identifier) @annotation-name
)
arguments: (argument_list
[
(interpreted_string_literal) @expression
]
)
(#match? @annotation-name "Given|When|Then|Step")))))@root
`,
],
snippetParameters: {
int: { type: 'int', name: 'i' },
float: { type: 'float', name: 'f' },
word: { type: 'string' },
string: { type: 'string', name: 's' },
double: { type: 'float', name: 'd' },
bigdecimal: { type: 'float64', name: 'bigDecimal' },
byte: { type: 'rune', name: 'b' },
short: { type: 'int32', name: 's' },
long: { type: 'int64', name: 'l' },
biginteger: { type: 'int64', name: 'bigInteger' },
'': { type: 'string', name: 'arg' },
},
defaultSnippetTemplate: `
// Generated with Cucumber Expressions syntax, which are not supported by Godog. Convert to Regular Expressions.
ctx.{{ keyword }}(\`{{ expression }}\`, <stepFunc>)
`,
}

export function stringLiteral(node: TreeSitterSyntaxNode | null): string {
if (node === null) throw new Error('node cannot be null')
return node.text.slice(1, -1)
}
4 changes: 4 additions & 0 deletions src/language/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ export function filter(
function flatten(node: TreeSitterSyntaxNode): TreeSitterSyntaxNode[] {
return node.children.reduce((r, o) => [...r, ...flatten(o)], [node])
}

export function unsupportedOperation(): never {
throw new Error('Unsupported operation')
}
2 changes: 2 additions & 0 deletions src/language/languages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { csharpLanguage } from './csharpLanguage.js'
import { goLanguage } from './goLanguage.js'
import { javaLanguage } from './javaLanguage.js'
import { javascriptLanguage } from './javascriptLanguage.js'
import { phpLanguage } from './phpLanguage.js'
Expand All @@ -17,6 +18,7 @@ const languageByName: Record<LanguageName, Language> = {
rust: rustLanguage,
python: pythonLanguage,
javascript: javascriptLanguage,
go: goLanguage,
}

export function getLanguage(languageName: LanguageName): Language {
Expand Down
9 changes: 3 additions & 6 deletions src/language/phpLanguage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { unsupportedOperation } from './helpers.js'
import { Language } from './types.js'

export const phpLanguage: Language = {
toParameterTypeName() {
throw new Error('Unsupported operation')
},
toParameterTypeRegExps() {
throw new Error('Unsupported operation')
},
toParameterTypeName: unsupportedOperation,
toParameterTypeRegExps: unsupportedOperation,
toStepDefinitionExpression(node) {
// match multiline comment
const text = node.text
Expand Down
1 change: 1 addition & 0 deletions src/language/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const LanguageNames = [
'ruby',
'rust',
'javascript',
'go',
] as const
export type LanguageName = (typeof LanguageNames)[number]

Expand Down
2 changes: 1 addition & 1 deletion src/service/getGenerateSnippetCodeAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { stepDefinitionSnippet } from './snippet/stepDefinitionSnippet.js'
* @param link where the snippet should be added
* @param relativePath the relative path from the workspace root
* @param createFile true if link.targetUri does not exist
* @param mustacheTemplate template to generae the snippet
* @param mustacheTemplate template to generate the snippet
* @param languageName the name of the language we're generating for
* @param registry parameter types
*/
Expand Down
5 changes: 5 additions & 0 deletions src/tree-sitter-node/NodeParserAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import Parser, { Query } from 'tree-sitter'
// @ts-ignore
import Csharp from 'tree-sitter-c-sharp'
// @ts-ignore
import Go from 'tree-sitter-go'
// @ts-ignore
import Java from 'tree-sitter-java'
// @ts-ignore
import Php from 'tree-sitter-php'
Expand Down Expand Up @@ -48,6 +50,9 @@ export class NodeParserAdapter implements ParserAdapter {
case 'rust':
this.parser.setLanguage(Rust)
break
case 'go':
this.parser.setLanguage(Go)
break
default:
throw new Error(`Unsupported language: ${languageName}`)
}
Expand Down
22 changes: 22 additions & 0 deletions test/language/goLanguage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import assert from 'assert'

import { stringLiteral } from '../../src/language/goLanguage.js'
import { TreeSitterSyntaxNode } from '../../src/language/types.js'

describe('goLanguage', () => {
it('should remove enclosing string quotations and backticks', () => {
const cases = ['`^I eat (d+)$`', '"^I eat (d+)$"']

cases.forEach((expression) => {
const node: TreeSitterSyntaxNode = {
type: 'raw_string_literal',
text: expression,
startPosition: { row: 1, column: 19 },
endPosition: { row: 1, column: 19 + expression.length },
children: [],
}
const result = stringLiteral(node)
assert.deepStrictEqual(result, '^I eat (d+)$')
})
})
})
11 changes: 11 additions & 0 deletions test/language/testdata/go/StepDefinitions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"github.com/cucumber/godog"
)

func a_regexp() {}

func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Given(`^a regexp$`, a_regexp)
}

0 comments on commit 9c4cec5

Please sign in to comment.