1
1
using Gherkin . Ast ;
2
+ using System . Collections ;
2
3
3
4
namespace Gherkin ;
4
5
@@ -97,69 +98,131 @@ public string GetRestTrimmed(int length)
97
98
return lineText . Substring ( trimmedStartIndex + length ) . Trim ( ) ;
98
99
}
99
100
100
- /// <summary>
101
- /// Tries parsing the line as a tag list, and returns the tags wihtout the leading '@' characters.
102
- /// </summary>
103
- /// <returns>(position,text) pairs, position is 0-based index</returns>
104
- public IEnumerable < GherkinLineSpan > GetTags ( )
101
+ public readonly struct TagsEnumerable : IEnumerable < GherkinLineSpan >
105
102
{
106
- string uncommentedLine = lineText ;
107
- var commentIndex = lineText . IndexOf ( GherkinLanguageConstants . COMMENT_PREFIX [ 0 ] , trimmedStartIndex ) ;
108
- while ( commentIndex >= 0 )
103
+ readonly int lineNumber ;
104
+ readonly string uncommentedLine ;
105
+ readonly int position ;
106
+ public TagsEnumerable ( int lineNumber , string lineText , int trimmedStartIndex )
109
107
{
110
- if ( commentIndex == 0 )
111
- yield break ;
112
- if ( Array . IndexOf ( inlineWhitespaceChars , lineText [ commentIndex - 1 ] ) != - 1 )
108
+ this . lineNumber = lineNumber ;
109
+ uncommentedLine = lineText ;
110
+ var commentIndex = lineText . IndexOf ( GherkinLanguageConstants . COMMENT_PREFIX [ 0 ] , trimmedStartIndex ) ;
111
+ while ( commentIndex >= 0 )
113
112
{
114
- uncommentedLine = uncommentedLine . Substring ( 0 , commentIndex ) ;
115
- break ;
113
+ if ( commentIndex == 0 )
114
+ {
115
+ position = - 1 ;
116
+ return ;
117
+ }
118
+ if ( Array . IndexOf ( inlineWhitespaceChars , lineText [ commentIndex - 1 ] ) != - 1 )
119
+ {
120
+ uncommentedLine = uncommentedLine . Substring ( 0 , commentIndex ) ;
121
+ break ;
122
+ }
123
+ commentIndex = lineText . IndexOf ( GherkinLanguageConstants . COMMENT_PREFIX [ 0 ] , commentIndex + 1 ) ;
116
124
}
117
- commentIndex = lineText . IndexOf ( GherkinLanguageConstants . COMMENT_PREFIX [ 0 ] , commentIndex + 1 ) ;
125
+ position = uncommentedLine . IndexOf ( GherkinLanguageConstants . TAG_PREFIX [ 0 ] , trimmedStartIndex ) ;
118
126
}
119
- int position = uncommentedLine . IndexOf ( GherkinLanguageConstants . TAG_PREFIX [ 0 ] , trimmedStartIndex ) ;
120
- while ( position >= 0 )
121
- {
122
- int nextPos = uncommentedLine . IndexOf ( GherkinLanguageConstants . TAG_PREFIX [ 0 ] , position + 1 ) ;
123
- int endPos ;
124
- if ( nextPos > 0 )
125
- endPos = nextPos - 1 ;
126
- else
127
- endPos = uncommentedLine . Length - 1 ;
128
-
129
- while ( endPos > position && Array . IndexOf ( inlineWhitespaceChars , lineText [ endPos ] ) != - 1 ) // TrimEnd
130
- endPos -= 1 ;
131
-
132
- int length = endPos - position + 1 ;
133
- if ( length <= 1 )
127
+
128
+ public TagsEnumerator GetEnumerator ( ) => new TagsEnumerator ( lineNumber , uncommentedLine , position ) ;
129
+
130
+ IEnumerator < GherkinLineSpan > IEnumerable < GherkinLineSpan > . GetEnumerator ( ) => GetEnumerator ( ) ;
131
+ IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
132
+ }
133
+
134
+ public struct TagsEnumerator : IEnumerator < GherkinLineSpan >
135
+ {
136
+ readonly int lineNumber ;
137
+ readonly string uncommentedLine ;
138
+ int position ;
139
+
140
+ public TagsEnumerator ( int lineNumber , string uncommentedLine , int position ) : this ( )
141
+ {
142
+ this . lineNumber = lineNumber ;
143
+ this . uncommentedLine = uncommentedLine ;
144
+ this . position = position ;
145
+ }
146
+
147
+ public GherkinLineSpan Current { readonly get ; private set ; }
148
+ readonly object IEnumerator . Current => Current ;
149
+
150
+ public bool MoveNext ( )
151
+ {
152
+ while ( position >= 0 )
134
153
{
135
- position = nextPos ;
136
- continue ;
137
- }
154
+ int nextPos = uncommentedLine . IndexOf ( GherkinLanguageConstants . TAG_PREFIX [ 0 ] , position + 1 ) ;
155
+ int endPos ;
156
+ if ( nextPos > 0 )
157
+ endPos = nextPos - 1 ;
158
+ else
159
+ endPos = uncommentedLine . Length - 1 ;
160
+
161
+ while ( endPos > position && Array . IndexOf ( inlineWhitespaceChars , uncommentedLine [ endPos ] ) != - 1 ) // TrimEnd
162
+ endPos -= 1 ;
163
+
164
+ int length = endPos - position + 1 ;
165
+ if ( length <= 1 )
166
+ {
167
+ position = nextPos ;
168
+ continue ;
169
+ }
138
170
139
- var tagName = lineText . Substring ( position , length ) ;
171
+ var tagName = uncommentedLine . Substring ( position , length ) ;
140
172
141
- if ( tagName . IndexOfAny ( inlineWhitespaceChars ) >= 0 )
142
- throw new InvalidTagException ( "A tag may not contain whitespace" , new Location ( LineNumber , position + 1 ) ) ;
173
+ if ( tagName . IndexOfAny ( inlineWhitespaceChars ) >= 0 )
174
+ throw new InvalidTagException ( "A tag may not contain whitespace" , new Location ( lineNumber , position + 1 ) ) ;
143
175
144
- yield return new GherkinLineSpan ( position + 1 , tagName ) ;
176
+ Current = new GherkinLineSpan ( position + 1 , tagName ) ;
177
+ position = nextPos ;
178
+ return true ;
179
+ }
145
180
146
- position = nextPos ;
181
+ Current = default ;
182
+ return false ;
183
+ }
184
+
185
+ readonly void IDisposable . Dispose ( )
186
+ {
187
+ // nothing to do
147
188
}
189
+
190
+ void IEnumerator . Reset ( ) => throw new NotImplementedException ( ) ;
148
191
}
149
192
150
193
/// <summary>
151
- /// Tries parsing the line as table row and returns the trimmed cell values .
194
+ /// Tries parsing the line as a tag list, and returns the tags wihtout the leading '@' characters .
152
195
/// </summary>
153
196
/// <returns>(position,text) pairs, position is 0-based index</returns>
154
- public IEnumerable < GherkinLineSpan > GetTableCells ( )
197
+ public TagsEnumerable GetTags ( ) => new TagsEnumerable ( LineNumber , lineText , trimmedStartIndex ) ;
198
+
199
+ public readonly struct TableCellsEnumerable ( string lineText , int startPos ) : IEnumerable < GherkinLineSpan >
155
200
{
156
- bool isFirstRow = true ;
201
+ public TableCellsEnumerator GetEnumerator ( ) => new TableCellsEnumerator ( lineText , startPos ) ;
157
202
158
- string cell = null ;
159
- int startPos = trimmedStartIndex ;
160
- int pos = startPos ;
203
+ IEnumerator < GherkinLineSpan > IEnumerable < GherkinLineSpan > . GetEnumerator ( ) => GetEnumerator ( ) ;
204
+ IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
205
+ }
206
+
207
+ public struct TableCellsEnumerator : IEnumerator < GherkinLineSpan >
208
+ {
209
+ readonly string lineText ;
210
+ int startPos ;
211
+ int pos ;
212
+ bool isFirstRow ;
161
213
162
- static void EnsureCellText ( ref string cell , string lineText , ref int startPos , int pos , bool trim )
214
+ public TableCellsEnumerator ( string lineText , int startPos )
215
+ {
216
+ this . lineText = lineText ;
217
+ this . startPos = startPos ;
218
+ this . pos = startPos ;
219
+ this . isFirstRow = true ;
220
+ }
221
+
222
+ public GherkinLineSpan Current { readonly get ; private set ; }
223
+ readonly object IEnumerator . Current => Current ;
224
+
225
+ void EnsureCellText ( ref string cell , bool trim )
163
226
{
164
227
if ( cell is not null )
165
228
{
@@ -178,53 +241,76 @@ static void EnsureCellText(ref string cell, string lineText, ref int startPos, i
178
241
cell = lineText . Substring ( startPos , trimedPos - startPos + 1 ) ;
179
242
}
180
243
181
- while ( pos < lineText . Length )
244
+ public bool MoveNext ( )
182
245
{
183
- char c = lineText [ pos ] ;
184
- pos ++ ;
185
- if ( c == GherkinLanguageConstants . TABLE_CELL_SEPARATOR_CHAR )
186
- {
187
- if ( isFirstRow )
188
- isFirstRow = false ;
189
- else
190
- {
191
- EnsureCellText ( ref cell , lineText , ref startPos , pos , true ) ;
246
+ string cell = null ;
192
247
193
- yield return new GherkinLineSpan ( startPos + 1 , cell ) ;
194
- }
195
- cell = null ;
196
- startPos = pos ;
197
- }
198
- else if ( c == GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR )
248
+ while ( pos < lineText . Length )
199
249
{
200
- EnsureCellText ( ref cell , lineText , ref startPos , pos , false ) ;
201
- if ( ( pos + 1 ) < lineText . Length )
250
+ char c = lineText [ pos ] ;
251
+ pos ++ ;
252
+ if ( c == GherkinLanguageConstants . TABLE_CELL_SEPARATOR_CHAR )
202
253
{
203
- c = lineText [ pos ] ;
204
- pos ++ ;
205
- if ( c == GherkinLanguageConstants . TABLE_CELL_NEWLINE_ESCAPE )
254
+ if ( isFirstRow )
206
255
{
207
- cell += "\n " ;
256
+ isFirstRow = false ;
257
+ startPos = pos ;
208
258
}
209
259
else
210
260
{
211
- if ( c != GherkinLanguageConstants . TABLE_CELL_SEPARATOR_CHAR && c != GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR )
261
+ EnsureCellText ( ref cell , true ) ;
262
+
263
+ Current = new GherkinLineSpan ( startPos + 1 , cell ) ;
264
+ startPos = pos ;
265
+ return true ;
266
+ }
267
+ }
268
+ else if ( c == GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR )
269
+ {
270
+ EnsureCellText ( ref cell , false ) ;
271
+ if ( ( pos + 1 ) < lineText . Length )
272
+ {
273
+ c = lineText [ pos ] ;
274
+ pos ++ ;
275
+ if ( c == GherkinLanguageConstants . TABLE_CELL_NEWLINE_ESCAPE )
212
276
{
213
- cell += GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR ;
277
+ cell += " \n " ;
214
278
}
215
- cell += c ;
279
+ else
280
+ {
281
+ if ( c != GherkinLanguageConstants . TABLE_CELL_SEPARATOR_CHAR && c != GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR )
282
+ {
283
+ cell += GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR ;
284
+ }
285
+ cell += c ;
286
+ }
287
+ }
288
+ else
289
+ {
290
+ cell += GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR ;
216
291
}
217
292
}
218
293
else
219
294
{
220
- cell += GherkinLanguageConstants . TABLE_CELL_ESCAPE_CHAR ;
295
+ if ( cell is not null )
296
+ cell += c ;
221
297
}
222
298
}
223
- else
224
- {
225
- if ( cell is not null )
226
- cell += c ;
227
- }
299
+
300
+ return false ;
228
301
}
302
+
303
+ readonly void IDisposable . Dispose ( )
304
+ {
305
+ // nothing to do
306
+ }
307
+
308
+ void IEnumerator . Reset ( ) => throw new NotImplementedException ( ) ;
229
309
}
310
+
311
+ /// <summary>
312
+ /// Tries parsing the line as table row and returns the trimmed cell values.
313
+ /// </summary>
314
+ /// <returns>(position,text) pairs, position is 0-based index</returns>
315
+ public TableCellsEnumerable GetTableCells ( ) => new TableCellsEnumerable ( lineText , trimmedStartIndex ) ;
230
316
}
0 commit comments