diff --git a/source/Handlebars.Test/IssueTests.cs b/source/Handlebars.Test/IssueTests.cs index f857d65b..f19f33b9 100644 --- a/source/Handlebars.Test/IssueTests.cs +++ b/source/Handlebars.Test/IssueTests.cs @@ -734,6 +734,48 @@ public void UnrecognisedExpressionThrowsOutOfMemoryException() Assert.Throws(()=> Handlebars.Compile(source)); } + // Issue: https://github.com/Handlebars-Net/Handlebars.Net/issues/462 + // Compile replaces \\ (double backslash) with \ (single backslash) + [Fact] + public void Issue462_DoubleBackslashPreservedInOutput() + { + var handlebars = Handlebars.Create(); + // Template contains two backslashes as literal text + var compiledTemplate = handlebars.Compile(@"\\"); + var result = compiledTemplate(null); + Assert.Equal(@"\\", result); + } + + [Fact] + public void Issue462_SingleBackslashPreservedInOutput() + { + var handlebars = Handlebars.Create(); + var compiledTemplate = handlebars.Compile(@"\"); + var result = compiledTemplate(null); + Assert.Equal(@"\", result); + } + + [Fact] + public void Issue462_DoubleBackslashBeforeExpressionStillCollapses() + { + // \\{{name}} should still produce a single literal backslash followed by the evaluated expression + var handlebars = Handlebars.Create(); + var compiledTemplate = handlebars.Compile(@"\\{{name}}"); + var result = compiledTemplate(new { name = "World" }); + Assert.Equal(@"\World", result); + } + + [Fact] + public void Issue462_DoubleBackslashInMixedTemplate() + { + // Template with backslashes mixed: \\to preserves both backslashes (not before {{), + // but \\{{name}} collapses to single backslash + evaluated expression (spec behavior) + var handlebars = Handlebars.Create(); + var compiledTemplate = handlebars.Compile(@"path\\to\\{{name}}"); + var result = compiledTemplate(new { name = "file" }); + Assert.Equal(@"path\\to\file", result); + } + // Issue: https://github.com/Handlebars-Net/Handlebars.Net/issues/539 // Parent context (../) resolves to wrong value inside a custom block helper used within #each [Fact] diff --git a/source/Handlebars/Compiler/Lexer/Tokenizer.cs b/source/Handlebars/Compiler/Lexer/Tokenizer.cs index d0d051c7..1f03b8b0 100644 --- a/source/Handlebars/Compiler/Lexer/Tokenizer.cs +++ b/source/Handlebars/Compiler/Lexer/Tokenizer.cs @@ -122,8 +122,18 @@ private static IEnumerable Parse(ExtendedStringReader source) { if ((char)node == '\\' && (char)source.Peek() == '\\') { - source.Read(); - buffer.Append('\\'); + source.Read(); // consume second '\' + if ((char)source.Peek() == '{') + { + // \\{{ → single literal backslash followed by evaluated expression + buffer.Append('\\'); + } + else + { + // \\ not followed by {{ → preserve both backslashes verbatim + buffer.Append('\\'); + buffer.Append('\\'); + } node = source.Read(); } else if ((char)node == '\\' && (char)source.Peek() == '{')