tmpl.v: fix of is_html_open_tag function and allow usage of V template sign '@' in JS code (#13067)
parent
d3317cbd4f
commit
a73e1462f0
|
@ -15,25 +15,30 @@ enum State {
|
||||||
// span // span.{
|
// span // span.{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tmpl_str_end = "')\n"
|
||||||
|
|
||||||
// check HTML open tag `<name attr="x" >`
|
// check HTML open tag `<name attr="x" >`
|
||||||
fn is_html_open_tag(name string, s string) bool {
|
fn is_html_open_tag(name string, s string) bool {
|
||||||
mut len := s.len
|
trimmed_line := s.trim_space()
|
||||||
|
mut len := trimmed_line.len
|
||||||
|
|
||||||
if len < name.len {
|
if len < name.len {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
mut sub := s[0..1]
|
|
||||||
|
mut sub := trimmed_line[0..1]
|
||||||
if sub != '<' { // not start with '<'
|
if sub != '<' { // not start with '<'
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
sub = s[len - 1..len]
|
sub = trimmed_line[len - 1..len]
|
||||||
if sub != '>' { // not end with '<'
|
if sub != '>' { // not end with '<'
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
sub = s[len - 2..len - 1]
|
sub = trimmed_line[len - 2..len - 1]
|
||||||
if sub == '/' { // self-closing
|
if sub == '/' { // self-closing
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
sub = s[1..len - 1]
|
sub = trimmed_line[1..len - 1]
|
||||||
if sub.contains_any('<>') { // `<name <bad> >`
|
if sub.contains_any('<>') { // `<name <bad> >`
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -51,6 +56,21 @@ fn is_html_open_tag(name string, s string) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn insert_template_code(fn_name string, tmpl_str_start string, line string) string {
|
||||||
|
// HTML, may include `@var`
|
||||||
|
// escaped by cgen, unless it's a `vweb.RawHtml` string
|
||||||
|
trailing_bs := parser.tmpl_str_end + 'sb_${fn_name}.write_b(92)\n' + tmpl_str_start
|
||||||
|
round1 := ['\\', '\\\\', r"'", "\\'", r'@', r'$']
|
||||||
|
round2 := [r'$$', r'\@', r'.$', r'.@']
|
||||||
|
mut rline := line.replace_each(round1).replace_each(round2)
|
||||||
|
|
||||||
|
if rline.ends_with('\\') {
|
||||||
|
rline = rline[0..rline.len - 2] + trailing_bs
|
||||||
|
}
|
||||||
|
|
||||||
|
return rline
|
||||||
|
}
|
||||||
|
|
||||||
// compile_file compiles the content of a file by the given path as a template
|
// compile_file compiles the content of a file by the given path as a template
|
||||||
pub fn (mut p Parser) compile_template_file(template_file string, fn_name string) string {
|
pub fn (mut p Parser) compile_template_file(template_file string, fn_name string) string {
|
||||||
mut lines := os.read_lines(template_file) or {
|
mut lines := os.read_lines(template_file) or {
|
||||||
|
@ -60,7 +80,6 @@ pub fn (mut p Parser) compile_template_file(template_file string, fn_name string
|
||||||
basepath := os.dir(template_file)
|
basepath := os.dir(template_file)
|
||||||
lstartlength := lines.len * 30
|
lstartlength := lines.len * 30
|
||||||
tmpl_str_start := "sb_${fn_name}.write_string('"
|
tmpl_str_start := "sb_${fn_name}.write_string('"
|
||||||
tmpl_str_end := "')\n"
|
|
||||||
mut source := strings.new_builder(1000)
|
mut source := strings.new_builder(1000)
|
||||||
source.writeln('
|
source.writeln('
|
||||||
import strings
|
import strings
|
||||||
|
@ -170,24 +189,24 @@ fn vweb_tmpl_${fn_name}() string {
|
||||||
source.write_string(line[pos + 6..line.len - 1])
|
source.write_string(line[pos + 6..line.len - 1])
|
||||||
source.writeln('" rel="stylesheet" type="text/css">')
|
source.writeln('" rel="stylesheet" type="text/css">')
|
||||||
} else if line.contains('@if ') {
|
} else if line.contains('@if ') {
|
||||||
source.writeln(tmpl_str_end)
|
source.writeln(parser.tmpl_str_end)
|
||||||
pos := line.index('@if') or { continue }
|
pos := line.index('@if') or { continue }
|
||||||
source.writeln('if ' + line[pos + 4..] + '{')
|
source.writeln('if ' + line[pos + 4..] + '{')
|
||||||
source.writeln(tmpl_str_start)
|
source.writeln(tmpl_str_start)
|
||||||
} else if line.contains('@end') {
|
} else if line.contains('@end') {
|
||||||
// Remove new line byte
|
// Remove new line byte
|
||||||
source.go_back(1)
|
source.go_back(1)
|
||||||
source.writeln(tmpl_str_end)
|
source.writeln(parser.tmpl_str_end)
|
||||||
source.writeln('}')
|
source.writeln('}')
|
||||||
source.writeln(tmpl_str_start)
|
source.writeln(tmpl_str_start)
|
||||||
} else if line.contains('@else') {
|
} else if line.contains('@else') {
|
||||||
// Remove new line byte
|
// Remove new line byte
|
||||||
source.go_back(1)
|
source.go_back(1)
|
||||||
source.writeln(tmpl_str_end)
|
source.writeln(parser.tmpl_str_end)
|
||||||
source.writeln(' } else { ')
|
source.writeln(' } else { ')
|
||||||
source.writeln(tmpl_str_start)
|
source.writeln(tmpl_str_start)
|
||||||
} else if line.contains('@for') {
|
} else if line.contains('@for') {
|
||||||
source.writeln(tmpl_str_end)
|
source.writeln(parser.tmpl_str_end)
|
||||||
pos := line.index('@for') or { continue }
|
pos := line.index('@for') or { continue }
|
||||||
source.writeln('for ' + line[pos + 4..] + '{')
|
source.writeln('for ' + line[pos + 4..] + '{')
|
||||||
source.writeln(tmpl_str_start)
|
source.writeln(tmpl_str_start)
|
||||||
|
@ -216,28 +235,22 @@ fn vweb_tmpl_${fn_name}() string {
|
||||||
source.writeln('</div>')
|
source.writeln('</div>')
|
||||||
}
|
}
|
||||||
} else if state == .js {
|
} else if state == .js {
|
||||||
// replace `$` to `\$` at first to escape JavaScript template literal syntax
|
if line.contains('//V_TEMPLATE') {
|
||||||
source.writeln(line.replace(r'$', r'\$').replace(r'$$', r'@').replace(r'.$',
|
source.writeln(insert_template_code(fn_name, tmpl_str_start, line))
|
||||||
r'.@').replace(r"'", r"\'"))
|
} else {
|
||||||
|
// replace `$` to `\$` at first to escape JavaScript template literal syntax
|
||||||
|
source.writeln(line.replace(r'$', r'\$').replace(r'$$', r'@').replace(r'.$',
|
||||||
|
r'.@').replace(r"'", r"\'"))
|
||||||
|
}
|
||||||
} else if state == .css {
|
} else if state == .css {
|
||||||
// disable template variable declaration in inline stylesheet
|
// disable template variable declaration in inline stylesheet
|
||||||
// because of some CSS rules prefixed with `@`.
|
// because of some CSS rules prefixed with `@`.
|
||||||
source.writeln(line.replace(r'.$', r'.@').replace(r"'", r"\'"))
|
source.writeln(line.replace(r'.$', r'.@').replace(r"'", r"\'"))
|
||||||
} else {
|
} else {
|
||||||
// HTML, may include `@var`
|
source.writeln(insert_template_code(fn_name, tmpl_str_start, line))
|
||||||
// escaped by cgen, unless it's a `vweb.RawHtml` string
|
|
||||||
trailing_bs := tmpl_str_end + 'sb_${fn_name}.write_b(92)\n' + tmpl_str_start
|
|
||||||
round1 := ['\\', '\\\\', r"'", "\\'", r'@', r'$']
|
|
||||||
round2 := [r'$$', r'\@', r'.$', r'.@']
|
|
||||||
rline := line.replace_each(round1).replace_each(round2)
|
|
||||||
if rline.ends_with('\\') {
|
|
||||||
source.writeln(rline[0..rline.len - 2] + trailing_bs)
|
|
||||||
} else {
|
|
||||||
source.writeln(rline)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source.writeln(tmpl_str_end)
|
source.writeln(parser.tmpl_str_end)
|
||||||
source.writeln('_tmpl_res_$fn_name := sb_${fn_name}.str() ')
|
source.writeln('_tmpl_res_$fn_name := sb_${fn_name}.str() ')
|
||||||
source.writeln('return _tmpl_res_$fn_name')
|
source.writeln('return _tmpl_res_$fn_name')
|
||||||
source.writeln('}')
|
source.writeln('}')
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// V won't parse that
|
||||||
|
<script>
|
||||||
|
var non_interpolated_labels = @benchmark_plot_data.dates;
|
||||||
|
var non_interpolated_values = @benchmark_plot_data.numerical_result;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
// V will parse that
|
||||||
|
<script>
|
||||||
|
var real_labels = @benchmark_plot_data.dates; //V_TEMPLATE
|
||||||
|
var real_values = @benchmark_plot_data.numerical_result; //V_TEMPLATE
|
||||||
|
</script>
|
|
@ -0,0 +1,16 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
struct PlotData {
|
||||||
|
dates []string
|
||||||
|
numerical_result []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_template_interpolation_can_be_selectively_turned_on_in_script_tags() {
|
||||||
|
benchmark_plot_data := PlotData{['2012-11-30', '2022-12-29'], [5, 6, 7, 1]}
|
||||||
|
text := $tmpl('tmpl/selective_interpolation_in_script_tag.html')
|
||||||
|
// dump(text)
|
||||||
|
assert text.contains('var non_interpolated_labels = @benchmark_plot_data.dates;')
|
||||||
|
assert text.contains('var non_interpolated_values = @benchmark_plot_data.numerical_result;')
|
||||||
|
assert text.contains("var real_labels = ['2012-11-30', '2022-12-29']; //V_TEMPLATE")
|
||||||
|
assert text.contains('var real_values = [5, 6, 7, 1]; //V_TEMPLATE')
|
||||||
|
}
|
Loading…
Reference in New Issue