diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 3bc6e1fb97..78c508f469 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -80,14 +80,15 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { } p.check(.lpar) spos := p.tok.position() - s := if is_html { '' } else { p.tok.lit } + literal_string_param := if is_html { '' } else { p.tok.lit } + path_of_literal_string_param := literal_string_param.replace('/', os.path_separator) if !is_html { p.check(.string) } p.check(.rpar) // $embed_file('/path/to/file') if is_embed_file { - mut epath := s + mut epath := path_of_literal_string_param // Validate that the epath exists, and that it is actually a file. if epath == '' { p.error_with_pos('supply a valid relative or absolute file path to the file to embed', @@ -119,7 +120,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { scope: 0 is_embed: true embed_file: ast.EmbeddedFile{ - rpath: s + rpath: literal_string_param apath: epath } } @@ -127,10 +128,12 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { // Compile vweb html template to V code, parse that V code and embed the resulting V function // that returns an html string. fn_path := p.cur_fn_name.split('_') - tmpl_path := if is_html { '${fn_path.last()}.html' } else { s } + fn_path_joined := fn_path.join(os.path_separator) + compiled_vfile_path := os.real_path(p.scanner.file_path.replace('/', os.path_separator)) + tmpl_path := if is_html { '${fn_path.last()}.html' } else { path_of_literal_string_param } // Looking next to the vweb program - dir := os.dir(p.scanner.file_path.replace('/', os.path_separator)) - mut path := os.join_path(dir, fn_path.join(os.path_separator)) + dir := os.dir(compiled_vfile_path) + mut path := os.join_path(dir, fn_path_joined) path += '.html' path = os.real_path(path) if !is_html { @@ -139,7 +142,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { if !os.exists(path) { // can be in `templates/` if is_html { - path = os.join_path(dir, 'templates', fn_path.join('/')) + path = os.join_path(dir, 'templates', fn_path_joined) path += '.html' } if !os.exists(path) { @@ -152,10 +155,10 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { } // println('path is now "$path"') } - if p.pref.is_verbose { - println('>>> compiling comptime template file "$path"') - } tmp_fn_name := p.cur_fn_name.replace('.', '__') + $if trace_comptime ? { + println('>>> compiling comptime template file "$path" for $tmp_fn_name') + } v_code := tmpl.compile_file(path, tmp_fn_name) $if print_vweb_template_expansions ? { lines := v_code.split('\n') @@ -167,12 +170,12 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { start_pos: 0 parent: p.global_scope } - if p.pref.is_verbose { - println('\n\n') + $if trace_comptime ? { + println('') println('>>> vweb template for $path:') println(v_code) println('>>> end of vweb template END') - println('\n\n') + println('') } mut file := parse_comptime(v_code, p.table, p.pref, scope, p.global_scope) file = ast.File{ @@ -207,7 +210,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall { is_vweb: true vweb_tmpl: file method_name: n - args_var: s + args_var: literal_string_param } } diff --git a/vlib/v/tests/tmpl/1.txt b/vlib/v/tests/tmpl/base.txt similarity index 79% rename from vlib/v/tests/tmpl/1.txt rename to vlib/v/tests/tmpl/base.txt index aeac0f3891..444b475f49 100644 --- a/vlib/v/tests/tmpl/1.txt +++ b/vlib/v/tests/tmpl/base.txt @@ -1,9 +1,9 @@ name: @name - age: @age - numbers: @numbers @for number in numbers @number @end + +@include 'inner.txt' \ No newline at end of file diff --git a/vlib/v/tests/tmpl/inner.txt b/vlib/v/tests/tmpl/inner.txt new file mode 100644 index 0000000000..4555ca1f12 --- /dev/null +++ b/vlib/v/tests/tmpl/inner.txt @@ -0,0 +1,17 @@ +@for i in 0..10 +${i*2} - $i +@end + +@for key, val in downloads +${key}, downloaded $val times. +@end + +@if !ignored +this is ignored +@else +this is not ignored +@end + +@if true +so, it's basically true +@end \ No newline at end of file diff --git a/vlib/v/tests/tmpl_test.v b/vlib/v/tests/tmpl_test.v index 3734715b3d..169e0790af 100644 --- a/vlib/v/tests/tmpl_test.v +++ b/vlib/v/tests/tmpl_test.v @@ -2,40 +2,84 @@ fn one() string { name := 'Peter' age := 25 numbers := [1, 2, 3] - return $tmpl('tmpl/1.txt') + downloads := map{ + 'vlang/ui': '3201' + 'vlang/vtl': '123' + } + ignored := true + return $tmpl('tmpl/base.txt') } fn test_tmpl() { - assert one().trim_space() == 'name: Peter - + assert one().trim_space() == "name: Peter age: 25 - numbers: [1, 2, 3] 1 - 2 +3 -3' + +0 - 0 +2 - 1 +4 - 2 +6 - 3 +8 - 4 +10 - 5 +12 - 6 +14 - 7 +16 - 8 +18 - 9 + + +vlang/ui, downloaded 3201 times. +vlang/vtl, downloaded 123 times. + + +this is not ignored + + +so, it's basically true" } fn test_tmpl_in_anon_fn() { - anon := fn (name string, age int, numbers []int) string { - return $tmpl('tmpl/1.txt') + anon := fn (name string, age int, numbers []int, downloads map[string]string, ignored bool) string { + + return $tmpl('tmpl/base.txt') } - println(anon('Peter', 25, [1, 2, 3])) - assert anon('Peter', 25, [1, 2, 3]).trim_space() == 'name: Peter - + assert anon('Peter', 25, [1, 2, 3], map{ + 'vlang/ui': '3201' + 'vlang/vtl': '123' + }, true).trim_space() == "name: Peter age: 25 - numbers: [1, 2, 3] 1 - 2 +3 -3' + +0 - 0 +2 - 1 +4 - 2 +6 - 3 +8 - 4 +10 - 5 +12 - 6 +14 - 7 +16 - 8 +18 - 9 + + +vlang/ui, downloaded 3201 times. +vlang/vtl, downloaded 123 times. + + +this is not ignored + + +so, it's basically true" } diff --git a/vlib/vweb/tmpl/tmpl.v b/vlib/vweb/tmpl/tmpl.v index 853ef9c3f6..85dd14e080 100644 --- a/vlib/vweb/tmpl/tmpl.v +++ b/vlib/vweb/tmpl/tmpl.v @@ -30,16 +30,16 @@ pub fn compile_template(basepath string, html_ string, fn_name string) string { mut html := html_.trim_space() mut header := '' mut footer := '' - if os.exists('templates/header.html') && html.contains('@header') { - h := os.read_file('templates/header.html') or { - panic('reading file templates/header.html failed') + if os.exists(os.join_path(basepath, 'header.html')) && html.contains('@header') { + h := os.read_file(os.join_path(basepath, 'header.html')) or { + panic('reading file ${os.join_path(basepath, 'header.html')} failed') } header = h.trim_space().replace("'", '"') html = header + html } - if os.exists('templates/footer.html') && html.contains('@footer') { - f := os.read_file('templates/footer.html') or { - panic('reading file templates/footer.html failed') + if os.exists(os.join_path(basepath, 'footer.html')) && html.contains('@footer') { + f := os.read_file(os.join_path(basepath, 'footer.html')) or { + panic('reading file ${os.join_path(basepath, 'footer.html')} failed') } footer = f.trim_space().replace("'", '"') html += footer @@ -82,22 +82,18 @@ _ = footer file_ext = '.html' } file_name = file_name.replace(file_ext, '') - mut templates_folder := os.join_path(basepath, 'templates') - if file_name.contains('/') { - if file_name.starts_with('/') { - // absolute path - templates_folder = '' - } else { - // relative path, starting with the current folder - templates_folder = os.real_path(basepath) - } + // relative path, starting with the current folder + mut templates_folder := os.real_path(basepath) + if file_name.contains('/') && file_name.starts_with('/') { + // an absolute path + templates_folder = '' } file_path := os.real_path(os.join_path(templates_folder, '$file_name$file_ext')) $if trace_tmpl ? { eprintln('>>> basepath: "$basepath" , fn_name: "$fn_name" , @include line: "$line" , file_name: "$file_name" , file_ext: "$file_ext" , templates_folder: "$templates_folder" , file_path: "$file_path"') } file_content := os.read_file(file_path) or { - panic('Vweb: Reading file $file_name failed.') + panic('Vweb: reading file $file_name from path: $file_path failed.') } file_splitted := file_content.split_into_lines().reverse() for f in file_splitted { @@ -120,10 +116,16 @@ _ = footer s.writeln('if ' + line[pos + 4..] + '{') s.writeln(tmpl.str_start) } else if line.contains('@end') { + // Remove new line byte + s.go_back(1) + s.writeln(tmpl.str_end) s.writeln('}') s.writeln(tmpl.str_start) } else if line.contains('@else') { + // Remove new line byte + s.go_back(1) + s.writeln(tmpl.str_end) s.writeln(' } else { ') s.writeln(tmpl.str_start) @@ -155,7 +157,7 @@ _ = footer } else { // HTML, may include `@var` // escaped by cgen, unless it's a `vweb.RawHtml` string - s.writeln(line.replace('@', '$').replace("'", '"')) + s.writeln(line.replace('@', '$').replace('$$', '@').replace("'", "\\'")) } } s.writeln(tmpl.str_end)