Skip to content

Commit be57972

Browse files
committed
Support for raw block helpers
See handlebars-lang/handlebars.js#730
1 parent 40452f9 commit be57972

File tree

13 files changed

+437
-224
lines changed

13 files changed

+437
-224
lines changed

handlebars/gen/com/dmarcotte/handlebars/parsing/_HbLexer.java

Lines changed: 255 additions & 215 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

handlebars/src/com/dmarcotte/handlebars/parsing/HbParsing.java

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ private void parseStatements(PsiBuilder builder) {
113113

114114
/**
115115
* statement
116-
* : openInverse program closeBlock
116+
* : openRawBlock CONTENT endRawBlock
117+
* | openInverse program closeBlock
117118
* | openBlock program closeBlock
118119
* | mustache
119120
* | partial
@@ -126,6 +127,18 @@ private void parseStatements(PsiBuilder builder) {
126127
private boolean parseStatement(PsiBuilder builder) {
127128
IElementType tokenType = builder.getTokenType();
128129

130+
if (tokenType == OPEN_RAW_BLOCK) {
131+
PsiBuilder.Marker blockMarker = builder.mark();
132+
if (parseOpenRawBlock(builder)) {
133+
parseRestOfBlock(builder, blockMarker, true);
134+
}
135+
else {
136+
return false;
137+
}
138+
139+
return true;
140+
}
141+
129142
if (atOpenInverseExpression(builder)) {
130143
PsiBuilder.Marker inverseBlockStartMarker = builder.mark();
131144
PsiBuilder.Marker lookAheadMarker = builder.mark();
@@ -144,7 +157,7 @@ private boolean parseStatement(PsiBuilder builder) {
144157

145158
PsiBuilder.Marker blockMarker = builder.mark();
146159
if (parseOpenInverse(builder)) {
147-
parseRestOfBlock(builder, blockMarker);
160+
parseRestOfBlock(builder, blockMarker, false);
148161
}
149162
else {
150163
return false;
@@ -156,7 +169,7 @@ private boolean parseStatement(PsiBuilder builder) {
156169
if (tokenType == OPEN_BLOCK) {
157170
PsiBuilder.Marker blockMarker = builder.mark();
158171
if (parseOpenBlock(builder)) {
159-
parseRestOfBlock(builder, blockMarker);
172+
parseRestOfBlock(builder, blockMarker, false);
160173
}
161174
else {
162175
return false;
@@ -206,12 +219,35 @@ private boolean parseStatement(PsiBuilder builder) {
206219
* <p/>
207220
* NOTE: will resolve the given blockMarker
208221
*/
209-
private void parseRestOfBlock(PsiBuilder builder, PsiBuilder.Marker blockMarker) {
222+
private void parseRestOfBlock(PsiBuilder builder, PsiBuilder.Marker blockMarker, boolean raw) {
210223
parseProgram(builder);
211-
parseCloseBlock(builder);
224+
if (raw) {
225+
parseCloseRawBlock(builder);
226+
} else {
227+
parseCloseBlock(builder);
228+
}
212229
blockMarker.done(HbTokenTypes.BLOCK_WRAPPER);
213230
}
214231

232+
/**
233+
* openRawBlock
234+
* : OPEN_RAW_BLOCK sexpr CLOSE_RAW_BLOCK
235+
*/
236+
private boolean parseOpenRawBlock(PsiBuilder builder) {
237+
PsiBuilder.Marker openRawBlockStacheMarker = builder.mark();
238+
if (!parseLeafToken(builder, OPEN_RAW_BLOCK)) {
239+
openRawBlockStacheMarker.drop();
240+
return false;
241+
}
242+
243+
if (parseSexpr(builder)) {
244+
parseLeafTokenGreedy(builder, CLOSE_RAW_BLOCK);
245+
}
246+
247+
openRawBlockStacheMarker.done(OPEN_BLOCK_STACHE);
248+
return true;
249+
}
250+
215251
/**
216252
* openBlock
217253
* : OPEN_BLOCK sexpr CLOSE { $$ = new yy.MustacheNode($2[0], $2[1]); }
@@ -263,6 +299,27 @@ private boolean parseOpenInverse(PsiBuilder builder) {
263299
return true;
264300
}
265301

302+
/**
303+
* closeRawBlock
304+
* : END_RAW_BLOCK path CLOSE_RAW_BLOCK
305+
* ;
306+
*/
307+
private boolean parseCloseRawBlock(PsiBuilder builder) {
308+
PsiBuilder.Marker closeRawBlockMarker = builder.mark();
309+
310+
if (!parseLeafToken(builder, END_RAW_BLOCK)) {
311+
closeRawBlockMarker.drop();
312+
return false;
313+
}
314+
315+
PsiBuilder.Marker mustacheNameMark = builder.mark();
316+
parsePath(builder);
317+
mustacheNameMark.done(HbTokenTypes.MUSTACHE_NAME);
318+
parseLeafTokenGreedy(builder, CLOSE_RAW_BLOCK);
319+
closeRawBlockMarker.done(CLOSE_BLOCK_STACHE);
320+
return true;
321+
}
322+
266323
/**
267324
* closeBlock
268325
* : OPEN_ENDBLOCK path CLOSE { $$ = $2; }

handlebars/src/com/dmarcotte/handlebars/parsing/HbTokenTypes.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ private HbTokenTypes() {
4545
public static final IElementType OPEN_UNESCAPED = new HbElementType("OPEN_UNESCAPED", "hb.parsing.element.expected.open_unescaped");
4646
public static final IElementType OPEN_SEXPR = new HbElementType("OPEN_SEXPR", "hb.parsing.element.expected.open_sexpr");
4747
public static final IElementType CLOSE_SEXPR = new HbElementType("CLOSE_SEXPR", "hb.parsing.element.expected.close_sexpr");
48+
public static final IElementType OPEN_RAW_BLOCK = new HbElementType("OPEN_RAW_BLOCK", "hb.parsing.element.expected.open_raw_block");
49+
public static final IElementType END_RAW_BLOCK = new HbElementType("END_RAW_BLOCK", "hb.parsing.element.expected.end_raw_block");
50+
public static final IElementType CLOSE_RAW_BLOCK = new HbElementType("CLOSE_RAW_BLOCK", "hb.parsing.element.expected.close_raw_block");
4851
public static final IElementType EQUALS = new HbElementType("EQUALS", "hb.parsing.element.expected.equals");
4952
public static final IElementType ID = new HbElementType("ID", "hb.parsing.element.expected.id");
5053
public static final IElementType DATA_PREFIX = new HbElementType("DATA_PREFIX", "hb.parsing.element.expected.data");

handlebars/src/com/dmarcotte/handlebars/parsing/handlebars.flex

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// We base our lexer directly on the official handlebars.l lexer definition,
22
// making some modifications to account for Jison/JFlex syntax and functionality differences
33
//
4-
// Revision ported: https://github.com/wycats/handlebars.js/commit/58a0b4f17d5338793c92cf4d104e9c44cc485c5b#src/handlebars.l
4+
// Revision ported: https://github.com/wycats/handlebars.js/blob/14b7ef9066d107dc83deedc8e6791947811cc764/src/handlebars.l
55

66
package com.dmarcotte.handlebars.parsing;
77

@@ -46,6 +46,7 @@ WhiteSpace = {LineTerminator} | [ \t\f]
4646
%state comment_block
4747
%state comment_end
4848
%state data
49+
%state raw
4950

5051
%%
5152

@@ -105,10 +106,36 @@ WhiteSpace = {LineTerminator} | [ \t\f]
105106
}
106107
}
107108

109+
<raw> {
110+
~"{{{{/" {
111+
// backtrack over the END_RAW_BLOCK we picked up at the end of this string
112+
yypushback(5);
113+
114+
yypopState();
115+
116+
// we stray from the handlebars.js lexer here since we need our WHITE_SPACE more clearly delineated
117+
// and we need to avoid creating extra tokens for empty strings (makes the parser and formatter happier)
118+
if (!yytext().toString().equals("")) {
119+
if (yytext().toString().trim().length() == 0) {
120+
return HbTokenTypes.WHITE_SPACE;
121+
} else {
122+
return HbTokenTypes.CONTENT;
123+
}
124+
}
125+
}
126+
127+
// Check for anything that is not a string containing "{{{{/"; that's CONTENT
128+
!([^]*"{{{{/"[^]*) { return HbTokenTypes.CONTENT; }
129+
}
130+
108131
<mu> {
109132
"(" { return HbTokenTypes.OPEN_SEXPR; }
110133
")" { return HbTokenTypes.CLOSE_SEXPR; }
111134

135+
"{{{{" { return HbTokenTypes.OPEN_RAW_BLOCK; }
136+
"{{{{/" { return HbTokenTypes.END_RAW_BLOCK; }
137+
"}}}}" { yypopState(); yypushState(raw); return HbTokenTypes.CLOSE_RAW_BLOCK; }
138+
112139
"{{"\~?">" { return HbTokenTypes.OPEN_PARTIAL; }
113140
"{{"\~?"#" { return HbTokenTypes.OPEN_BLOCK; }
114141
"{{"\~?"/" { return HbTokenTypes.OPEN_ENDBLOCK; }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- "Change block start 'bar' to 'foo'" "true" -->
2+
{{{{foo}}}}
3+
4+
{{{{/foo}}}}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- "Change block start 'bar' to 'foo'" "true" -->
2+
{{{{<caret>bar}}}}
3+
4+
{{{{/foo}}}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{{{raw}}}} {{test}} {{{{/raw}}}}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
HbFile:RawBlock.hbs
2+
HbStatementsImpl(STATEMENTS)
3+
HbBlockWrapperImpl(BLOCK_WRAPPER)
4+
HbOpenBlockMustacheImpl(OPEN_BLOCK_STACHE)
5+
HbPsiElementImpl([Hb] OPEN_RAW_BLOCK)
6+
PsiElement([Hb] OPEN_RAW_BLOCK)('{{{{')
7+
HbMustacheNameImpl(MUSTACHE_NAME)
8+
HbPathImpl(PATH)
9+
HbPsiElementImpl([Hb] ID)
10+
PsiElement([Hb] ID)('raw')
11+
HbPsiElementImpl([Hb] CLOSE_RAW_BLOCK)
12+
PsiElement([Hb] CLOSE_RAW_BLOCK)('}}}}')
13+
HbStatementsImpl(STATEMENTS)
14+
PsiElement([Hb] CONTENT)(' {{test}} ')
15+
HbCloseBlockMustacheImpl(CLOSE_BLOCK_STACHE)
16+
HbPsiElementImpl([Hb] END_RAW_BLOCK)
17+
PsiElement([Hb] END_RAW_BLOCK)('{{{{/')
18+
HbMustacheNameImpl(MUSTACHE_NAME)
19+
HbPathImpl(PATH)
20+
HbPsiElementImpl([Hb] ID)
21+
PsiElement([Hb] ID)('raw')
22+
HbPsiElementImpl([Hb] CLOSE_RAW_BLOCK)
23+
PsiElement([Hb] CLOSE_RAW_BLOCK)('}}}}')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
HbFile:RawBlockParameters.hbs
2+
HbStatementsImpl(STATEMENTS)
3+
HbBlockWrapperImpl(BLOCK_WRAPPER)
4+
HbOpenBlockMustacheImpl(OPEN_BLOCK_STACHE)
5+
HbPsiElementImpl([Hb] OPEN_RAW_BLOCK)
6+
PsiElement([Hb] OPEN_RAW_BLOCK)('{{{{')
7+
HbMustacheNameImpl(MUSTACHE_NAME)
8+
HbPathImpl(PATH)
9+
HbPsiElementImpl([Hb] ID)
10+
PsiElement([Hb] ID)('raw')
11+
PsiWhiteSpace(' ')
12+
HbParamImpl(PARAM)
13+
HbPsiElementImpl([Hb] NUMBER)
14+
PsiElement([Hb] NUMBER)('1')
15+
PsiWhiteSpace(' ')
16+
HbParamImpl(PARAM)
17+
HbPsiElementImpl([Hb] NUMBER)
18+
PsiElement([Hb] NUMBER)('2')
19+
PsiWhiteSpace(' ')
20+
HbParamImpl(PARAM)
21+
HbPsiElementImpl([Hb] NUMBER)
22+
PsiElement([Hb] NUMBER)('3')
23+
HbPsiElementImpl([Hb] CLOSE_RAW_BLOCK)
24+
PsiElement([Hb] CLOSE_RAW_BLOCK)('}}}}')
25+
HbStatementsImpl(STATEMENTS)
26+
PsiElement([Hb] CONTENT)(' {{test}} ')
27+
HbCloseBlockMustacheImpl(CLOSE_BLOCK_STACHE)
28+
HbPsiElementImpl([Hb] END_RAW_BLOCK)
29+
PsiElement([Hb] END_RAW_BLOCK)('{{{{/')
30+
HbMustacheNameImpl(MUSTACHE_NAME)
31+
HbPathImpl(PATH)
32+
HbPsiElementImpl([Hb] ID)
33+
PsiElement([Hb] ID)('raw')
34+
HbPsiElementImpl([Hb] CLOSE_RAW_BLOCK)
35+
PsiElement([Hb] CLOSE_RAW_BLOCK)('}}}}')

handlebars/test/src/com/dmarcotte/handlebars/inspections/HbBlockMismatchFixTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public void testWrongOpenBlock2() {
3333
doTest("Change block start");
3434
}
3535

36+
public void testWrongOpenRawBlock() {
37+
doTest("Change block start");
38+
}
39+
3640
private void doTest(String intentionHint) {
3741
myFixture.configureByFile("inspections/before" + getTestName(false) + ".hbs");
3842
final IntentionAction intention = myFixture.findSingleIntention(intentionHint);

handlebars/test/src/com/dmarcotte/handlebars/parsing/HbLexerFreeFormTest.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ public void testRegularMustacheFollowedByUnescaped() {
9898
}
9999

100100
public void testTooManyMustaches() {
101-
TokenizerResult result = tokenize("{{{{");
102-
result.shouldMatchTokenTypes(OPEN_UNESCAPED, INVALID);
103-
result.shouldMatchTokenContent("{{{", "{");
101+
TokenizerResult result = tokenize("{{{{{");
102+
result.shouldMatchTokenTypes(OPEN_RAW_BLOCK, INVALID);
103+
result.shouldMatchTokenContent("{{{{", "{");
104104
}
105105

106106
public void testTooManyCommentCloseStaches() {
@@ -276,4 +276,10 @@ public void testDataParamsForPartials() {
276276
result.shouldMatchTokenTypes(OPEN_PARTIAL, ID, WHITE_SPACE, DATA_PREFIX, ID, SEP, ID, CLOSE);
277277
result.shouldMatchTokenContent("{{>", "foo", " ", "@", "bar", ".", "baz", "}}");
278278
}
279+
280+
public void testRawBlock() {
281+
TokenizerResult result = tokenize("{{{{raw}}}} {{test}} {{{{/raw}}}}");
282+
result.shouldMatchTokenTypes(OPEN_RAW_BLOCK, ID, CLOSE_RAW_BLOCK, CONTENT, END_RAW_BLOCK, ID, CLOSE_RAW_BLOCK);
283+
result.shouldMatchTokenContent("{{{{", "raw", "}}}}", " {{test}} ", "{{{{/", "raw", "}}}}");
284+
}
279285
}

handlebars/test/src/com/dmarcotte/handlebars/parsing/HbParserFreeFormTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,12 @@ public void testParamWithDecimal() {
8383
public void testSubexpressions() {
8484
doTest(true);
8585
}
86+
87+
public void testRawBlock() {
88+
doTest(true);
89+
}
90+
91+
public void testRawBlockParameters() {
92+
doTest(true);
93+
}
8694
}

0 commit comments

Comments
 (0)