cucumber-gherkin 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +10 -0
- data/bin/gherkin +68 -0
- data/bin/gherkin-ruby +2 -0
- data/lib/gherkin.rb +39 -0
- data/lib/gherkin/ast_builder.rb +257 -0
- data/lib/gherkin/ast_node.rb +30 -0
- data/lib/gherkin/dialect.rb +62 -0
- data/lib/gherkin/errors.rb +47 -0
- data/lib/gherkin/gherkin-languages.json +3623 -0
- data/lib/gherkin/gherkin_line.rb +97 -0
- data/lib/gherkin/parser.rb +3429 -0
- data/lib/gherkin/pickles/compiler.rb +188 -0
- data/lib/gherkin/query.rb +61 -0
- data/lib/gherkin/stream/parser_message_stream.rb +96 -0
- data/lib/gherkin/stream/subprocess_message_stream.rb +26 -0
- data/lib/gherkin/token.rb +18 -0
- data/lib/gherkin/token_formatter_builder.rb +39 -0
- data/lib/gherkin/token_matcher.rb +169 -0
- data/lib/gherkin/token_scanner.rb +40 -0
- data/spec/capture_warnings.rb +74 -0
- data/spec/coverage.rb +7 -0
- data/spec/gherkin/dialect_spec.rb +13 -0
- data/spec/gherkin/gherkin_spec.rb +45 -0
- data/spec/gherkin/query_spec.rb +142 -0
- data/spec/gherkin/stream/parser_message_stream_spec.rb +67 -0
- data/spec/gherkin/stream/subprocess_message_stream_spec.rb +18 -0
- metadata +167 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
module Gherkin
|
2
|
+
module Pickles
|
3
|
+
class Compiler
|
4
|
+
def initialize(id_generator)
|
5
|
+
@id_generator = id_generator
|
6
|
+
end
|
7
|
+
|
8
|
+
def compile(gherkin_document, source)
|
9
|
+
pickles = []
|
10
|
+
|
11
|
+
return pickles unless gherkin_document.feature
|
12
|
+
feature = gherkin_document.feature
|
13
|
+
language = feature.language
|
14
|
+
tags = feature.tags
|
15
|
+
|
16
|
+
compile_feature(pickles, language, tags, feature, source)
|
17
|
+
pickles
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def compile_feature(pickles, language, tags, feature, source)
|
23
|
+
feature_background_steps = []
|
24
|
+
feature.children.each do |child|
|
25
|
+
if child.background
|
26
|
+
feature_background_steps.concat(child.background.steps)
|
27
|
+
elsif child.rule
|
28
|
+
compile_rule(pickles, language, tags, feature_background_steps, child.rule, source)
|
29
|
+
else
|
30
|
+
scenario = child.scenario
|
31
|
+
if scenario.examples.empty?
|
32
|
+
compile_scenario(tags, feature_background_steps, scenario, language, pickles, source)
|
33
|
+
else
|
34
|
+
compile_scenario_outline(tags, feature_background_steps, scenario, language, pickles, source)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def compile_rule(pickles, language, tags, feature_background_steps, rule, source)
|
41
|
+
rule_background_steps = feature_background_steps.dup
|
42
|
+
rule.children.each do |child|
|
43
|
+
if child.background
|
44
|
+
rule_background_steps.concat(child.background.steps)
|
45
|
+
else
|
46
|
+
scenario = child.scenario
|
47
|
+
if scenario.examples.empty?
|
48
|
+
compile_scenario(tags, rule_background_steps, scenario, language, pickles, source)
|
49
|
+
else
|
50
|
+
compile_scenario_outline(tags, rule_background_steps, scenario, language, pickles, source)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile_scenario(feature_tags, background_steps, scenario, language, pickles, source)
|
57
|
+
steps = scenario.steps.empty? ? [] : [].concat(pickle_steps(background_steps))
|
58
|
+
|
59
|
+
tags = [].concat(feature_tags).concat(scenario.tags)
|
60
|
+
|
61
|
+
scenario.steps.each do |step|
|
62
|
+
steps.push(pickle_step(step))
|
63
|
+
end
|
64
|
+
|
65
|
+
pickle = Cucumber::Messages::Pickle.new(
|
66
|
+
uri: source.uri,
|
67
|
+
id: @id_generator.new_id,
|
68
|
+
tags: pickle_tags(tags),
|
69
|
+
name: scenario.name,
|
70
|
+
language: language,
|
71
|
+
ast_node_ids: [scenario.id],
|
72
|
+
steps: steps
|
73
|
+
)
|
74
|
+
pickles.push(pickle)
|
75
|
+
end
|
76
|
+
|
77
|
+
def compile_scenario_outline(feature_tags, background_steps, scenario, language, pickles, source)
|
78
|
+
scenario.examples.reject { |examples| examples.table_header.nil? }.each do |examples|
|
79
|
+
variable_cells = examples.table_header.cells
|
80
|
+
examples.table_body.each do |values_row|
|
81
|
+
value_cells = values_row.cells
|
82
|
+
steps = scenario.steps.empty? ? [] : [].concat(pickle_steps(background_steps))
|
83
|
+
tags = [].concat(feature_tags).concat(scenario.tags).concat(examples.tags)
|
84
|
+
|
85
|
+
scenario.steps.each do |scenario_outline_step|
|
86
|
+
step_props = pickle_step_props(scenario_outline_step, variable_cells, values_row)
|
87
|
+
steps.push(Cucumber::Messages::Pickle::PickleStep.new(step_props))
|
88
|
+
end
|
89
|
+
|
90
|
+
pickle = Cucumber::Messages::Pickle.new(
|
91
|
+
uri: source.uri,
|
92
|
+
id: @id_generator.new_id,
|
93
|
+
name: interpolate(scenario.name, variable_cells, value_cells),
|
94
|
+
language: language,
|
95
|
+
steps: steps,
|
96
|
+
tags: pickle_tags(tags),
|
97
|
+
ast_node_ids: [
|
98
|
+
scenario.id,
|
99
|
+
values_row.id
|
100
|
+
],
|
101
|
+
)
|
102
|
+
pickles.push(pickle)
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def interpolate(name, variable_cells, value_cells)
|
109
|
+
variable_cells.each_with_index do |variable_cell, n|
|
110
|
+
value_cell = value_cells[n]
|
111
|
+
name = name.gsub('<' + variable_cell.value + '>', value_cell.value)
|
112
|
+
end
|
113
|
+
name
|
114
|
+
end
|
115
|
+
|
116
|
+
def pickle_steps(steps)
|
117
|
+
steps.map do |step|
|
118
|
+
pickle_step(step)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def pickle_step(step)
|
123
|
+
Cucumber::Messages::Pickle::PickleStep.new(pickle_step_props(step, [], nil))
|
124
|
+
end
|
125
|
+
|
126
|
+
def pickle_step_props(step, variable_cells, values_row)
|
127
|
+
value_cells = values_row ? values_row.cells : []
|
128
|
+
props = {
|
129
|
+
id: @id_generator.new_id,
|
130
|
+
ast_node_ids: [step.id],
|
131
|
+
text: interpolate(step.text, variable_cells, value_cells),
|
132
|
+
}
|
133
|
+
if values_row
|
134
|
+
props[:ast_node_ids].push(values_row.id)
|
135
|
+
end
|
136
|
+
|
137
|
+
if step.data_table
|
138
|
+
data_table = Cucumber::Messages::PickleStepArgument.new(
|
139
|
+
data_table: pickle_data_table(step.data_table, variable_cells, value_cells)
|
140
|
+
)
|
141
|
+
props[:argument] = data_table
|
142
|
+
end
|
143
|
+
if step.doc_string
|
144
|
+
doc_string = Cucumber::Messages::PickleStepArgument.new(
|
145
|
+
doc_string: pickle_doc_string(step.doc_string, variable_cells, value_cells)
|
146
|
+
)
|
147
|
+
props[:argument] = doc_string
|
148
|
+
end
|
149
|
+
props
|
150
|
+
end
|
151
|
+
|
152
|
+
def pickle_data_table(data_table, variable_cells, value_cells)
|
153
|
+
Cucumber::Messages::PickleStepArgument::PickleTable.new(
|
154
|
+
rows: data_table.rows.map do |row|
|
155
|
+
Cucumber::Messages::PickleStepArgument::PickleTable::PickleTableRow.new(
|
156
|
+
cells: row.cells.map do |cell|
|
157
|
+
Cucumber::Messages::PickleStepArgument::PickleTable::PickleTableRow::PickleTableCell.new(
|
158
|
+
value: interpolate(cell.value, variable_cells, value_cells)
|
159
|
+
)
|
160
|
+
end
|
161
|
+
)
|
162
|
+
end
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
def pickle_doc_string(doc_string, variable_cells, value_cells)
|
167
|
+
props = {
|
168
|
+
content: interpolate(doc_string.content, variable_cells, value_cells)
|
169
|
+
}
|
170
|
+
if doc_string.media_type
|
171
|
+
props[:media_type] = interpolate(doc_string.media_type, variable_cells, value_cells)
|
172
|
+
end
|
173
|
+
Cucumber::Messages::PickleStepArgument::PickleDocString.new(props)
|
174
|
+
end
|
175
|
+
|
176
|
+
def pickle_tags(tags)
|
177
|
+
tags.map {|tag| pickle_tag(tag)}
|
178
|
+
end
|
179
|
+
|
180
|
+
def pickle_tag(tag)
|
181
|
+
Cucumber::Messages::Pickle::PickleTag.new(
|
182
|
+
name: tag.name,
|
183
|
+
ast_node_id: tag.id
|
184
|
+
)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Gherkin
|
2
|
+
class Query
|
3
|
+
def initialize
|
4
|
+
@ast_node_locations = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def update(message)
|
8
|
+
update_feature(message.gherkin_document.feature) if message.gherkin_document
|
9
|
+
end
|
10
|
+
|
11
|
+
def location(ast_node_id)
|
12
|
+
return @ast_node_locations[ast_node_id] if @ast_node_locations.has_key?(ast_node_id)
|
13
|
+
raise AstNodeNotLocatedException, "No location found for #{ast_node_id} }. Known: #{@ast_node_locations.keys}"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def update_feature(feature)
|
19
|
+
store_nodes_location(feature.tags)
|
20
|
+
|
21
|
+
feature.children.each do |child|
|
22
|
+
update_rule(child.rule) if child.rule
|
23
|
+
update_background(child.background) if child.background
|
24
|
+
update_scenario(child.scenario) if child.scenario
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_rule(rule)
|
29
|
+
rule.children.each do |child|
|
30
|
+
update_background(child.background) if child.background
|
31
|
+
update_scenario(child.scenario) if child.scenario
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_background(background)
|
36
|
+
update_steps(background.steps)
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_scenario(scenario)
|
40
|
+
store_node_location(scenario)
|
41
|
+
store_nodes_location(scenario.tags)
|
42
|
+
update_steps(scenario.steps)
|
43
|
+
scenario.examples.each do |examples|
|
44
|
+
store_nodes_location(examples.tags)
|
45
|
+
store_nodes_location(examples.table_body)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_steps(steps)
|
50
|
+
store_nodes_location(steps)
|
51
|
+
end
|
52
|
+
|
53
|
+
def store_nodes_location(nodes)
|
54
|
+
nodes.each { |node| store_node_location(node) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def store_node_location(node)
|
58
|
+
@ast_node_locations[node.id] = node.location
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'cucumber/messages'
|
2
|
+
require 'gherkin/parser'
|
3
|
+
require 'gherkin/token_matcher'
|
4
|
+
require 'gherkin/pickles/compiler'
|
5
|
+
|
6
|
+
module Gherkin
|
7
|
+
module Stream
|
8
|
+
class ParserMessageStream
|
9
|
+
def initialize(paths, sources, options)
|
10
|
+
@paths = paths
|
11
|
+
@sources = sources
|
12
|
+
@options = options
|
13
|
+
|
14
|
+
id_generator = options[:id_generator] || Cucumber::Messages::IdGenerator::UUID.new
|
15
|
+
@parser = Parser.new(AstBuilder.new(id_generator))
|
16
|
+
@compiler = Pickles::Compiler.new(id_generator)
|
17
|
+
end
|
18
|
+
|
19
|
+
def messages
|
20
|
+
enumerated = false
|
21
|
+
Enumerator.new do |y|
|
22
|
+
raise DoubleIterationException, "Messages have already been enumerated" if enumerated
|
23
|
+
enumerated = true
|
24
|
+
|
25
|
+
sources.each do |source|
|
26
|
+
y.yield(Cucumber::Messages::Envelope.new(source: source)) if @options[:include_source]
|
27
|
+
begin
|
28
|
+
gherkin_document = nil
|
29
|
+
|
30
|
+
if @options[:include_gherkin_document]
|
31
|
+
gherkin_document = build_gherkin_document(source)
|
32
|
+
y.yield(Cucumber::Messages::Envelope.new(gherkin_document: gherkin_document))
|
33
|
+
end
|
34
|
+
if @options[:include_pickles]
|
35
|
+
gherkin_document ||= build_gherkin_document(source)
|
36
|
+
pickles = @compiler.compile(gherkin_document, source)
|
37
|
+
pickles.each do |pickle|
|
38
|
+
y.yield(Cucumber::Messages::Envelope.new(pickle: pickle))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue CompositeParserException => err
|
42
|
+
yield_error_attachments(y, err.errors, source.uri)
|
43
|
+
rescue ParserException => err
|
44
|
+
yield_error_attachments(y, [err], source.uri)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def yield_error_attachments(y, errors, uri)
|
53
|
+
errors.each do |err|
|
54
|
+
attachment = Cucumber::Messages::Attachment.new(
|
55
|
+
source: Cucumber::Messages::SourceReference.new(
|
56
|
+
uri: uri,
|
57
|
+
location: Cucumber::Messages::Location.new(
|
58
|
+
line: err.location[:line],
|
59
|
+
column: err.location[:column]
|
60
|
+
)
|
61
|
+
),
|
62
|
+
text: err.message
|
63
|
+
)
|
64
|
+
y.yield(Cucumber::Messages::Envelope.new(attachment: attachment))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def sources
|
69
|
+
Enumerator.new do |y|
|
70
|
+
@paths.each do |path|
|
71
|
+
source = Cucumber::Messages::Source.new(
|
72
|
+
uri: path,
|
73
|
+
data: File.open(path, 'r:UTF-8', &:read),
|
74
|
+
media_type: 'text/x.cucumber.gherkin+plain'
|
75
|
+
)
|
76
|
+
y.yield(source)
|
77
|
+
end
|
78
|
+
@sources.each do |source|
|
79
|
+
y.yield(source)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_gherkin_document(source)
|
85
|
+
if @options[:default_dialect]
|
86
|
+
token_matcher = TokenMatcher.new(@options[:default_dialect])
|
87
|
+
gd = @parser.parse(source.data, token_matcher)
|
88
|
+
else
|
89
|
+
gd = @parser.parse(source.data)
|
90
|
+
end
|
91
|
+
gd[:uri] = source.uri
|
92
|
+
Cucumber::Messages::GherkinDocument.new(gd)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'cucumber/messages'
|
3
|
+
|
4
|
+
module Gherkin
|
5
|
+
module Stream
|
6
|
+
class SubprocessMessageStream
|
7
|
+
def initialize(gherkin_executable, paths, print_source, print_ast, print_pickles)
|
8
|
+
@gherkin_executable, @paths, @print_source, @print_ast, @print_pickles = gherkin_executable, paths, print_source, print_ast, print_pickles
|
9
|
+
end
|
10
|
+
|
11
|
+
def messages
|
12
|
+
args = [@gherkin_executable]
|
13
|
+
args.push('--no-source') unless @print_source
|
14
|
+
args.push('--no-ast') unless @print_ast
|
15
|
+
args.push('--no-pickles') unless @print_pickles
|
16
|
+
args = args.concat(@paths)
|
17
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(*args)
|
18
|
+
if(stdout.eof?)
|
19
|
+
error = stderr.read
|
20
|
+
raise error
|
21
|
+
end
|
22
|
+
Cucumber::Messages::BinaryToMessageEnumerator.new(stdout)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gherkin
|
2
|
+
class Token < Struct.new(:line, :location)
|
3
|
+
attr_accessor :matched_type, :matched_text, :matched_keyword, :matched_indent,
|
4
|
+
:matched_items, :matched_gherkin_dialect
|
5
|
+
|
6
|
+
def eof?
|
7
|
+
line.nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
def detach
|
11
|
+
# TODO: detach line - is this needed?
|
12
|
+
end
|
13
|
+
|
14
|
+
def token_value
|
15
|
+
eof? ? "EOF" : line.get_line_text(-1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Gherkin
|
2
|
+
class TokenFormatterBuilder
|
3
|
+
def initialize
|
4
|
+
reset
|
5
|
+
end
|
6
|
+
|
7
|
+
def reset
|
8
|
+
@tokens_text = ""
|
9
|
+
end
|
10
|
+
|
11
|
+
def build(token)
|
12
|
+
@tokens_text << "#{format_token(token)}\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_rule(rule_type)
|
16
|
+
end
|
17
|
+
|
18
|
+
def end_rule(rule_type)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_result
|
22
|
+
@tokens_text
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def format_token(token)
|
27
|
+
return "EOF" if token.eof?
|
28
|
+
|
29
|
+
sprintf "(%s:%s)%s:%s/%s/%s",
|
30
|
+
token.location[:line],
|
31
|
+
token.location[:column],
|
32
|
+
token.matched_type,
|
33
|
+
token.matched_keyword,
|
34
|
+
token.matched_text,
|
35
|
+
Array(token.matched_items).map { |i| "#{i.column}:#{i.text}"}.join(',')
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'gherkin/dialect'
|
2
|
+
require 'gherkin/errors'
|
3
|
+
|
4
|
+
module Gherkin
|
5
|
+
class TokenMatcher
|
6
|
+
LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$/
|
7
|
+
|
8
|
+
def initialize(dialect_name = 'en')
|
9
|
+
@default_dialect_name = dialect_name
|
10
|
+
change_dialect(dialect_name, nil)
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset
|
15
|
+
change_dialect(@default_dialect_name, nil) unless @dialect_name == @default_dialect_name
|
16
|
+
@active_doc_string_separator = nil
|
17
|
+
@indent_to_remove = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def match_TagLine(token)
|
21
|
+
return false unless token.line.start_with?('@')
|
22
|
+
|
23
|
+
set_token_matched(token, :TagLine, nil, nil, nil, token.line.tags)
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def match_FeatureLine(token)
|
28
|
+
match_title_line(token, :FeatureLine, @dialect.feature_keywords)
|
29
|
+
end
|
30
|
+
|
31
|
+
def match_RuleLine(token)
|
32
|
+
match_title_line(token, :RuleLine, @dialect.rule_keywords)
|
33
|
+
end
|
34
|
+
|
35
|
+
def match_ScenarioLine(token)
|
36
|
+
match_title_line(token, :ScenarioLine, @dialect.scenario_keywords) ||
|
37
|
+
match_title_line(token, :ScenarioLine, @dialect.scenario_outline_keywords)
|
38
|
+
end
|
39
|
+
|
40
|
+
def match_BackgroundLine(token)
|
41
|
+
match_title_line(token, :BackgroundLine, @dialect.background_keywords)
|
42
|
+
end
|
43
|
+
|
44
|
+
def match_ExamplesLine(token)
|
45
|
+
match_title_line(token, :ExamplesLine, @dialect.examples_keywords)
|
46
|
+
end
|
47
|
+
|
48
|
+
def match_TableRow(token)
|
49
|
+
return false unless token.line.start_with?('|')
|
50
|
+
# TODO: indent
|
51
|
+
set_token_matched(token, :TableRow, nil, nil, nil, token.line.table_cells)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def match_Empty(token)
|
56
|
+
return false unless token.line.empty?
|
57
|
+
set_token_matched(token, :Empty, nil, nil, 0)
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def match_Comment(token)
|
62
|
+
return false unless token.line.start_with?('#')
|
63
|
+
text = token.line.get_line_text(0) #take the entire line, including leading space
|
64
|
+
set_token_matched(token, :Comment, text, nil, 0)
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def match_Language(token)
|
69
|
+
return false unless token.line.trimmed_line_text =~ LANGUAGE_PATTERN
|
70
|
+
|
71
|
+
dialect_name = $1
|
72
|
+
set_token_matched(token, :Language, dialect_name)
|
73
|
+
|
74
|
+
change_dialect(dialect_name, token.location)
|
75
|
+
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def match_DocStringSeparator(token)
|
80
|
+
if @active_doc_string_separator.nil?
|
81
|
+
# open
|
82
|
+
_match_DocStringSeparator(token, '"""', true) ||
|
83
|
+
_match_DocStringSeparator(token, '```', true)
|
84
|
+
else
|
85
|
+
# close
|
86
|
+
_match_DocStringSeparator(token, @active_doc_string_separator, false)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def _match_DocStringSeparator(token, separator, is_open)
|
91
|
+
return false unless token.line.start_with?(separator)
|
92
|
+
|
93
|
+
media_type = nil
|
94
|
+
if is_open
|
95
|
+
media_type = token.line.get_rest_trimmed(separator.length)
|
96
|
+
@active_doc_string_separator = separator
|
97
|
+
@indent_to_remove = token.line.indent
|
98
|
+
else
|
99
|
+
@active_doc_string_separator = nil
|
100
|
+
@indent_to_remove = 0
|
101
|
+
end
|
102
|
+
|
103
|
+
set_token_matched(token, :DocStringSeparator, media_type, separator)
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
def match_EOF(token)
|
108
|
+
return false unless token.eof?
|
109
|
+
set_token_matched(token, :EOF)
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def match_Other(token)
|
114
|
+
text = token.line.get_line_text(@indent_to_remove) # take the entire line, except removing DocString indents
|
115
|
+
set_token_matched(token, :Other, unescape_docstring(text), nil, 0)
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def match_StepLine(token)
|
120
|
+
keywords = @dialect.given_keywords +
|
121
|
+
@dialect.when_keywords +
|
122
|
+
@dialect.then_keywords +
|
123
|
+
@dialect.and_keywords +
|
124
|
+
@dialect.but_keywords
|
125
|
+
|
126
|
+
keyword = keywords.detect { |k| token.line.start_with?(k) }
|
127
|
+
|
128
|
+
return false unless keyword
|
129
|
+
|
130
|
+
title = token.line.get_rest_trimmed(keyword.length)
|
131
|
+
set_token_matched(token, :StepLine, title, keyword)
|
132
|
+
return true
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def change_dialect(dialect_name, location)
|
138
|
+
dialect = Dialect.for(dialect_name)
|
139
|
+
raise NoSuchLanguageException.new(dialect_name, location) if dialect.nil?
|
140
|
+
|
141
|
+
@dialect_name = dialect_name
|
142
|
+
@dialect = dialect
|
143
|
+
end
|
144
|
+
|
145
|
+
def match_title_line(token, token_type, keywords)
|
146
|
+
keyword = keywords.detect { |k| token.line.start_with_title_keyword?(k) }
|
147
|
+
|
148
|
+
return false unless keyword
|
149
|
+
|
150
|
+
title = token.line.get_rest_trimmed(keyword.length + ':'.length)
|
151
|
+
set_token_matched(token, token_type, title, keyword)
|
152
|
+
true
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_token_matched(token, matched_type, text=nil, keyword=nil, indent=nil, items=[])
|
156
|
+
token.matched_type = matched_type
|
157
|
+
token.matched_text = text && text.chomp
|
158
|
+
token.matched_keyword = keyword
|
159
|
+
token.matched_indent = indent || (token.line && token.line.indent) || 0
|
160
|
+
token.matched_items = items
|
161
|
+
token.location[:column] = token.matched_indent + 1
|
162
|
+
token.matched_gherkin_dialect = @dialect_name
|
163
|
+
end
|
164
|
+
|
165
|
+
def unescape_docstring(text)
|
166
|
+
@active_doc_string_separator ? text.gsub("\\\"\\\"\\\"", "\"\"\"") : text
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|