diff --git a/src/Microsoft.TemplateEngine.Core/Operations/ConditionalType.cs b/src/Microsoft.TemplateEngine.Core/Operations/ConditionalType.cs index 738a47c648..a282d2ebaf 100644 --- a/src/Microsoft.TemplateEngine.Core/Operations/ConditionalType.cs +++ b/src/Microsoft.TemplateEngine.Core/Operations/ConditionalType.cs @@ -6,8 +6,9 @@ public enum ConditionalType None, Xml, Razor, - CWithComments, CNoComments, - CBlockComments + CLineComments, + CBlockComments, + HashSignLineComment } } diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Config/ConditionalConfig.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Config/ConditionalConfig.cs index 3bdde1a274..12282ecd75 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Config/ConditionalConfig.cs +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Config/ConditionalConfig.cs @@ -60,14 +60,17 @@ public static IReadOnlyList ConditionalSetup(ConditionalType case ConditionalType.Razor: setup = RazorConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); break; - case ConditionalType.CWithComments: - setup = CStyleWithCommentsConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); + case ConditionalType.CLineComments: + setup = CStyleLineCommentsConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); break; case ConditionalType.CNoComments: setup = CStyleNoCommentsConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); break; case ConditionalType.CBlockComments: - setup = CBlockCommentConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); + setup = CStyleBlockCommentConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); + break; + case ConditionalType.HashSignLineComment: + setup = HashSignLineCommentConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id); break; default: throw new Exception($"Unrecognized conditional type {style}"); @@ -138,7 +141,7 @@ public static List RazorConditionalSetup(string evaluatorTyp }; } - public static List CBlockCommentConditionalSetup(string evaluatorType, bool wholeLine, bool trimWhiteSpace, string id) + public static List CStyleBlockCommentConditionalSetup(string evaluatorType, bool wholeLine, bool trimWhiteSpace, string id) { // This is the operationId (flag) for the balanced nesting string commentFixingOperationId = "Fix pseudo comments (C Block)"; @@ -167,7 +170,7 @@ public static List CBlockCommentConditionalSetup(string eval }; } - public static List CStyleWithCommentsConditionalSetup(string evaluatorType, bool wholeLine, bool trimWhiteSpace, string id) + public static List CStyleLineCommentsConditionalSetup(string evaluatorType, bool wholeLine, bool trimWhiteSpace, string id) { string replaceOperationId = "Replacement (C style): (//) -> ()"; string uncommentOperationId = "Uncomment (C style): (////) -> (//)"; @@ -215,5 +218,37 @@ public static List CStyleNoCommentsConditionalSetup(string e conditional }; } + + // TODO: test + // this should work for nginx.conf, Perl, bash, etc. + public static List HashSignLineCommentConditionalSetup(string evaluatorType, bool wholeLine, bool trimWhiteSpace, string id) + { + string uncommentOperationId = "Uncomment (hash line): (##) -> (#)"; + string replaceOperationId = "Replacement (hash line): (#) -> ()"; + IOperationProvider uncomment = new Replacement("##", "#", uncommentOperationId); + IOperationProvider commentReplace = new Replacement("#", "", replaceOperationId); + + ConditionalTokens tokens = new ConditionalTokens + { + IfTokens = new[] { "#if" }, + ElseTokens = new[] { "#else" }, + ElseIfTokens = new[] { "#elseif" }, + EndIfTokens = new[] { "#endif" }, + ActionableIfTokens = new[] { "##if" }, + ActionableElseIfTokens = new[] { "##elseif" }, + ActionableElseTokens = new[] { "##else" }, + ActionableOperations = new[] { replaceOperationId, uncommentOperationId } + }; + + ConditionEvaluator evaluator = EvaluatorSelector.Select(evaluatorType); + IOperationProvider conditional = new Conditional(tokens, wholeLine, trimWhiteSpace, evaluator, id); + + return new List() + { + conditional, + uncomment, + commentReplace + }; + } } } \ No newline at end of file diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/SimpleConfigModel.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/SimpleConfigModel.cs index e9e4f29604..493d2da223 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/SimpleConfigModel.cs +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/SimpleConfigModel.cs @@ -197,7 +197,7 @@ IReadOnlyList IRunnableProjectConfig.Sources private readonly Dictionary _guidToGuidPrefixMap = new Dictionary(); - private static readonly SpecialOperationConfigParams DefaultOperationParams = new SpecialOperationConfigParams(string.Empty, "//", ConditionalType.CWithComments); + private static readonly SpecialOperationConfigParams DefaultOperationParams = new SpecialOperationConfigParams(string.Empty, "//", ConditionalType.CLineComments); // operation info read from the config private ICustomFileGlobModel CustomOperations = new CustomFileGlobModel(); @@ -239,7 +239,7 @@ IReadOnlyList> IRunnableProjectConfig.Spe if (_specialOperationConfig == null) { List defaultSpecials = new List(); - defaultSpecials.Add(new SpecialOperationConfigParams("**/*.json", "//", ConditionalType.CWithComments)); + defaultSpecials.Add(new SpecialOperationConfigParams("**/*.json", "//", ConditionalType.CLineComments)); defaultSpecials.Add(new SpecialOperationConfigParams("**/*.css.min", "/*", ConditionalType.CBlockComments)); defaultSpecials.Add(new SpecialOperationConfigParams("**/*.css", "/*", ConditionalType.CBlockComments)); defaultSpecials.Add(new SpecialOperationConfigParams("**/*.cs", "//", ConditionalType.CNoComments)); diff --git a/test/Microsoft.TemplateEngine.Core.UnitTests/ConditionalTests.cs b/test/Microsoft.TemplateEngine.Core.UnitTests/ConditionalTests.cs index 89af5326af..3fa1dda6fb 100644 --- a/test/Microsoft.TemplateEngine.Core.UnitTests/ConditionalTests.cs +++ b/test/Microsoft.TemplateEngine.Core.UnitTests/ConditionalTests.cs @@ -43,6 +43,12 @@ private IProcessor SetupCStyleNoCommentsProcessor(VariableCollection vc) return SetupTestProcessor(operations, vc); } + private IProcessor SetupHashSignLineCommentsProcessor(VariableCollection vc) + { + IOperationProvider[] operations = HashSignLineCommentConditionalOperations; + return SetupTestProcessor(operations, vc); + } + /// /// Sets up a processor with the input params. /// @@ -209,8 +215,307 @@ private IOperationProvider[] CStyleNoCommentsConditionalOperations } } + private IOperationProvider[] HashSignLineCommentConditionalOperations + { + get + { + string uncommentOperationId = "Uncomment (hash line): (##) -> (#)"; + string replaceOperationId = "Replacement (hash line): (#) -> ()"; + + ConditionalTokens tokens = new ConditionalTokens + { + IfTokens = new[] { "#if" }, + ElseTokens = new[] { "#else" }, + ElseIfTokens = new[] { "#elseif" }, + EndIfTokens = new[] { "#endif", "##endif" }, + ActionableIfTokens = new[] { "##if" }, + ActionableElseIfTokens = new[] { "##elseif" }, + ActionableElseTokens = new[] { "##else" }, + ActionableOperations = new[] { replaceOperationId, uncommentOperationId } + }; + + IOperationProvider[] operations = + { + new Conditional(tokens, true, true, CppStyleEvaluatorDefinition.CppStyleEvaluator, null), + new Replacement("##", "#", uncommentOperationId), + new Replacement("#", "", replaceOperationId), + }; + + return operations; + } + } + #endregion initialization & support + #region HashSign Line Comments + + [Fact] + public void VerifyBasicHashCommentHAndling() + { + string originalValue = @"Start +##if (CLAUSE) +## Actual Comment +# content +##endif +# end comment +## end quad comment +End"; + string expectedValue = @"Start +# Actual Comment + content +# end comment +## end quad comment +End"; + VariableCollection vc = new VariableCollection + { + ["CLAUSE"] = true, + }; + + IProcessor processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, expectedValue, processor, 9999); + + string originalValueEndifChanged = @"Start +##if (CLAUSE) +## Actual Comment +# content +#endif +# end comment +## end quad comment +End"; + RunAndVerify(originalValueEndifChanged, expectedValue, processor, 9999); + + string originalNoCommentRemoval = @"Start +#if (CLAUSE) +## Actual Comment +# content +##endif +# end comment +## end quad comment +End"; + string expectedValueNoCommentRemoval = @"Start +## Actual Comment +# content +# end comment +## end quad comment +End"; + RunAndVerify(originalNoCommentRemoval, expectedValueNoCommentRemoval, processor, 9999); + } + + [Fact] + public void VerifyHashStyleCommentRemovalForEachClauseNoEmbedding() + { + string originalValue = @"Start +##if (IF) +# content: if +## Comment: if +# content: if part 2 +## Comment: if part 2 +##elseif (ELSEIF) +## Comment: elseif +# content: elseif +## Comment: elseif part 2 +# content: elseif part 2 +##else +# content: else +## Comment: else +## Comment: else 2 +# content: else 2 +##endif +# end comment +## end quad comment +End"; + string ifExpectedValue = @"Start + content: if +# Comment: if + content: if part 2 +# Comment: if part 2 +# end comment +## end quad comment +End"; + VariableCollection vc = new VariableCollection + { + ["IF"] = true, + }; + IProcessor processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, ifExpectedValue, processor, 9999); + + string elseIfExpectedValue = @"Start +# Comment: elseif + content: elseif +# Comment: elseif part 2 + content: elseif part 2 +# end comment +## end quad comment +End"; + vc = new VariableCollection + { + ["IF"] = false, + ["ELSEIF"] = true + }; + processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, elseIfExpectedValue, processor, 9999); + + string elseExpectedValue = @"Start + content: else +# Comment: else +# Comment: else 2 + content: else 2 +# end comment +## end quad comment +End"; + vc = new VariableCollection + { + ["IF"] = false, + ["ELSEIF"] = false + }; + processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, elseExpectedValue, processor, 9999); + } + + [Fact] + public void VerifyHashStyleCommentRemovalWithNestedClause() + { + string originalValue = @"Start +##if (OUTER_IF) + ## Comment: outer if + #content outer if + ##if (INNER_IF) + ## Comment: inner if + #content: inner if + ##endif +##endif +# end comment +## end quad comment +End"; + string outerTrueInnerFalseExpectedValue = @"Start + # Comment: outer if + content outer if +# end comment +## end quad comment +End"; + VariableCollection vc = new VariableCollection + { + ["OUTER_IF"] = true, + ["INNER_IF"] = false + }; + IProcessor processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, outerTrueInnerFalseExpectedValue, processor, 9999); + + string outerTrueInnerTrueExpectedValue = @"Start + # Comment: outer if + content outer if + # Comment: inner if + content: inner if +# end comment +## end quad comment +End"; + vc = new VariableCollection + { + ["OUTER_IF"] = true, + ["INNER_IF"] = true + }; + processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, outerTrueInnerTrueExpectedValue, processor, 9999); + } + + [Fact] + public void VerifyHashStyleCommentRemovalNestedDoesntRemove() + { + string originalValue = @"Start +##if (OUTER_IF) + ## Comment: outer if + #content outer if + ##if (INNER_IF) + ## Comment: inner if + #content: inner if + ##endif +##endif +# end comment +## end quad comment +End"; + string outerTrueInnerFalseExpectedValue = @"Start + # Comment: outer if + content outer if +# end comment +## end quad comment +End"; + VariableCollection vc = new VariableCollection + { + ["OUTER_IF"] = true, + ["INNER_IF"] = false + }; + IProcessor processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, outerTrueInnerFalseExpectedValue, processor, 9999); + + // TODO: determine if this is correct, or if the inner should //#if overrides the outer ////#if + string outerTrueInnerTrueExpectedValue = @"Start + # Comment: outer if + content outer if + # Comment: inner if + content: inner if +# end comment +## end quad comment +End"; + vc = new VariableCollection + { + ["OUTER_IF"] = true, + ["INNER_IF"] = true + }; + processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, outerTrueInnerTrueExpectedValue, processor, 9999); + } + + [Fact] + public void VerifyHashSignMixedConditionalsThreeLevelEmbedding() + { + string originalValue = @"Lead content +##if (LEVEL_1_IF) +# content: level-1 if +# ##if (LEVEL_2_IF) +# # content: level-2 if +# # ##if (LEVEL_3_IF) +# # # content: level-3 if +# # ##elseif (LEVEL_3_ELSEIF) +# # # content: level-3 elseif +# # ##else +# # # content: level-3 else +# # ##endif +# ##elseif (LEVEL_2_ELSEIF) +# # content: level-2 elseif +# ##else +# # content: level-2 else +# ##endif +##elseif true +# content: level-1 elseif +##else +# content: level-1 else +##endif +# commented trailing content +moar trailing content"; + + // outer if & inner if get uncommented + string expectedValue = @"Lead content + content: level-1 if + content: level-2 if + content: level-3 if +# commented trailing content +moar trailing content"; + + VariableCollection vc = new VariableCollection + { + ["LEVEL_1_IF"] = true, + ["LEVEL_2_IF"] = true, + ["LEVEL_3_IF"] = true, + ["LEVEL_3_ELSEIF"] = true, // irrelevant + ["LEVEL_2_ELSEIF"] = true, // irrelevant + }; + + IProcessor processor = SetupHashSignLineCommentsProcessor(vc); + RunAndVerify(originalValue, expectedValue, processor, 9999); + } + + #endregion HashSign Line Comments + #region XmlBlockComments // The emitted value is not valid Xml in this test because the unbalanced comment in the first if