From 1cf14bd596ef760154b24c6b2faf802034ef200e Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 21 Mar 2018 12:33:44 -0700 Subject: [PATCH 01/12] Added Markdown rendering cmdlets ConvertFrom-Markdown is used for converting a markdown document or string to a MarkdownInfo object. It can optionally return a HTML or VT100 encoded string in addition to a AST of the markdown document. Show-Markdown is used to either display the VT100 encoded string on console or redirect the HTML string to the browser. Set/Get-MarkdownOption cmdlets get be used to view or set markdown rendering options. --- PowerShell.sln | 120 ++++---- ThirdPartyNotices.txt | 133 +++------ ...crosoft.PowerShell.Commands.Utility.csproj | 1 + .../utility/ConvertFromMarkdownCommand.cs | 210 ++++++++++++++ .../utility/MarkdownOptionCommands.cs | 245 +++++++++++++++++ .../commands/utility/ShowMarkdownCommand.cs | 143 ++++++++++ .../resources/ConvertMarkdownStrings.resx | 132 +++++++++ .../CodeInlineRenderer.cs | 22 ++ .../EmphasisInlineRenderer.cs | 22 ++ .../FencedCodeBlockRenderer.cs | 40 +++ .../HeaderBlockRenderer.cs | 54 ++++ .../LeafInlineRenderer.cs | 32 +++ .../LineBreakRenderer.cs | 32 +++ .../LinkInlineRenderer.cs | 30 ++ .../ListBlockRenderer.cs | 56 ++++ .../ListItemBlockRenderer.cs | 66 +++++ .../MarkdownConverter.cs | 89 ++++++ ...Microsoft.PowerShell.MarkdownRender.csproj | 30 ++ .../ParagraphBlockRenderer.cs | 25 ++ .../QuoteBlockRenderer.cs | 29 ++ .../VT100EscapeSequences.cs | 257 ++++++++++++++++++ .../VT100ObjectRenderer.cs | 18 ++ .../VT100Renderer.cs | 43 +++ .../Microsoft.PowerShell.Utility.psd1 | 2 +- .../Microsoft.PowerShell.Utility.psd1 | 2 +- .../engine/TypesV3_Ps1Xml.cs | 25 ++ 26 files changed, 1694 insertions(+), 164 deletions(-) create mode 100644 src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs create mode 100644 src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx create mode 100644 src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj create mode 100644 src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs create mode 100644 src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs diff --git a/PowerShell.sln b/PowerShell.sln index 86e297792f..21d9e1ca74 100644 --- a/PowerShell.sln +++ b/PowerShell.sln @@ -1,7 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -# https://github.com/dotnet/project-system/blob/master/docs/opening-with-new-project-system.md#project-type-guids VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "powershell-win-core", "src\powershell-win-core\powershell-win-core.csproj", "{8359D422-E0C4-4A0D-94EB-3C9DD16B7932}" @@ -30,15 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WSMan.Runtime", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "powershell-unix", "src\powershell-unix\powershell-unix.csproj", "{73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}" EndProject -# Configuration mapping comment -# All global configurations must be mapped to project configurations -# -# 4BC19063-1F66-467B-87DE-80449C72BCD6 - Microsoft.PowerShell.SDK -# 8359D422-E0C4-4A0D-94EB-3C9DD16B7932 - PowerShell-Win -# Linux is invalid and mapped to Release -# -# 73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690 - powershell-unix -# Only Linux is valid, all configurations mapped to Linux +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.MarkdownRender", "src\Microsoft.PowerShell.MarkdownRender\Microsoft.PowerShell.MarkdownRender.csproj", "{43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeCoverage|Any CPU = CodeCoverage|Any CPU @@ -47,52 +39,52 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Debug|Any CPU.Build.0 = Debug|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Linux|Any CPU.ActiveCfg = Release|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Linux|Any CPU.Build.0 = Release|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Release|Any CPU.ActiveCfg = Release|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Release|Any CPU.Build.0 = Release|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.Build.0 = Linux|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.Build.0 = Release|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Release|Any CPU.Build.0 = Release|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.Build.0 = Linux|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.Build.0 = Release|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Release|Any CPU.Build.0 = Release|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Debug|Any CPU.Build.0 = Debug|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.Build.0 = Linux|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.Build.0 = Release|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Release|Any CPU.ActiveCfg = Release|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Release|Any CPU.Build.0 = Release|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.Build.0 = Linux|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.Build.0 = Release|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Release|Any CPU.Build.0 = Release|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.Build.0 = Linux|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.Build.0 = Release|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Release|Any CPU.Build.0 = Release|Any CPU {4BC19063-1F66-467B-87DE-80449C72BCD6}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU @@ -111,54 +103,62 @@ Global {131A8527-92D7-468F-822D-5354229A865C}.Linux|Any CPU.Build.0 = Release|Any CPU {131A8527-92D7-468F-822D-5354229A865C}.Release|Any CPU.ActiveCfg = Release|Any CPU {131A8527-92D7-468F-822D-5354229A865C}.Release|Any CPU.Build.0 = Release|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Debug|Any CPU.Build.0 = Debug|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.Build.0 = Linux|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.Build.0 = Release|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Release|Any CPU.ActiveCfg = Release|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Release|Any CPU.Build.0 = Release|Any CPU - {07BFD271-8992-4F34-9091-6CFC3E224A24}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {07BFD271-8992-4F34-9091-6CFC3E224A24}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {07BFD271-8992-4F34-9091-6CFC3E224A24}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {07BFD271-8992-4F34-9091-6CFC3E224A24}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {07BFD271-8992-4F34-9091-6CFC3E224A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07BFD271-8992-4F34-9091-6CFC3E224A24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07BFD271-8992-4F34-9091-6CFC3E224A24}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {07BFD271-8992-4F34-9091-6CFC3E224A24}.Linux|Any CPU.Build.0 = Linux|Any CPU + {07BFD271-8992-4F34-9091-6CFC3E224A24}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {07BFD271-8992-4F34-9091-6CFC3E224A24}.Linux|Any CPU.Build.0 = Release|Any CPU {07BFD271-8992-4F34-9091-6CFC3E224A24}.Release|Any CPU.ActiveCfg = Release|Any CPU {07BFD271-8992-4F34-9091-6CFC3E224A24}.Release|Any CPU.Build.0 = Release|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.Build.0 = Linux|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.Build.0 = Release|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Release|Any CPU.Build.0 = Release|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.Build.0 = Linux|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.Build.0 = Release|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Release|Any CPU.Build.0 = Release|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.Build.0 = Linux|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.Build.0 = Release|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Release|Any CPU.Build.0 = Release|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.Build.0 = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.ActiveCfg = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.Build.0 = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.ActiveCfg = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.Build.0 = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.ActiveCfg = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.Build.0 = Linux|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.ActiveCfg = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.Build.0 = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.ActiveCfg = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.Build.0 = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.Build.0 = Release|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.ActiveCfg = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.Build.0 = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.ActiveCfg = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.Build.0 = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 76f5226d23..65977f14b6 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -103,108 +103,6 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------- -WebListener -------------------------------------------------- - -Microsoft.AspNetCore.App 2.1.0 -Copyright (c) .NET Foundation and Contributors -All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -------------------------------------------------- -XUnit tests -------------------------------------------------- - -Xunit.SkippableFact v bnm -Copyright (c) Andrew Arnott - -This software is licensed under the Microsoft Public License -http://opensource.org/licenses/ms-pl - -This license governs use of the accompanying software. If you use the software, you -accept this license. If you do not accept the license, do not use the software. - -1. Definitions - The terms "reproduce," "reproduction," "derivative works," and "distribution" have the - same meaning here as under U.S. copyright law. - A "contribution" is the original software, or any additions or changes to the software. - A "contributor" is any person that distributes its contribution under this license. - "Licensed patents" are a contributor's patent claims that read directly on its contribution. - -2. Grant of Rights - (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. - (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. - -3. Conditions and Limitations - (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. - (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. - (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. - (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. - (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. - ------------------------------------------------------- -Libmi.so ------------------------------------------------------- -Copyright (c) Microsoft Corporation. All rights reserved. -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The following components are governed by the MIT license, a copy of which appears -below the list of components: ------------------------------------------------------- -Newtonsoft.Json ------------------------------------------------------- -Copyright (c) 2007 James Newton-King -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------- -Libuv v.1.9.0 ---------------------------------------------------------- - -https://raw.githubusercontent.com/aspnet/libuv-package/dev/content/License.txt - -This software is licensed to you by Microsoft Corporation under the original terms of -the copyright holder provided below: - -========================================= libuv is part of the Node project: http://nodejs.org/ libuv may be distributed alone under Node's license: @@ -899,3 +797,34 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +======= + +------------------------------------------------- +File: markdig +------------------------------------------------- + + https://github.com/lunet-io/markdig + +"Copyright (c) 2016, Alexandre Mutel +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification +, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." diff --git a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj index ea348f89ef..05f0b40d4e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj +++ b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs new file mode 100644 index 0000000000..3371d8500d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -0,0 +1,210 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Management.Automation; +using Microsoft.PowerShell.MarkdownRender; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Converts a markdown string to a MarkdownInfo object. + /// The conversion can be done into a HTML text or VT100 encoding string. + /// + [Cmdlet( + VerbsData.ConvertFrom, "Markdown", + DefaultParameterSetName = PathParamSet, + HelpUri = "TBD" + )] + [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownInfo))] + public class ConvertFromMarkdownCommand : PSCmdlet + { + /// + /// Path to the file to convert from Markdown to MarkdownInfo + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = PathParamSet, Mandatory = true)] + public string[] Path { get; set; } + + /// + /// Path to the file to convert from Markdown to MarkdownInfo + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = LitPathParamSet, Mandatory = true)] + public string[] LiteralPath { get; set; } + + /// + /// InputObject of type System.IO.FileInfo or string with content to convert from Markdown to MarkdownInfo + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = InputObjParamSet, Mandatory = true, ValueFromPipeline = true)] + public PSObject InputObject { get; set; } + + /// + /// The Markdown document should be converted to a VT100 encoded string. + /// + [Parameter()] + public SwitchParameter AsVT100EncodedString { get; set; } + + private const string PathParamSet = "PathParamSet"; + private const string LitPathParamSet = "LiteralParamSet"; + + private const string InputObjParamSet = "InputObjParamSet"; + + /// + /// Override ProcessRecord + /// + protected override void ProcessRecord() + { + var conversionType = MarkdownConversionType.HTML; + + var mdOption = (SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo())) as MarkdownOptionInfo; + + if(mdOption == null) + { + throw new InvalidOperationException(); + } + + if (AsVT100EncodedString) + { + conversionType = MarkdownConversionType.VT100; + } + + switch (ParameterSetName) + { + case InputObjParamSet: + Object baseObj = InputObject.BaseObject; + + var fileInfo = baseObj as FileInfo; + if (fileInfo != null) + { + WriteObject( + MarkdownConverter.Convert( + ReadContentFromFile(fileInfo.FullName).Result, + conversionType, + mdOption + ) + ); + } + else + { + var inpObj = baseObj as string; + if (inpObj != null) + { + WriteObject(MarkdownConverter.Convert(inpObj, conversionType, mdOption)); + } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, inputObj.GetType()); + ErrorRecord errorRecord = new ErrorRecord( + new InvalidDataException(errorMessage), + "ConvertFromMarkdownInvalidInputObject", + ErrorCategory.InvalidData, + InputObject); + + WriteError(errorRecord); + } + } + + break; + + case PathParamSet: + ConvertEachFile(Path, conversionType, isLiteral: false, optionInfo: mdOption); + break; + + case LitPathParamSet: + ConvertEachFile(LiteralPath, conversionType, isLiteral: true, optionInfo: mdOption); + break; + } + } + + private void ConvertEachFile(IEnumerable paths, MarkdownConversionType conversionType, bool isLiteral, MarkdownOptionInfo optionInfo) + { + foreach (var path in paths) + { + var resolvedPaths = ResolvePath(path, isLiteral); + + foreach (var resolvedPath in resolvedPaths) + { + if (File.Exists(resolvedPath)) + { + WriteObject( + MarkdownConverter.Convert( + ReadContentFromFile(resolvedPath).Result, + conversionType, + optionInfo) + ); + } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InputFileNotFound, resolvedPath); + var errorRecord = new ErrorRecord( + new FileNotFoundException(errorMessage), + "ConvertFromMarkdownFileNotFound", + ErrorCategory.ResourceUnavailable, + resolvedPath); + + WriteError(errorRecord); + } + } + } + } + + private async Task ReadContentFromFile(string filePath) + { + if (File.Exists(filePath)) + { + using (StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))) + { + string mdContent = await reader.ReadToEndAsync(); + return mdContent; + } + } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InputFileNotFound, filePath); + var errorRecord = new ErrorRecord( + new FileNotFoundException(errorMessage), + "ConvertFromMarkdownFileNotFound", + ErrorCategory.ResourceUnavailable, + resolvedPath); + + WriteError(errorRecord); + } + } + + private List ResolvePath(string path, bool isLiteral) + { + ProviderInfo provider = null; + PSDriveInfo drive = null; + List resolvedPaths = new List(); + + if (isLiteral) + { + resolvedPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(path, out provider, out drive)); + } + else + { + resolvedPaths.AddRange(Context.SessionState.Path.GetResolvedProviderPathFromPSPath(path, out provider)); + } + + if (!provider.Name.Equals("FileSystem", StringComparison.OrdinalIgnoreCase)) + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.FileSystemPathsOnly, path); + ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(), + "ConvertFromMarkdownOnlySupportsFileSystemPaths", + ErrorCategory.InvalidArgument, + path); + WriteError(errorRecord); + + return null; + } + + return resolvedPaths; + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs new file mode 100644 index 0000000000..eba767db17 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Management.Automation; +using Microsoft.PowerShell.MarkdownRender; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// + [Cmdlet( + VerbsCommon.Set, "MarkdownOption", + DefaultParameterSetName = IndividualSetting, + HelpUri = "TBD" + )] + [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownOptionInfo))] + public class SetMarkdownOptionCommand : PSCmdlet + { + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header1Color { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header2Color { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header3Color { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header4Color { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header5Color { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header6Color { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string CodeBlockForegroundColor { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string CodeBlockBackgroundColor { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string ImageAltTextForegroundColor { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string LinkForegroundColor { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string ItalicsForegroundColor { get; set;} + + /// + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string BoldForegroundColor { get; set;} + + /// + /// + [Parameter()] + public SwitchParameter PassThru { get; set;} + + /// + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = ThemeParamSet, Mandatory = true)] + public string Theme { get; set;} + + /// + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = InputObjectParamSet, Mandatory = true, ValueFromPipeline = true)] + public PSObject InputObject { get; set;} + + private const string IndividualSetting = "IndividualSetting"; + + private const string InputObjectParamSet = "InputObject"; + + private const string ThemeParamSet = "Theme"; + + /// + /// + protected override void EndProcessing() + { + MarkdownOptionInfo mdOptionInfo = null; + + switch(ParameterSetName) + { + case ThemeParamSet: + mdOptionInfo = new MarkdownOptionInfo(); + if(string.Equals(Theme, "Light", StringComparison.OrdinalIgnoreCase)) + { + mdOptionInfo.SetLightTheme(); + } + else if(string.Equals(Theme, "Dark", StringComparison.OrdinalIgnoreCase)) + { + mdOptionInfo.SetDarkTheme(); + } + break; + + case InputObjectParamSet: + Object baseObj = InputObject.BaseObject; + mdOptionInfo = baseObj as MarkdownOptionInfo; + + if(mdOptionInfo == null) + { + throw new ArgumentException(); + } + + break; + + case IndividualSetting: + mdOptionInfo = new MarkdownOptionInfo(); + SetOptions(mdOptionInfo); + break; + } + + var sessionVar = SessionState.PSVariable; + sessionVar.Set("MarkdownOptionInfo", mdOptionInfo); + + if(PassThru.IsPresent) + { + WriteObject(mdOptionInfo); + } + } + + private void SetOptions(MarkdownOptionInfo mdOptionInfo) + { + if (!String.IsNullOrEmpty(Header1Color)) + { + mdOptionInfo.Header1 = Header1Color; + } + + if (!String.IsNullOrEmpty(Header2Color)) + { + mdOptionInfo.Header2 = Header2Color; + } + + if (!String.IsNullOrEmpty(Header3Color)) + { + mdOptionInfo.Header3 = Header3Color; + } + + if (!String.IsNullOrEmpty(Header4Color)) + { + mdOptionInfo.Header4 = Header4Color; + } + + if (!String.IsNullOrEmpty(Header5Color)) + { + mdOptionInfo.Header5 = Header5Color; + } + + if (!String.IsNullOrEmpty(Header6Color)) + { + mdOptionInfo.Header6 = Header6Color; + } + + if (!String.IsNullOrEmpty(CodeBlockBackgroundColor)) + { + mdOptionInfo.Code = CodeBlockBackgroundColor; + } + + if (!String.IsNullOrEmpty(CodeBlockForegroundColor)) + { + mdOptionInfo.Code = CodeBlockForegroundColor; + } + + if (!String.IsNullOrEmpty(ImageAltTextForegroundColor)) + { + mdOptionInfo.Image = ImageAltTextForegroundColor; + } + + if (!String.IsNullOrEmpty(LinkForegroundColor)) + { + mdOptionInfo.Link = LinkForegroundColor; + } + + if (!String.IsNullOrEmpty(ItalicsForegroundColor)) + { + mdOptionInfo.EmphasisItalics = ItalicsForegroundColor; + } + + if (!String.IsNullOrEmpty(BoldForegroundColor)) + { + mdOptionInfo.EmphasisBold = BoldForegroundColor; + } + } + } + + /// + /// + [Cmdlet( + VerbsCommon.Get, "MarkdownOption", + HelpUri = "TBD" + )] + [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownOptionInfo))] + public class GetMarkdownOptionCommand : PSCmdlet + { + /// + /// + protected override void EndProcessing() + { + WriteObject(SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo())); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs new file mode 100644 index 0000000000..8e4f639844 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Diagnostics; +using System.Management.Automation; +using Microsoft.PowerShell.MarkdownRender; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Show the VT100EncodedString or Html property of on console or show. + /// VT100EncodedString will be displayed on console. + /// Html will be displayed in default browser. + /// + [Cmdlet( + VerbsCommon.Show, "Markdown", + HelpUri = "TBD" + )] + [OutputType(typeof(string))] + public class ShowMarkdownCommand : PSCmdlet + { + /// + /// InputObject of type Microsoft.PowerShell.MarkdownRender.MarkdownInfo to display + /// + [ValidateNotNullOrEmpty] + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public PSObject InputObject { get; set; } + + /// + /// Switch to view Html in default browser. + /// + [Parameter()] + public SwitchParameter UseBrowser { get; set; } + + private SteppablePipeline stepPipe; + + /// + /// + protected override void BeginProcessing() + { + if(! this.MyInvocation.BoundParameters.ContainsKey("UseBrowser")) + { + // Since UseBrowser is not bound, we use proxy to Out-Default + stepPipe = ScriptBlock.Create(@"Microsoft.PowerShell.Core\Out-Default @PSBoundParameters").GetSteppablePipeline(this.MyInvocation.CommandOrigin); + stepPipe.Begin(this); + } + } + + /// + /// Override ProcessRecord + /// + protected override void ProcessRecord() + { + Object inpObj = InputObject.BaseObject; + var markdownInfo = inpObj as MarkdownInfo; + if (markdownInfo == null) + { + var errorRecord = new ErrorRecord( + new ArgumentException(), + "InvalidInputObject", + ErrorCategory.InvalidArgument, + InputObject); + + WriteError(errorRecord); + } + else + { + if (UseBrowser) + { + var html = markdownInfo.Html; + + if (!String.IsNullOrEmpty(html)) + { + string tmpFilePath = Path.Combine(Path.GetTempPath(), (Guid.NewGuid().ToString() + ".html")); + using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) + { + writer.Write(html); + } + + ProcessStartInfo startInfo = new ProcessStartInfo(); + +#if UNIX + startInfo.FileName = Platform.IsLinux ? "xdg-open" : /* macOS */ "open"; + startInfo.Arguments = tmpFilePath; +#else + startInfo.FileName = tmpFilePath; + startInfo.UseShellExecute = true; +#endif + + Process.Start(startInfo); + } + else + { + var errorRecord = new ErrorRecord( + new InvalidDataException(), + "HtmlIsNullOrEmpty", + ErrorCategory.InvalidData, + html); + + WriteError(errorRecord); + } + } + else + { + var vt100String = markdownInfo.VT100EncodedString; + + if(!String.IsNullOrEmpty(vt100String)) + { + if(stepPipe != null) + { + stepPipe.Process(vt100String); + } + } + else + { + var errorRecord = new ErrorRecord( + new InvalidDataException(), + "VT100EncodedStringIsNullOrEmpty", + ErrorCategory.InvalidData, + vt100String); + + WriteError(errorRecord); + } + } + } + } + + /// + /// + protected override void EndProcessing() + { + if(stepPipe != null) + { + stepPipe.End(); + } + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx new file mode 100644 index 0000000000..9bf09f3c12 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The type of the input object '{0}' is invalid. + + + The given file path '{0}' is not found. + + + The given path '{0}' is not supported. This command only supports the FileSystem Provider paths. + + + The given path '{0}' is not supported. This command only supports the FileSystem Provider paths. + + diff --git a/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs new file mode 100644 index 0000000000..35bf5e4c7d --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax.Inlines; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for inline code elements. + /// + internal class CodeInlineRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, CodeInline obj) + { + renderer.Write(renderer.EscapeSequences.FormatCode(obj.Content , isInline: true)); + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs new file mode 100644 index 0000000000..a09b58b63a --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax.Inlines; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for bold and italics elements. + /// + internal class EmphasisInlineRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, EmphasisInline obj) + { + renderer.Write(renderer.EscapeSequences.FormatEmphasis(obj.FirstChild.ToString() , isBold: obj.IsDouble ? true : false )); + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs new file mode 100644 index 0000000000..f7f4493b45 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for code blocks with language type. + /// + internal class FencedCodeBlockRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, FencedCodeBlock obj) + { + foreach (var codeLine in obj.Lines.Lines) + { + if (!String.IsNullOrWhiteSpace(codeLine.ToString())) + { + // If the code block is of type YAML, then tab to right to improve readability. + // This specifically helps for parameters help content. + if (String.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) + { + renderer.WriteLine("\t" + codeLine.ToString()); + } + else + { + renderer.WriteLine(renderer.EscapeSequences.FormatCode(codeLine.ToString(), isInline: false)); + } + } + } + + // Add a blank line after the code block for better readability. + renderer.WriteLine(); + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs new file mode 100644 index 0000000000..29bfb76eb7 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for headings. + /// + internal class HeaderBlockRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, HeadingBlock obj) + { + // Format header and then add blank line to improve readability. + switch(obj.Level) + { + case 1: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader1(obj.Inline.FirstChild.ToString())); + renderer.WriteLine(); + break; + + case 2: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader2(obj.Inline.FirstChild.ToString())); + renderer.WriteLine(); + break; + + case 3: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader3(obj.Inline.FirstChild.ToString())); + renderer.WriteLine(); + break; + + case 4: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader4(obj.Inline.FirstChild.ToString())); + renderer.WriteLine(); + break; + + case 5: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader5(obj.Inline.FirstChild.ToString())); + renderer.WriteLine(); + break; + + case 6: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader6(obj.Inline.FirstChild.ToString())); + renderer.WriteLine(); + break; + } + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs new file mode 100644 index 0000000000..ff2a202bed --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax.Inlines; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for leaf elements like plain text in paragraphs. + /// + internal class LeafInlineRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, LeafInline obj) + { + // If the next sibling is null, then this is the last line in the paragraph. + // Add new line character at the end. + // Else just write without newline at the end. + if(obj.NextSibling == null) + { + renderer.WriteLine(obj.ToString()); + } + else + { + renderer.Write(obj.ToString()); + } + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs new file mode 100644 index 0000000000..2f5a630306 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax.Inlines; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + + /// + /// Renderer for adding VT100 escape sequences for line breaks. + /// + internal class LineBreakRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, LineBreakInline obj) + { + // If it is a hard line break add new line at the end. + // Else, add a space for after the last character to improve readability. + if(obj.IsHard) + { + renderer.WriteLine(); + } + else + { + renderer.Write(" "); + } + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs new file mode 100644 index 0000000000..2028b7488d --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax.Inlines; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for links. + /// + internal class LinkInlineRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, LinkInline obj) + { + // Format link as image or link. + if(obj.IsImage) + { + renderer.Write(renderer.EscapeSequences.FormatImage(obj.FirstChild.ToString())); + } + else + { + renderer.Write(renderer.EscapeSequences.FormatLink(obj.FirstChild.ToString(), obj.Url)); + } + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs new file mode 100644 index 0000000000..3747bebe1a --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for list blocks. + /// + internal class ListBlockRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, ListBlock obj) + { + // start index of a numbered block. + int index = 1; + + foreach (var item in obj) + { + var listItem = item as ListItemBlock; + + if (listItem != null) + { + if (obj.IsOrdered) + { + RenderNumberedList(renderer, listItem, index++); + } + else + { + renderer.Write(listItem); + } + } + } + + renderer.WriteLine(); + } + + private void RenderNumberedList(VT100Renderer renderer, ListItemBlock block, int index) + { + // For a numbered list, we need to make sure the index is incremented. + foreach (var line in block) + { + var paragraphBlock = line as ParagraphBlock; + + if(paragraphBlock != null) + { + renderer.Write(index.ToString()).Write(". ").Write(paragraphBlock.Inline); + } + } + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs new file mode 100644 index 0000000000..276d773757 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for items in a list block. + /// + internal class ListItemBlockRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, ListItemBlock obj) + { + var parent = obj.Parent as ListBlock; + + if (parent != null) + { + if (!parent.IsOrdered) + { + foreach (var line in obj) + { + RenderWithIndent(renderer, line, parent.BulletType, 0); + } + } + } + } + + private void RenderWithIndent(VT100Renderer renderer, MarkdownObject block, char listBullet, int indentLevel) + { + // Indent left by 2 for each level on list. + string indent = "".PadLeft(indentLevel * 2); + + var paragraphBlock = block as ParagraphBlock; + + if(paragraphBlock != null) + { + renderer.Write(indent).Write(listBullet).Write(" ").Write(paragraphBlock.Inline); + } + else //If there is a sublist, the block is a ListBlock instead of ParagraphBlock. + { + var subList = block as ListBlock; + if (subList != null) + { + foreach(var subListItem in subList) + { + var subListItemBlock = subListItem as ListItemBlock; + + if(subListItemBlock != null) + { + foreach (var line in subListItemBlock) + { + // Increment indent level for sub list. + RenderWithIndent(renderer, line, listBullet, indentLevel + 1); + } + } + } + } + } + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs b/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs new file mode 100644 index 0000000000..0ab0bf5412 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Type of conversion from markdown. + /// + [Flags] + public enum MarkdownConversionType + { + /// + /// Convert to HTML. + /// + HTML = 1, + + /// + /// Convert to VT100 encoded string. + /// + VT100 = 2 + } + + /// + /// Object representing the conversion from markdown. + /// + public class MarkdownInfo + { + /// + /// Html content after conversion. + /// + public string Html { get; internal set;} + + /// + /// VT100 encoded string after conversion. + /// + public string VT100EncodedString { get; internal set;} + + /// + /// AST of the markdown string. + /// + public Markdig.Syntax.MarkdownDocument Tokens { get; internal set; } + } + + /// + /// Class to convert a markdown string to VT100, HTML or AST. + /// + public sealed class MarkdownConverter + { + /// + /// Convert from markdown string to VT100 encoded string or HTML. Returns MarkdownInfo object. + /// + /// string with markdown content to be converted + /// specifies type of conversion, either VT100 or HTML + /// specifies the rendering options for VT100 rendering + public static MarkdownInfo Convert(string markdownString, MarkdownConversionType conversionType, MarkdownOptionInfo optionInfo) + { + var renderInfo = new MarkdownInfo(); + var writer = new StringWriter(); + MarkdownPipeline pipeline = null; + + if(conversionType.HasFlag(MarkdownConversionType.HTML)) + { + pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); + var renderer = new Markdig.Renderers.HtmlRenderer(writer); + renderInfo.Html = Markdig.Markdown.Convert(markdownString, renderer, pipeline).ToString(); + } + + if(conversionType.HasFlag(MarkdownConversionType.VT100)) + { + pipeline = new MarkdownPipelineBuilder().Build(); + // Use the VT100 renderer. + var renderer = new VT100Renderer(writer, optionInfo); + renderInfo.VT100EncodedString = Markdig.Markdown.Convert(markdownString, renderer, pipeline).ToString(); + } + + // Always have AST available. + var parsed = Markdig.Markdown.Parse(markdownString, pipeline); + renderInfo.Tokens = parsed; + + return renderInfo; + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj new file mode 100644 index 0000000000..bac0826757 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj @@ -0,0 +1,30 @@ + + + + + PowerShell Core's Markdown Rendering project + Microsoft.PowerShell.MarkdownRender + + + + $(DefineConstants);CORECLR + + + + portable + + + + $(DefineConstants);UNIX + + + + full + + + + + + + + diff --git a/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs new file mode 100644 index 0000000000..fa7043d07f --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for paragraphs. + /// + internal class ParagraphBlockRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, ParagraphBlock obj) + { + // Call the renderer for children, leaf inline or line breaks. + renderer.WriteChildren(obj.Inline); + // Add new line at the end of the paragraph. + renderer.WriteLine(); + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs new file mode 100644 index 0000000000..a7e9217a72 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Renderer for adding VT100 escape sequences for quote blocks. + /// + internal class QuoteBlockRenderer : VT100ObjectRenderer + { + protected override void Write(VT100Renderer renderer, QuoteBlock obj) + { + // Iterate through each item and add the quote character before the content. + foreach(var item in obj) + { + renderer.Write(obj.QuoteChar).Write(" ").Write(item); + } + + // Add blank line after the quote block. + renderer.WriteLine(); + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs new file mode 100644 index 0000000000..360221bed3 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Class to represent color preference options for various markdown elements. + /// + public sealed class MarkdownOptionInfo + { + private const char Esc = (char) 0x1b; + + /// + /// Current VT100 escape sequence for header 1. + /// + public string Header1 { get; set; } + + /// + /// Current VT100 escape sequence for header 2. + /// + public string Header2 { get; set; } + + /// + /// Current VT100 escape sequence for header 3. + /// + public string Header3 { get; set; } + + /// + /// Current VT100 escape sequence for header 4. + /// + public string Header4 { get; set; } + + /// + /// Current VT100 escape sequence for header 5. + /// + public string Header5 { get; set; } + + /// + /// Current VT100 escape sequence for header 6. + /// + public string Header6 { get; set; } + + /// + /// Current VT100 escape sequence for code inline and code blocks. + /// + public string Code { get; set; } + + /// + /// Current VT100 escape sequence for links. + /// + public string Link { get; set; } + + /// + /// Current VT100 escape sequence for images. + /// + public string Image { get; set; } + + /// + /// Current VT100 escape sequence for bold text. + /// + public string EmphasisBold { get; set; } + + /// + /// Current VT100 escape sequence for italics text. + /// + public string EmphasisItalics { get; set; } + + /// + /// Get the property as an rendered escape sequence. + /// This is used for typesps1xml for displaying. + /// + public string AsEscapeSequence(string propertyName) + { + var propertyValue = this.GetType().GetProperty(propertyName)?.GetValue(this) as string; + + if(!String.IsNullOrEmpty(propertyValue)) + { + return string.Concat(Esc, propertyValue, propertyValue, Esc, "[0m"); + } + else + { + throw new InvalidOperationException(); + } + } + + /// + /// Set dark as the default theme. + /// + public MarkdownOptionInfo() + { + SetDarkTheme(); + } + + /// + /// Set all preference for dark theme. + /// + public void SetDarkTheme() + { + Header1 = "[7m"; + Header2 = "[4;93m"; + Header3 = "[4;94m"; + Header4 = "[4;95m"; + Header5 = "[4;96m"; + Header6 = "[4;97m"; + Code = "[48;2;155;155;155;38;2;30;30;30m"; + Link = "[4;38;5;117m"; + Image = "[33m"; + EmphasisBold = "[1m"; + EmphasisItalics = "[36m"; + } + + /// + /// Set all preference for light theme. + /// + public void SetLightTheme() + { + Header1 = "[7m"; + Header2 = "[4;33m"; + Header3 = "[4;34m"; + Header4 = "[4;35m"; + Header5 = "[4;36m"; + Header6 = "[4;30m"; + Code = "[48;2;155;155;155;38;2;30;30;30m"; + Link = "[4;38;5;117m"; + Image = "[33m"; + EmphasisBold = "[1m"; + EmphasisItalics = "[36m"; + } + } + + /// + /// Class to represent default VT100 escape sequences + /// + public class VT100EscapeSequences + { + private const char Esc = (char) 0x1B; + + private string EndSequence = Esc + "[0m"; + + private MarkdownOptionInfo options; + + /// + /// Set the options as per + /// + public VT100EscapeSequences(MarkdownOptionInfo optionInfo) + { + if(optionInfo == null) + { + throw new ArgumentNullException("optionInfo"); + } + + options = optionInfo; + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatHeader1(string headerText) + { + return String.Concat(Esc, options.Header1, headerText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatHeader2(string headerText) + { + return String.Concat(Esc, options.Header2, headerText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatHeader3(string headerText) + { + return String.Concat(Esc, options.Header3, headerText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatHeader4(string headerText) + { + return String.Concat(Esc, options.Header4, headerText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatHeader5(string headerText) + { + return String.Concat(Esc, options.Header5, headerText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatHeader6(string headerText) + { + return String.Concat(Esc, options.Header6, headerText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatCode(string codeText, bool isInline) + { + if(isInline) + { + return String.Concat(Esc, options.Code, codeText, EndSequence); + } + else + { + // For code blocks, [500@ make sure that the whole line has background color. + return String.Concat(Esc, options.Code, codeText, Esc, "[500@", EndSequence); + } + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatLink(string linkText, string url, bool hideUrl = true) + { + if(hideUrl) + { + return String.Concat(Esc, options.Link, "\"", linkText, "\"", EndSequence); + } + else + { + return String.Concat("\"", linkText, "\" (", Esc, options.Link, url, EndSequence, ")"); + } + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatEmphasis(string emphasisText, bool isBold) + { + var sequence = isBold ? options.EmphasisBold : options.EmphasisItalics; + return String.Concat(Esc, sequence, emphasisText, EndSequence); + } + + /// + /// Class to represent default VT100 escape sequences + /// + public string FormatImage(string altText) + { + return String.Concat(Esc, options.Image, "[", altText, "]", EndSequence); + } + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs new file mode 100644 index 0000000000..1cfd1bb04a --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Implement the MarkdownObjectRenderer with VT100Renderer. + /// + public abstract class VT100ObjectRenderer : MarkdownObjectRenderer where T : MarkdownObject + { + } +} diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs new file mode 100644 index 0000000000..ff085b06f8 --- /dev/null +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Markdig; +using Markdig.Syntax; +using Markdig.Renderers; + +namespace Microsoft.PowerShell.MarkdownRender +{ + /// + /// Implementation of the VT100 renderer. + /// + public sealed class VT100Renderer : TextRendererBase + { + /// + /// Initialize the VT100 renderer with and write the output . + /// + public VT100Renderer(TextWriter writer, MarkdownOptionInfo optionInfo) : base(writer) + { + EscapeSequences = new VT100EscapeSequences(optionInfo); + + // Add the various element renderers. + ObjectRenderers.Add(new HeaderBlockRenderer()); + ObjectRenderers.Add(new LineBreakRenderer()); + ObjectRenderers.Add(new CodeInlineRenderer()); + ObjectRenderers.Add(new FencedCodeBlockRenderer()); + ObjectRenderers.Add(new EmphasisInlineRenderer()); + ObjectRenderers.Add(new ParagraphBlockRenderer()); + ObjectRenderers.Add(new LeafInlineRenderer()); + ObjectRenderers.Add(new LinkInlineRenderer()); + ObjectRenderers.Add(new ListBlockRenderer()); + ObjectRenderers.Add(new ListItemBlockRenderer()); + ObjectRenderers.Add(new QuoteBlockRenderer()); + } + + /// + /// Get the current escape sequences. + /// + public VT100EscapeSequences EscapeSequences { get; private set;} + } +} diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 5adfcf86bc..15d23791fe 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -24,7 +24,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex", - "Test-Json", "Remove-Alias" + "Test-Json", "Remove-Alias", "ConvertFrom-Markdown", "Show-Markdown", "Set-MarkdownOption", "Get-MarkdownOption" FunctionsToExport= "Import-PowerShellDataFile" AliasesToExport= "fhx" NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index d427680156..b33a5b3b0d 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -24,7 +24,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash", "Unblock-File", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "Get-Verb", "Format-Hex", - "Test-Json", "Remove-Alias" + "Test-Json", "Remove-Alias", "ConvertFrom-Markdown", "Show-Markdown", "Set-MarkdownOption", "Get-MarkdownOption" FunctionsToExport= "ConvertFrom-SddlString" AliasesToExport= "fhx" NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" diff --git a/src/System.Management.Automation/engine/TypesV3_Ps1Xml.cs b/src/System.Management.Automation/engine/TypesV3_Ps1Xml.cs index e12ad850a5..46c152f83b 100644 --- a/src/System.Management.Automation/engine/TypesV3_Ps1Xml.cs +++ b/src/System.Management.Automation/engine/TypesV3_Ps1Xml.cs @@ -114,6 +114,31 @@ namespace System.Management.Automation.Runspaces var td15 = new TypeData(@"Deserialized.System.Management.Automation.DebuggerStopEventArgs", true); td15.TargetTypeForDeserialization = typeof(Microsoft.PowerShell.DeserializingTypeConverter); yield return td15; + + var td16 = new TypeData(@"Microsoft.PowerShell.MarkdownRender.MarkdownOptionInfo"); + td16.Members.Add("Header1", + new ScriptPropertyData(@"Header1", GetScriptBlock(@"$this.AsEscapeSequence('Header1')"), setScriptBlock: null)); + td16.Members.Add("Header2", + new ScriptPropertyData(@"Header2", GetScriptBlock(@"$this.AsEscapeSequence('Header2')"), setScriptBlock: null)); + td16.Members.Add("Header3", + new ScriptPropertyData(@"Header3", GetScriptBlock(@"$this.AsEscapeSequence('Header3')"), setScriptBlock: null)); + td16.Members.Add("Header4", + new ScriptPropertyData(@"Header4", GetScriptBlock(@"$this.AsEscapeSequence('Header4')"), setScriptBlock: null)); + td16.Members.Add("Header5", + new ScriptPropertyData(@"Header5", GetScriptBlock(@"$this.AsEscapeSequence('Header5')"), setScriptBlock: null)); + td16.Members.Add("Header6", + new ScriptPropertyData(@"Header6", GetScriptBlock(@"$this.AsEscapeSequence('Header6')"), setScriptBlock: null)); + td16.Members.Add("Code", + new ScriptPropertyData(@"Code", GetScriptBlock(@"$this.AsEscapeSequence('Code')"), setScriptBlock: null)); + td16.Members.Add("Link", + new ScriptPropertyData(@"Link", GetScriptBlock(@"$this.AsEscapeSequence('Link')"), setScriptBlock: null)); + td16.Members.Add("Image", + new ScriptPropertyData(@"Image", GetScriptBlock(@"$this.AsEscapeSequence('Image')"), setScriptBlock: null)); + td16.Members.Add("EmphasisBold", + new ScriptPropertyData(@"EmphasisBold", GetScriptBlock(@"$this.AsEscapeSequence('EmphasisBold')"), setScriptBlock: null)); + td16.Members.Add("EmphasisItalics", + new ScriptPropertyData(@"EmphasisItalics", GetScriptBlock(@"$this.AsEscapeSequence('EmphasisItalics')"), setScriptBlock: null)); + yield return td16; } } } From 32876c89e74abbaf895365431ef6890e99ffdc87 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Tue, 24 Apr 2018 16:10:29 -0700 Subject: [PATCH 02/12] Added Tests for Markdown cmdlets --- .../utility/ConvertFromMarkdownCommand.cs | 70 +++--- .../resources/ConvertMarkdownStrings.resx | 3 - .../MarkdownCmdlets.Tests.ps1 | 216 ++++++++++++++++++ 3 files changed, 247 insertions(+), 42 deletions(-) create mode 100644 test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs index 3371d8500d..cb76de25ee 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -7,7 +7,9 @@ using System.Threading.Tasks; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Management.Automation; +using System.Management.Automation.Internal; using Microsoft.PowerShell.MarkdownRender; +using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -99,10 +101,10 @@ namespace Microsoft.PowerShell.Commands } else { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, inputObj.GetType()); + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, baseObj.GetType()); ErrorRecord errorRecord = new ErrorRecord( new InvalidDataException(errorMessage), - "ConvertFromMarkdownInvalidInputObject", + "InvalidInputObject", ErrorCategory.InvalidData, InputObject); @@ -126,55 +128,31 @@ namespace Microsoft.PowerShell.Commands { foreach (var path in paths) { + // ResolvePath checks for file existence. var resolvedPaths = ResolvePath(path, isLiteral); foreach (var resolvedPath in resolvedPaths) { - if (File.Exists(resolvedPath)) - { - WriteObject( + WriteObject( MarkdownConverter.Convert( ReadContentFromFile(resolvedPath).Result, conversionType, optionInfo) ); - } - else - { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InputFileNotFound, resolvedPath); - var errorRecord = new ErrorRecord( - new FileNotFoundException(errorMessage), - "ConvertFromMarkdownFileNotFound", - ErrorCategory.ResourceUnavailable, - resolvedPath); - - WriteError(errorRecord); - } } } } private async Task ReadContentFromFile(string filePath) { - if (File.Exists(filePath)) - { - using (StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))) - { - string mdContent = await reader.ReadToEndAsync(); - return mdContent; - } - } - else - { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InputFileNotFound, filePath); - var errorRecord = new ErrorRecord( - new FileNotFoundException(errorMessage), - "ConvertFromMarkdownFileNotFound", - ErrorCategory.ResourceUnavailable, - resolvedPath); + Dbg.Diagnostics.Assert(File.Exists(filePath), "Caller should make sure the file exists."); - WriteError(errorRecord); + using (StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))) + { + string mdContent = await reader.ReadToEndAsync(); + return mdContent; } + } private List ResolvePath(string path, bool isLiteral) @@ -183,20 +161,34 @@ namespace Microsoft.PowerShell.Commands PSDriveInfo drive = null; List resolvedPaths = new List(); - if (isLiteral) + try { - resolvedPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(path, out provider, out drive)); + if (isLiteral) + { + resolvedPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(path, out provider, out drive)); + } + else + { + resolvedPaths.AddRange(Context.SessionState.Path.GetResolvedProviderPathFromPSPath(path, out provider)); + } } - else + catch (ItemNotFoundException infe) { - resolvedPaths.AddRange(Context.SessionState.Path.GetResolvedProviderPathFromPSPath(path, out provider)); + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InputFileNotFound, path); + var errorRecord = new ErrorRecord( + infe, + "FileNotFound", + ErrorCategory.ResourceUnavailable, + path); + + WriteError(errorRecord); } if (!provider.Name.Equals("FileSystem", StringComparison.OrdinalIgnoreCase)) { string errorMessage = StringUtil.Format(ConvertMarkdownStrings.FileSystemPathsOnly, path); ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(), - "ConvertFromMarkdownOnlySupportsFileSystemPaths", + "OnlyFileSystemPathsSupported", ErrorCategory.InvalidArgument, path); WriteError(errorRecord); diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx index 9bf09f3c12..5eff4c73f5 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -126,7 +126,4 @@ The given path '{0}' is not supported. This command only supports the FileSystem Provider paths. - - The given path '{0}' is not supported. This command only supports the FileSystem Provider paths. - diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 new file mode 100644 index 0000000000..3424b1949d --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -0,0 +1,216 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe 'ConvertFrom-Markdown tests' -Tags 'CI' { + + BeforeAll { + $esc = [char]0x1b + + function GetExpectedString + { + [CmdletBinding()] + param( + [ValidateSet( + "Header1", "Header2", "Header3", "Header4", "Header5", "Header6", + "Code", "CodeBlock", + "Link", "Image", + "Bold", "Italics")] + [Parameter()] + [string] $ElementType, + + [string] $Text, + + [string] $CodeFormatString, + + [string] $CodeText + ) + + switch($elementType) + { + "Header1" { "$esc[7m$text$esc[0m`n`n" } + "Header2" { "$esc[4;93m$text$esc[0m`n`n" } + "Header3" { "$esc[4;94m$text$esc[0m`n`n" } + "Header4" { "$esc[4;95m$text$esc[0m`n`n" } + "Header5" { "$esc[4;96m$text$esc[0m`n`n" } + "Header6" { "$esc[4;97m$text$esc[0m`n`n" } + + "Code" { ($CodeFormatString -f "$esc[48;2;155;155;155;38;2;30;30;30m$CodeText$esc[0m") + "`n`n" } + "CodeBlock" { + $expectedString = @() + $CodeText -split "`n" | ForEach-Object { $expectedString += "$esc[48;2;155;155;155;38;2;30;30;30m$_$esc[500@$esc[0m" } + $returnString = $expectedString -join "`n" + "$returnString`n`n" + } + + "Link" { "$esc[4;38;5;117m`"$text`"$esc[0m`n" } + "Image" { "$esc[33m[$text]$esc[0m`n" } + "Bold" { "$esc[1m$text$esc[0m`n" } + "Italics" { "$esc[36m$text$esc[0m`n" } + } + } + + function GetExpectedHTML + { + [CmdletBinding()] + param( + [ValidateSet( + "Header1", "Header2", "Header3", "Header4", "Header5", "Header6", + "Code", "CodeBlock", + "Link", "Image", + "Bold", "Italics")] + [Parameter()] + [string] $ElementType, + + [string] $Text, + + [string] $Url, + + [string] $CodeFormatString, + + [string] $CodeText + ) + + $id = $Text.Replace(" ","-").ToLowerInvariant() + + switch($elementType) + { + "Header1" { "

$text

`n" } + "Header2" { "

$text

`n" } + "Header3" { "

$text

`n" } + "Header4" { "

$text

`n" } + "Header5" { "
$text
`n" } + "Header6" { "
$text
`n" } + + "Code" { "

" + ($CodeFormatString -f "$CodeText") + "

`n" } + "CodeBlock" { "
$CodeText`n
`n" } + + "Link" { "

$text

`n" } + "Image" { "

`"$text`"

`n" } + "Bold" { "

$text

`n" } + "Italics" { "

$text

`n" } + } + } + } + + Context 'Basic tests' { + BeforeAll { + $esc = [char]0x1b + $mdFile = New-Item -Path $TestDrive/input.md -Value "Some **test string** to write in a file" -Force + $mdLiteralPath = New-Item -Path $TestDrive/LiteralPath.md -Value "Some **test string** to write in a file" -Force + $expectedStringFromFile = "Some $esc[1mtest string$esc[0m to write in a file`n`n" + + $codeBlock = @' +``` +bool function() +{ +} +``` +'@ + + $codeBlockText = @" +bool function()`n{`n} +"@ + + $TestCases = @( + @{ element = 'Header1'; InputMD = '# Header 1'; Text = 'Header 1' } + @{ element = 'Header2'; InputMD = '## Header 2'; Text = 'Header 2' } + @{ element = 'Header3'; InputMD = '### Header 3'; Text = 'Header 3' } + @{ element = 'Header4'; InputMD = '#### Header 4'; Text = 'Header 4' } + @{ element = 'Header5'; InputMD = '##### Header 5'; Text = 'Header 5' } + @{ element = 'Header6'; InputMD = '###### Header 6'; Text = 'Header 6' } + @{ element = 'Code'; InputMD = 'This is a `code` sample'; CodeFormatString = 'This is a {0} sample'; CodeText = 'code'} + @{ element = 'CodeBlock'; InputMD = $codeBlock; CodeText = $codeBlockText } + @{ element = 'Link'; InputMD = '[GitHub](https://www.github.com)'; Text = 'GitHub'; Url = 'https://www.github.com'} + @{ element = 'Image'; InputMD = '![alt-text](https://bing.com/ps.svg)'; Text = 'alt-text'; Url = 'https://bing.com/ps.svg'} + @{ element = 'Bold'; InputMD = '**bold text**'; Text = 'bold text' } + @{ element = 'Italics'; InputMD = '*italics text*'; Text = 'italics text' } + ) + } + + + It 'Can convert element : to vt100 using pipeline input' -TestCases $TestCases { + param($element, $inputMD, $text, $codeFormatString, $codeText) + + $output = $inputMD | ConvertFrom-Markdown -AsVT100EncodedString + + if($element -like 'Header?' -or + $element -eq 'Link' -or + $element -eq 'Image' -or + $element -eq 'Bold' -or + $element -eq 'Italics') + { + $expectedString = GetExpectedString -ElementType $element -Text $text + } + elseif($element -eq 'Code') + { + $expectedString = GetExpectedString -ElementType $element -CodeFormatString $codeFormatString -CodeText $codeText + } + elseif($element -eq 'CodeBlock') + { + $expectedString = GetExpectedString -ElementType $element -CodeText $codeText + } + + $output.VT100EncodedString | Should BeExactly $expectedString + } + + It 'Can convert element : to HTML using pipeline input' -TestCases $TestCases { + param($element, $inputMD, $text, $codeFormatString, $codeText, $url) + + $output = $inputMD | ConvertFrom-Markdown + + if($element -like 'Header?' -or + $element -eq 'Bold' -or + $element -eq 'Italics') + { + $expectedString = GetExpectedHTML -ElementType $element -Text $text + } + elseif($element -eq 'Code') + { + $expectedString = GetExpectedHTML -ElementType $element -CodeFormatString $codeFormatString -CodeText $codeText + } + elseif($element -eq 'CodeBlock') + { + $expectedString = GetExpectedHTML -ElementType $element -CodeText $codeText + } + elseif ($element -eq 'Link') + { + $expectedString = GetExpectedHTML -ElementType $element -Text $text -Url $url + } + elseif ($element -eq 'Image') + { + $expectedString = GetExpectedHTML -ElementType $element -Text $text -Url $url + } + + $output.Html | Should BeExactly $expectedString + } + + It 'Can convert input from a file path to vt100 encoded string' { + $output = ConvertFrom-Markdown -Path $mdFile.FullName -AsVT100EncodedString + $output.VT100EncodedString | Should BeExactly $expectedStringFromFile + } + + It 'Can convert input from a fileinfo object to vt100 encoded string' { + $ouputFromFileInfo = $mdFile | ConvertFrom-Markdown -AsVT100EncodedString + $ouputFromFileInfo.VT100EncodedString | Should BeExactly $expectedStringFromFile + } + + It 'Can convert input from a literal path to vt100 encoded string' { + $output = ConvertFrom-Markdown -Path $mdLiteralPath -AsVT100EncodedString + $output.VT100EncodedString | Should BeExactly $expectedStringFromFile + } + } + + Context "ConvertFrom-Markdown error cases" { + It "Gets an error if path is not FileSystem provider path" { + { ConvertFrom-Markdown -Path Env:\PSModulePath -ErrorAction Stop } | Should -Throw -ErrorId 'OnlyFileSystemPathsSupported,Microsoft.PowerShell.Commands.ConvertFromMarkdownCommand' + } + + It "Gets an error if path does not exist" { + { ConvertFrom-Markdown -Path DoestnotExist -ErrorAction Stop } | Should -Throw -ErrorId 'FileNotFound,Microsoft.PowerShell.Commands.ConvertFromMarkdownCommand' + } + + It "Gets an error if input object type is not correct" { + { ConvertFrom-Markdown -InputObject 1 -ErrorAction Stop } | Should -Throw -ErrorId 'InvalidInputObject,Microsoft.PowerShell.Commands.ConvertFromMarkdownCommand' + } + } +} From 275e10920276e8f48d8b5952e00e4032a019111c Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Tue, 10 Jul 2018 08:58:19 -0700 Subject: [PATCH 03/12] [Feature] Address code review comments --- .../utility/ConvertFromMarkdownCommand.cs | 69 ++++++++------- .../utility/MarkdownOptionCommands.cs | 51 ++++++----- .../commands/utility/ShowMarkdownCommand.cs | 3 +- .../resources/ConvertMarkdownStrings.resx | 2 +- .../ListBlockRenderer.cs | 8 +- .../ListItemBlockRenderer.cs | 22 ++++- ...Microsoft.PowerShell.MarkdownRender.csproj | 4 +- .../MarkdownCmdlets.Tests.ps1 | 86 +++++++++++++++++-- 8 files changed, 173 insertions(+), 72 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs index cb76de25ee..a2592d9bf1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -19,28 +19,28 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet( VerbsData.ConvertFrom, "Markdown", - DefaultParameterSetName = PathParamSet, + DefaultParameterSetName = PathParameterSet, HelpUri = "TBD" )] [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownInfo))] public class ConvertFromMarkdownCommand : PSCmdlet { /// - /// Path to the file to convert from Markdown to MarkdownInfo + /// Path to the file to convert from Markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] - [Parameter(ParameterSetName = PathParamSet, Mandatory = true)] + [Parameter(ParameterSetName = PathParameterSet, Mandatory = true)] public string[] Path { get; set; } /// - /// Path to the file to convert from Markdown to MarkdownInfo + /// Path to the file to convert from Markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] - [Parameter(ParameterSetName = LitPathParamSet, Mandatory = true)] + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true)] public string[] LiteralPath { get; set; } /// - /// InputObject of type System.IO.FileInfo or string with content to convert from Markdown to MarkdownInfo + /// InputObject of type System.IO.FileInfo or string with content to convert from Markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = InputObjParamSet, Mandatory = true, ValueFromPipeline = true)] @@ -52,19 +52,18 @@ namespace Microsoft.PowerShell.Commands [Parameter()] public SwitchParameter AsVT100EncodedString { get; set; } - private const string PathParamSet = "PathParamSet"; - private const string LitPathParamSet = "LiteralParamSet"; - + private const string PathParameterSet = "PathParamSet"; + private const string LiteralPathParameterSet = "LiteralParamSet"; private const string InputObjParamSet = "InputObjParamSet"; + private MarkdownConversionType conversionType = MarkdownConversionType.HTML; + private MarkdownOptionInfo mdOption = null; /// - /// Override ProcessRecord + /// Override BeginProcess. /// - protected override void ProcessRecord() + protected override void BeginProcessing() { - var conversionType = MarkdownConversionType.HTML; - - var mdOption = (SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo())) as MarkdownOptionInfo; + mdOption = (SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo())) as MarkdownOptionInfo; if(mdOption == null) { @@ -75,14 +74,20 @@ namespace Microsoft.PowerShell.Commands { conversionType = MarkdownConversionType.VT100; } + } + /// + /// Override ProcessRecord. + /// + protected override void ProcessRecord() + { switch (ParameterSetName) { case InputObjParamSet: Object baseObj = InputObject.BaseObject; - var fileInfo = baseObj as FileInfo; - if (fileInfo != null) + //var fileInfo = baseObj as FileInfo; + if (baseObj is FileInfo fileInfo) { WriteObject( MarkdownConverter.Convert( @@ -92,33 +97,28 @@ namespace Microsoft.PowerShell.Commands ) ); } + else if (baseObj is string inpObj) + { + WriteObject(MarkdownConverter.Convert(inpObj, conversionType, mdOption)); + } else { - var inpObj = baseObj as string; - if (inpObj != null) - { - WriteObject(MarkdownConverter.Convert(inpObj, conversionType, mdOption)); - } - else - { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, baseObj.GetType()); - ErrorRecord errorRecord = new ErrorRecord( - new InvalidDataException(errorMessage), - "InvalidInputObject", - ErrorCategory.InvalidData, - InputObject); + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, baseObj.GetType()); + ErrorRecord errorRecord = new ErrorRecord( + new InvalidDataException(errorMessage), + "InvalidInputObject", + ErrorCategory.InvalidData, + InputObject); - WriteError(errorRecord); - } + WriteError(errorRecord); } - break; - case PathParamSet: + case PathParameterSet: ConvertEachFile(Path, conversionType, isLiteral: false, optionInfo: mdOption); break; - case LitPathParamSet: + case LiteralPathParameterSet: ConvertEachFile(LiteralPath, conversionType, isLiteral: true, optionInfo: mdOption); break; } @@ -128,7 +128,6 @@ namespace Microsoft.PowerShell.Commands { foreach (var path in paths) { - // ResolvePath checks for file existence. var resolvedPaths = ResolvePath(path, isLiteral); foreach (var resolvedPath in resolvedPaths) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs index eba767db17..e2ce0bf931 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs @@ -22,101 +22,112 @@ namespace Microsoft.PowerShell.Commands public class SetMarkdownOptionCommand : PSCmdlet { /// + /// Gets or sets the VT100 escape sequence for Header Level 1. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string Header1Color { get; set;} /// + /// Gets or sets the VT100 escape sequence for Header Level 2. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string Header2Color { get; set;} /// + /// Gets or sets the VT100 escape sequence for Header Level 3. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string Header3Color { get; set;} /// + /// Gets or sets the VT100 escape sequence for Header Level 4. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string Header4Color { get; set;} /// + /// Gets or sets the VT100 escape sequence for Header Level 5. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string Header5Color { get; set;} /// + /// Gets or sets the VT100 escape sequence for Header Level 6. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string Header6Color { get; set;} /// + /// Gets or sets the VT100 escape sequence for code block background. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string CodeBlockForegroundColor { get; set;} - - /// - /// - [ValidatePattern(@"^\[*[0-9;]*?m{1}")] - [Parameter(ParameterSetName = IndividualSetting)] - public string CodeBlockBackgroundColor { get; set;} + public string Code { get; set;} /// + /// Gets or sets the VT100 escape sequence for image alt text foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string ImageAltTextForegroundColor { get; set;} /// + /// Gets or sets the VT100 escape sequence for link foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string LinkForegroundColor { get; set;} /// + /// Gets or sets the VT100 escape sequence for italics text foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string ItalicsForegroundColor { get; set;} /// + /// Gets or sets the VT100 escape sequence for bold text foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] public string BoldForegroundColor { get; set;} /// + /// Gets or sets the switch to PassThru the values set. /// [Parameter()] public SwitchParameter PassThru { get; set;} /// + /// Gets or sets the Theme. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = ThemeParamSet, Mandatory = true)] + [ValidateSet(DarkThemeName, LightThemeName)] public string Theme { get; set;} /// + /// Gets or sets InputObject. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = InputObjectParamSet, Mandatory = true, ValueFromPipeline = true)] public PSObject InputObject { get; set;} private const string IndividualSetting = "IndividualSetting"; - private const string InputObjectParamSet = "InputObject"; - private const string ThemeParamSet = "Theme"; + private const string MarkdownOptionInfoVariableName = "MarkdownOptionInfo"; + private const string LightThemeName = "Light"; + private const string DarkThemeName = "Dark"; /// + /// Override EndProcessing. /// protected override void EndProcessing() { @@ -126,11 +137,11 @@ namespace Microsoft.PowerShell.Commands { case ThemeParamSet: mdOptionInfo = new MarkdownOptionInfo(); - if(string.Equals(Theme, "Light", StringComparison.OrdinalIgnoreCase)) + if(string.Equals(Theme, LightThemeName, StringComparison.OrdinalIgnoreCase)) { mdOptionInfo.SetLightTheme(); } - else if(string.Equals(Theme, "Dark", StringComparison.OrdinalIgnoreCase)) + else if(string.Equals(Theme, DarkThemeName, StringComparison.OrdinalIgnoreCase)) { mdOptionInfo.SetDarkTheme(); } @@ -144,7 +155,6 @@ namespace Microsoft.PowerShell.Commands { throw new ArgumentException(); } - break; case IndividualSetting: @@ -154,7 +164,7 @@ namespace Microsoft.PowerShell.Commands } var sessionVar = SessionState.PSVariable; - sessionVar.Set("MarkdownOptionInfo", mdOptionInfo); + sessionVar.Set(MarkdownOptionInfoVariableName, mdOptionInfo); if(PassThru.IsPresent) { @@ -194,14 +204,9 @@ namespace Microsoft.PowerShell.Commands mdOptionInfo.Header6 = Header6Color; } - if (!String.IsNullOrEmpty(CodeBlockBackgroundColor)) + if (!String.IsNullOrEmpty(Code)) { - mdOptionInfo.Code = CodeBlockBackgroundColor; - } - - if (!String.IsNullOrEmpty(CodeBlockForegroundColor)) - { - mdOptionInfo.Code = CodeBlockForegroundColor; + mdOptionInfo.Code = Code; } if (!String.IsNullOrEmpty(ImageAltTextForegroundColor)) @@ -227,6 +232,7 @@ namespace Microsoft.PowerShell.Commands } /// + /// Implements the cmdlet for getting the markdown options that are set. /// [Cmdlet( VerbsCommon.Get, "MarkdownOption", @@ -235,11 +241,14 @@ namespace Microsoft.PowerShell.Commands [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownOptionInfo))] public class GetMarkdownOptionCommand : PSCmdlet { + private const string MarkdownOptionInfoVariableName = "MarkdownOptionInfo"; + /// + /// Override endproessing. /// protected override void EndProcessing() { - WriteObject(SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo())); + WriteObject(SessionState.PSVariable.GetValue(MarkdownOptionInfoVariableName, new MarkdownOptionInfo())); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index 8e4f639844..bd37f41fb1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -58,7 +58,8 @@ namespace Microsoft.PowerShell.Commands { Object inpObj = InputObject.BaseObject; var markdownInfo = inpObj as MarkdownInfo; - if (markdownInfo == null) + + if (markdownInfo != null) { var errorRecord = new ErrorRecord( new ArgumentException(), diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx index 5eff4c73f5..f9aacc5d6f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -124,6 +124,6 @@ The given file path '{0}' is not found. - The given path '{0}' is not supported. This command only supports the FileSystem Provider paths. + Only FileSystem Provider paths are supported. The given path '{0}' is not supported. diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs index 3747bebe1a..5ee5dd5da8 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs @@ -21,9 +21,7 @@ namespace Microsoft.PowerShell.MarkdownRender foreach (var item in obj) { - var listItem = item as ListItemBlock; - - if (listItem != null) + if (item is ListItemBlock listItem) { if (obj.IsOrdered) { @@ -44,9 +42,7 @@ namespace Microsoft.PowerShell.MarkdownRender // For a numbered list, we need to make sure the index is incremented. foreach (var line in block) { - var paragraphBlock = line as ParagraphBlock; - - if(paragraphBlock != null) + if(line is ParagraphBlock paragraphBlock) { renderer.Write(index.ToString()).Write(". ").Write(paragraphBlock.Inline); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs index 276d773757..d96b744d65 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Threading; using Markdig; using Markdig.Syntax; using Markdig.Renderers; @@ -33,7 +34,7 @@ namespace Microsoft.PowerShell.MarkdownRender private void RenderWithIndent(VT100Renderer renderer, MarkdownObject block, char listBullet, int indentLevel) { // Indent left by 2 for each level on list. - string indent = "".PadLeft(indentLevel * 2); + string indent = Padding(indentLevel * 2); var paragraphBlock = block as ParagraphBlock; @@ -62,5 +63,24 @@ namespace Microsoft.PowerShell.MarkdownRender } } } + + // Typical padding is at most a screen's width, any more than that and we won't bother caching. + private const int IndentCacheMax = 120; + private static readonly string[] IndentCache = new string[IndentCacheMax]; + internal static string Padding(int countOfSpaces) + { + if (countOfSpaces >= IndentCacheMax) + return new string(' ', countOfSpaces); + + var result = IndentCache[countOfSpaces]; + + if (result == null) + { + Interlocked.CompareExchange(ref IndentCache[countOfSpaces], new string(' ', countOfSpaces), comparand:null); + result = IndentCache[countOfSpaces]; + } + + return result; + } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj index bac0826757..bae34827c7 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj +++ b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj @@ -6,7 +6,7 @@ Microsoft.PowerShell.MarkdownRender
- + diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 index 3424b1949d..6877136b09 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -150,7 +150,7 @@ bool function()`n{`n} $expectedString = GetExpectedString -ElementType $element -CodeText $codeText } - $output.VT100EncodedString | Should BeExactly $expectedString + $output.VT100EncodedString | Should -BeExactly $expectedString } It 'Can convert element : to HTML using pipeline input' -TestCases $TestCases { @@ -181,22 +181,22 @@ bool function()`n{`n} $expectedString = GetExpectedHTML -ElementType $element -Text $text -Url $url } - $output.Html | Should BeExactly $expectedString + $output.Html | Should -BeExactly $expectedString } It 'Can convert input from a file path to vt100 encoded string' { $output = ConvertFrom-Markdown -Path $mdFile.FullName -AsVT100EncodedString - $output.VT100EncodedString | Should BeExactly $expectedStringFromFile + $output.VT100EncodedString | Should -BeExactly $expectedStringFromFile } It 'Can convert input from a fileinfo object to vt100 encoded string' { $ouputFromFileInfo = $mdFile | ConvertFrom-Markdown -AsVT100EncodedString - $ouputFromFileInfo.VT100EncodedString | Should BeExactly $expectedStringFromFile + $ouputFromFileInfo.VT100EncodedString | Should -BeExactly $expectedStringFromFile } It 'Can convert input from a literal path to vt100 encoded string' { $output = ConvertFrom-Markdown -Path $mdLiteralPath -AsVT100EncodedString - $output.VT100EncodedString | Should BeExactly $expectedStringFromFile + $output.VT100EncodedString | Should -BeExactly $expectedStringFromFile } } @@ -213,4 +213,80 @@ bool function()`n{`n} { ConvertFrom-Markdown -InputObject 1 -ErrorAction Stop } | Should -Throw -ErrorId 'InvalidInputObject,Microsoft.PowerShell.Commands.ConvertFromMarkdownCommand' } } + + Context "Get/Set-MarkdownOption tests" { + + BeforeAll { + $esc = [char]0x1b + } + + BeforeEach { + $originalOptions = Get-MarkdownOption + } + + AfterEach { + Set-MarkdownOption -InputObject $originalOptions + } + + It "Verify default values for MarkdownOptions" { + $options = Get-MarkdownOption + + $options.Header1 | Should -BeExactly "$esc[7m[7m$esc[0m" + $options.Header2 | Should -BeExactly "$esc[4;93m[4;93m$esc[0m" + $options.Header3 | Should -BeExactly "$esc[4;94m[4;94m$esc[0m" + $options.Header4 | Should -BeExactly "$esc[4;95m[4;95m$esc[0m" + $options.Header5 | Should -BeExactly "$esc[4;96m[4;96m$esc[0m" + $options.Header6 | Should -BeExactly "$esc[4;97m[4;97m$esc[0m" + $options.Code | Should -BeExactly "$esc[48;2;155;155;155;38;2;30;30;30m[48;2;155;155;155;38;2;30;30;30m$esc[0m" + $options.Link | Should -BeExactly "$esc[4;38;5;117m[4;38;5;117m$esc[0m" + $options.Image | Should -BeExactly "$esc[33m[33m$esc[0m" + $options.EmphasisBold | Should -BeExactly "$esc[1m[1m$esc[0m" + $options.EmphasisItalics | Should -BeExactly "$esc[36m[36m$esc[0m" + } + + It "Verify Set-MarkdownOption can get options" { + Set-MarkdownOption ` + -Header1Color "[4;1m" ` + -Header2Color "[93m" ` + -Header3Color "[94m" ` + -Header4Color "[95m" ` + -Header5Color "[96m" ` + -Header6Color "[97m" ` + -ImageAltTextForegroundColor "[34m" ` + -LinkForegroundColor "[4;38;5;88m" ` + -ItalicsForegroundColor "[35m" ` + -BoldForegroundColor "[32m" + + $newOptions = Get-MarkdownOption + + $options.Header1 | Should -BeExactly "$esc[4;1m[4;1m$esc[0m" + $options.Header2 | Should -BeExactly "$esc[93m[93m$esc[0m" + $options.Header3 | Should -BeExactly "$esc[94m[94m$esc[0m" + $options.Header4 | Should -BeExactly "$esc[95m[95m$esc[0m" + $options.Header5 | Should -BeExactly "$esc[96m[96m$esc[0m" + $options.Header6 | Should -BeExactly "$esc[97m[97m$esc[0m" + #$options.Code | Should -BeExactly "$esc[48;2;155;155;155;38;2;30;30;30m[48;2;155;155;155;38;2;30;30;30m$esc[0m" + $options.Link | Should -BeExactly "$esc[4;38;5;88m[4;38;5;88m$esc[0m" + $options.Image | Should -BeExactly "$esc[34m[34m$esc[0m" + $options.EmphasisBold | Should -BeExactly "$esc[32m[32m$esc[0m" + $options.EmphasisItalics | Should -BeExactly "$esc[35m[35m$esc[0m" + } + + It "Verify defaults for light theme" { + $options = Get-MarkdownOption + + $options.Header1 | Should -BeExactly "$esc[7m[7m$esc[0m" + $options.Header2 | Should -BeExactly "$esc[4;33m[4;33m$esc[0m" + $options.Header3 | Should -BeExactly "$esc[4;34m[4;34m$esc[0m" + $options.Header4 | Should -BeExactly "$esc[4;35m[4;35m$esc[0m" + $options.Header5 | Should -BeExactly "$esc[4;36m[4;36m$esc[0m" + $options.Header6 | Should -BeExactly "$esc[4;30m[4;30m$esc[0m" + $options.Code | Should -BeExactly "$esc[48;2;155;155;155;38;2;30;30;30m[48;2;155;155;155;38;2;30;30;30m$esc[0m" + $options.Link | Should -BeExactly "$esc[4;38;5;117m[4;38;5;117m$esc[0m" + $options.Image | Should -BeExactly "$esc[33m[33m$esc[0m" + $options.EmphasisBold | Should -BeExactly "$esc[1m[1m$esc[0m" + $options.EmphasisItalics | Should -BeExactly "$esc[36m[36m$esc[0m" + } + } + } From 481d3b1ebf4357c1f5cc39da65cbffacd8541907 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Tue, 10 Jul 2018 16:12:00 -0700 Subject: [PATCH 04/12] [Feature] Add more tests --- .../commands/utility/ShowMarkdownCommand.cs | 26 +++++++++- .../MarkdownCmdlets.Tests.ps1 | 48 +++++++++++++++---- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index bd37f41fb1..6a6839b22f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -59,7 +59,7 @@ namespace Microsoft.PowerShell.Commands Object inpObj = InputObject.BaseObject; var markdownInfo = inpObj as MarkdownInfo; - if (markdownInfo != null) + if (markdownInfo == null) { var errorRecord = new ErrorRecord( new ArgumentException(), @@ -83,6 +83,12 @@ namespace Microsoft.PowerShell.Commands writer.Write(html); } + if(OutputBypassTestHook) + { + WriteObject(html); + return; + } + ProcessStartInfo startInfo = new ProcessStartInfo(); #if UNIX @@ -112,6 +118,12 @@ namespace Microsoft.PowerShell.Commands if(!String.IsNullOrEmpty(vt100String)) { + if(OutputBypassTestHook) + { + WriteObject(vt100String); + return; + } + if(stepPipe != null) { stepPipe.Process(vt100String); @@ -132,6 +144,7 @@ namespace Microsoft.PowerShell.Commands } /// + /// Override EndProcessing. /// protected override void EndProcessing() { @@ -140,5 +153,16 @@ namespace Microsoft.PowerShell.Commands stepPipe.End(); } } + + private static bool OutputBypassTestHook = false; + + /// + /// Test hook to enable or disable launching of browser. + /// When set, the converted output is returned. + /// + public static void SetOutputBypassTestHook(bool value) + { + OutputBypassTestHook = value; + } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 index 6877136b09..551eb5bf94 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -259,20 +259,21 @@ bool function()`n{`n} $newOptions = Get-MarkdownOption - $options.Header1 | Should -BeExactly "$esc[4;1m[4;1m$esc[0m" - $options.Header2 | Should -BeExactly "$esc[93m[93m$esc[0m" - $options.Header3 | Should -BeExactly "$esc[94m[94m$esc[0m" - $options.Header4 | Should -BeExactly "$esc[95m[95m$esc[0m" - $options.Header5 | Should -BeExactly "$esc[96m[96m$esc[0m" - $options.Header6 | Should -BeExactly "$esc[97m[97m$esc[0m" + $newOptions.Header1 | Should -BeExactly "$esc[4;1m[4;1m$esc[0m" + $newOptions.Header2 | Should -BeExactly "$esc[93m[93m$esc[0m" + $newOptions.Header3 | Should -BeExactly "$esc[94m[94m$esc[0m" + $newOptions.Header4 | Should -BeExactly "$esc[95m[95m$esc[0m" + $newOptions.Header5 | Should -BeExactly "$esc[96m[96m$esc[0m" + $newOptions.Header6 | Should -BeExactly "$esc[97m[97m$esc[0m" #$options.Code | Should -BeExactly "$esc[48;2;155;155;155;38;2;30;30;30m[48;2;155;155;155;38;2;30;30;30m$esc[0m" - $options.Link | Should -BeExactly "$esc[4;38;5;88m[4;38;5;88m$esc[0m" - $options.Image | Should -BeExactly "$esc[34m[34m$esc[0m" - $options.EmphasisBold | Should -BeExactly "$esc[32m[32m$esc[0m" - $options.EmphasisItalics | Should -BeExactly "$esc[35m[35m$esc[0m" + $newOptions.Link | Should -BeExactly "$esc[4;38;5;88m[4;38;5;88m$esc[0m" + $newOptions.Image | Should -BeExactly "$esc[34m[34m$esc[0m" + $newOptions.EmphasisBold | Should -BeExactly "$esc[32m[32m$esc[0m" + $newOptions.EmphasisItalics | Should -BeExactly "$esc[35m[35m$esc[0m" } It "Verify defaults for light theme" { + Set-MarkdownOption -Theme Light $options = Get-MarkdownOption $options.Header1 | Should -BeExactly "$esc[7m[7m$esc[0m" @@ -289,4 +290,31 @@ bool function()`n{`n} } } + Context "Show-Markdown tests" { + BeforeEach { + [Microsoft.PowerShell.Commands.ShowMarkdownCommand]::SetOutputBypassTestHook($true) + } + + AfterEach { + [Microsoft.PowerShell.Commands.ShowMarkdownCommand]::SetOutputBypassTestHook($false) + } + + It "can show VT100 converted from markdown" { + $text = "Bold" + $mdText = "**$text**" + $expectedString = GetExpectedString -ElementType 'Bold' -Text $text + + $result = $mdText | ConvertFrom-Markdown -AsVT100EncodedString | Show-Markdown + $result | Should -BeExactly $expectedString + } + + It "can show HTML converted from markdown" { + $text = "Bold" + $mdText = "**$text**" + $expectedString = GetExpectedHTML -ElementType 'Bold' -Text $text + + $result = $mdText | ConvertFrom-Markdown | Show-Markdown -UseBrowser + $result | Should -BeExactly $expectedString + } + } } From 7bf1604f97bbe14aab5926e635346456b4cc0dc0 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 11 Jul 2018 12:52:22 -0700 Subject: [PATCH 05/12] Codefactor style fixes --- .../utility/ConvertFromMarkdownCommand.cs | 47 +++--- .../utility/MarkdownOptionCommands.cs | 79 ++++----- .../commands/utility/ShowMarkdownCommand.cs | 39 ++--- .../CodeInlineRenderer.cs | 4 +- .../EmphasisInlineRenderer.cs | 4 +- .../FencedCodeBlockRenderer.cs | 6 +- .../HeaderBlockRenderer.cs | 4 +- .../LeafInlineRenderer.cs | 4 +- .../LineBreakRenderer.cs | 5 +- .../LinkInlineRenderer.cs | 4 +- .../ListBlockRenderer.cs | 4 +- .../ListItemBlockRenderer.cs | 16 +- .../MarkdownConverter.cs | 24 +-- .../ParagraphBlockRenderer.cs | 3 +- .../QuoteBlockRenderer.cs | 4 +- .../VT100EscapeSequences.cs | 159 ++++++++++-------- .../VT100ObjectRenderer.cs | 3 +- .../VT100Renderer.cs | 12 +- 18 files changed, 228 insertions(+), 193 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs index a2592d9bf1..b5698603a0 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -2,12 +2,12 @@ // Licensed under the MIT License. using System; -using System.IO; -using System.Threading.Tasks; -using System.Collections.ObjectModel; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; +using System.Threading.Tasks; using Microsoft.PowerShell.MarkdownRender; using Dbg = System.Management.Automation; @@ -20,36 +20,35 @@ namespace Microsoft.PowerShell.Commands [Cmdlet( VerbsData.ConvertFrom, "Markdown", DefaultParameterSetName = PathParameterSet, - HelpUri = "TBD" - )] + HelpUri = "TBD")] [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownInfo))] public class ConvertFromMarkdownCommand : PSCmdlet { /// - /// Path to the file to convert from Markdown to MarkdownInfo. + /// Gets or sets path to the file to convert from markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = PathParameterSet, Mandatory = true)] public string[] Path { get; set; } /// - /// Path to the file to convert from Markdown to MarkdownInfo. + /// Gets or sets the path to the file to convert from markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true)] public string[] LiteralPath { get; set; } /// - /// InputObject of type System.IO.FileInfo or string with content to convert from Markdown to MarkdownInfo. + /// Gets or sets the InputObject of type System.IO.FileInfo or string with content to convert from markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = InputObjParamSet, Mandatory = true, ValueFromPipeline = true)] public PSObject InputObject { get; set; } /// - /// The Markdown document should be converted to a VT100 encoded string. + /// Gets or sets if the markdown document should be converted to a VT100 encoded string. /// - [Parameter()] + [Parameter] public SwitchParameter AsVT100EncodedString { get; set; } private const string PathParameterSet = "PathParamSet"; @@ -59,13 +58,13 @@ namespace Microsoft.PowerShell.Commands private MarkdownOptionInfo mdOption = null; /// - /// Override BeginProcess. + /// Override BeginProcessing. /// protected override void BeginProcessing() { - mdOption = (SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo())) as MarkdownOptionInfo; + mdOption = SessionState.PSVariable.GetValue("MarkdownOptionInfo", new MarkdownOptionInfo()) as MarkdownOptionInfo; - if(mdOption == null) + if (mdOption == null) { throw new InvalidOperationException(); } @@ -84,18 +83,15 @@ namespace Microsoft.PowerShell.Commands switch (ParameterSetName) { case InputObjParamSet: - Object baseObj = InputObject.BaseObject; + object baseObj = InputObject.BaseObject; - //var fileInfo = baseObj as FileInfo; if (baseObj is FileInfo fileInfo) { WriteObject( MarkdownConverter.Convert( ReadContentFromFile(fileInfo.FullName).Result, conversionType, - mdOption - ) - ); + mdOption)); } else if (baseObj is string inpObj) { @@ -112,6 +108,7 @@ namespace Microsoft.PowerShell.Commands WriteError(errorRecord); } + break; case PathParameterSet: @@ -136,8 +133,7 @@ namespace Microsoft.PowerShell.Commands MarkdownConverter.Convert( ReadContentFromFile(resolvedPath).Result, conversionType, - optionInfo) - ); + optionInfo)); } } } @@ -151,7 +147,6 @@ namespace Microsoft.PowerShell.Commands string mdContent = await reader.ReadToEndAsync(); return mdContent; } - } private List ResolvePath(string path, bool isLiteral) @@ -186,10 +181,12 @@ namespace Microsoft.PowerShell.Commands if (!provider.Name.Equals("FileSystem", StringComparison.OrdinalIgnoreCase)) { string errorMessage = StringUtil.Format(ConvertMarkdownStrings.FileSystemPathsOnly, path); - ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(), - "OnlyFileSystemPathsSupported", - ErrorCategory.InvalidArgument, - path); + ErrorRecord errorRecord = new ErrorRecord( + new ArgumentException(), + "OnlyFileSystemPathsSupported", + ErrorCategory.InvalidArgument, + path); + WriteError(errorRecord); return null; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs index e2ce0bf931..2f7cbc631b 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs @@ -2,22 +2,22 @@ // Licensed under the MIT License. using System; -using System.IO; -using System.Threading.Tasks; -using System.Collections.ObjectModel; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; using System.Management.Automation; +using System.Threading.Tasks; using Microsoft.PowerShell.MarkdownRender; namespace Microsoft.PowerShell.Commands { /// + /// Class for implementing Set-MarkdownOption cmdlet. /// [Cmdlet( VerbsCommon.Set, "MarkdownOption", DefaultParameterSetName = IndividualSetting, - HelpUri = "TBD" - )] + HelpUri = "TBD")] [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownOptionInfo))] public class SetMarkdownOptionCommand : PSCmdlet { @@ -26,83 +26,83 @@ namespace Microsoft.PowerShell.Commands /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Header1Color { get; set;} + public string Header1Color { get; set; } /// /// Gets or sets the VT100 escape sequence for Header Level 2. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Header2Color { get; set;} + public string Header2Color { get; set; } /// /// Gets or sets the VT100 escape sequence for Header Level 3. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Header3Color { get; set;} + public string Header3Color { get; set; } /// /// Gets or sets the VT100 escape sequence for Header Level 4. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Header4Color { get; set;} + public string Header4Color { get; set; } /// /// Gets or sets the VT100 escape sequence for Header Level 5. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Header5Color { get; set;} + public string Header5Color { get; set; } /// /// Gets or sets the VT100 escape sequence for Header Level 6. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Header6Color { get; set;} + public string Header6Color { get; set; } /// /// Gets or sets the VT100 escape sequence for code block background. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string Code { get; set;} + public string Code { get; set; } /// /// Gets or sets the VT100 escape sequence for image alt text foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string ImageAltTextForegroundColor { get; set;} + public string ImageAltTextForegroundColor { get; set; } /// /// Gets or sets the VT100 escape sequence for link foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string LinkForegroundColor { get; set;} + public string LinkForegroundColor { get; set; } /// /// Gets or sets the VT100 escape sequence for italics text foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string ItalicsForegroundColor { get; set;} + public string ItalicsForegroundColor { get; set; } /// /// Gets or sets the VT100 escape sequence for bold text foreground. /// [ValidatePattern(@"^\[*[0-9;]*?m{1}")] [Parameter(ParameterSetName = IndividualSetting)] - public string BoldForegroundColor { get; set;} + public string BoldForegroundColor { get; set; } /// /// Gets or sets the switch to PassThru the values set. /// - [Parameter()] - public SwitchParameter PassThru { get; set;} + [Parameter] + public SwitchParameter PassThru { get; set; } /// /// Gets or sets the Theme. @@ -110,14 +110,14 @@ namespace Microsoft.PowerShell.Commands [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = ThemeParamSet, Mandatory = true)] [ValidateSet(DarkThemeName, LightThemeName)] - public string Theme { get; set;} + public string Theme { get; set; } /// /// Gets or sets InputObject. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = InputObjectParamSet, Mandatory = true, ValueFromPipeline = true)] - public PSObject InputObject { get; set;} + public PSObject InputObject { get; set; } private const string IndividualSetting = "IndividualSetting"; private const string InputObjectParamSet = "InputObject"; @@ -133,28 +133,30 @@ namespace Microsoft.PowerShell.Commands { MarkdownOptionInfo mdOptionInfo = null; - switch(ParameterSetName) + switch (ParameterSetName) { case ThemeParamSet: mdOptionInfo = new MarkdownOptionInfo(); - if(string.Equals(Theme, LightThemeName, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(Theme, LightThemeName, StringComparison.OrdinalIgnoreCase)) { mdOptionInfo.SetLightTheme(); } - else if(string.Equals(Theme, DarkThemeName, StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(Theme, DarkThemeName, StringComparison.OrdinalIgnoreCase)) { mdOptionInfo.SetDarkTheme(); } + break; case InputObjectParamSet: - Object baseObj = InputObject.BaseObject; + object baseObj = InputObject.BaseObject; mdOptionInfo = baseObj as MarkdownOptionInfo; - if(mdOptionInfo == null) + if (mdOptionInfo == null) { throw new ArgumentException(); } + break; case IndividualSetting: @@ -174,57 +176,57 @@ namespace Microsoft.PowerShell.Commands private void SetOptions(MarkdownOptionInfo mdOptionInfo) { - if (!String.IsNullOrEmpty(Header1Color)) + if (!string.IsNullOrEmpty(Header1Color)) { mdOptionInfo.Header1 = Header1Color; } - if (!String.IsNullOrEmpty(Header2Color)) + if (!string.IsNullOrEmpty(Header2Color)) { mdOptionInfo.Header2 = Header2Color; } - if (!String.IsNullOrEmpty(Header3Color)) + if (!string.IsNullOrEmpty(Header3Color)) { mdOptionInfo.Header3 = Header3Color; } - if (!String.IsNullOrEmpty(Header4Color)) + if (!string.IsNullOrEmpty(Header4Color)) { mdOptionInfo.Header4 = Header4Color; } - if (!String.IsNullOrEmpty(Header5Color)) + if (!string.IsNullOrEmpty(Header5Color)) { mdOptionInfo.Header5 = Header5Color; } - if (!String.IsNullOrEmpty(Header6Color)) + if (!string.IsNullOrEmpty(Header6Color)) { mdOptionInfo.Header6 = Header6Color; } - if (!String.IsNullOrEmpty(Code)) + if (!string.IsNullOrEmpty(Code)) { mdOptionInfo.Code = Code; } - if (!String.IsNullOrEmpty(ImageAltTextForegroundColor)) + if (!string.IsNullOrEmpty(ImageAltTextForegroundColor)) { mdOptionInfo.Image = ImageAltTextForegroundColor; } - if (!String.IsNullOrEmpty(LinkForegroundColor)) + if (!string.IsNullOrEmpty(LinkForegroundColor)) { mdOptionInfo.Link = LinkForegroundColor; } - if (!String.IsNullOrEmpty(ItalicsForegroundColor)) + if (!string.IsNullOrEmpty(ItalicsForegroundColor)) { mdOptionInfo.EmphasisItalics = ItalicsForegroundColor; } - if (!String.IsNullOrEmpty(BoldForegroundColor)) + if (!string.IsNullOrEmpty(BoldForegroundColor)) { mdOptionInfo.EmphasisBold = BoldForegroundColor; } @@ -236,15 +238,14 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet( VerbsCommon.Get, "MarkdownOption", - HelpUri = "TBD" - )] + HelpUri = "TBD")] [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.MarkdownOptionInfo))] public class GetMarkdownOptionCommand : PSCmdlet { private const string MarkdownOptionInfoVariableName = "MarkdownOptionInfo"; /// - /// Override endproessing. + /// Override EndProcessing. /// protected override void EndProcessing() { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index 6a6839b22f..d2d4601698 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -2,11 +2,11 @@ // Licensed under the MIT License. using System; -using System.IO; using System.Collections; -using System.Collections.ObjectModel; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; +using System.IO; using System.Management.Automation; using Microsoft.PowerShell.MarkdownRender; @@ -19,31 +19,31 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet( VerbsCommon.Show, "Markdown", - HelpUri = "TBD" - )] + HelpUri = "TBD")] [OutputType(typeof(string))] public class ShowMarkdownCommand : PSCmdlet { /// - /// InputObject of type Microsoft.PowerShell.MarkdownRender.MarkdownInfo to display + /// Gets or sets InputObject of type Microsoft.PowerShell.MarkdownRender.MarkdownInfo to display. /// [ValidateNotNullOrEmpty] [Parameter(Mandatory = true, ValueFromPipeline = true)] public PSObject InputObject { get; set; } /// - /// Switch to view Html in default browser. + /// Gets or sets the switch to view Html in default browser. /// - [Parameter()] + [Parameter] public SwitchParameter UseBrowser { get; set; } private SteppablePipeline stepPipe; /// + /// Override BeginProcessing. /// protected override void BeginProcessing() { - if(! this.MyInvocation.BoundParameters.ContainsKey("UseBrowser")) + if (!this.MyInvocation.BoundParameters.ContainsKey("UseBrowser")) { // Since UseBrowser is not bound, we use proxy to Out-Default stepPipe = ScriptBlock.Create(@"Microsoft.PowerShell.Core\Out-Default @PSBoundParameters").GetSteppablePipeline(this.MyInvocation.CommandOrigin); @@ -52,11 +52,11 @@ namespace Microsoft.PowerShell.Commands } /// - /// Override ProcessRecord + /// Override ProcessRecord. /// protected override void ProcessRecord() { - Object inpObj = InputObject.BaseObject; + object inpObj = InputObject.BaseObject; var markdownInfo = inpObj as MarkdownInfo; if (markdownInfo == null) @@ -75,15 +75,15 @@ namespace Microsoft.PowerShell.Commands { var html = markdownInfo.Html; - if (!String.IsNullOrEmpty(html)) + if (!string.IsNullOrEmpty(html)) { - string tmpFilePath = Path.Combine(Path.GetTempPath(), (Guid.NewGuid().ToString() + ".html")); + string tmpFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".html"); using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) { writer.Write(html); } - if(OutputBypassTestHook) + if (outputBypassTestHook) { WriteObject(html); return; @@ -116,15 +116,15 @@ namespace Microsoft.PowerShell.Commands { var vt100String = markdownInfo.VT100EncodedString; - if(!String.IsNullOrEmpty(vt100String)) + if (!string.IsNullOrEmpty(vt100String)) { - if(OutputBypassTestHook) + if (outputBypassTestHook) { WriteObject(vt100String); return; } - if(stepPipe != null) + if (stepPipe != null) { stepPipe.Process(vt100String); } @@ -148,21 +148,22 @@ namespace Microsoft.PowerShell.Commands /// protected override void EndProcessing() { - if(stepPipe != null) + if (stepPipe != null) { stepPipe.End(); } } - private static bool OutputBypassTestHook = false; + private static bool outputBypassTestHook = false; /// /// Test hook to enable or disable launching of browser. /// When set, the converted output is returned. /// + /// True to enable test hook, false to disable. public static void SetOutputBypassTestHook(bool value) { - OutputBypassTestHook = value; + outputBypassTestHook = value; } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs index 35bf5e4c7d..ff755ac790 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/CodeInlineRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax.Inlines; using Markdig.Renderers; +using Markdig.Syntax.Inlines; namespace Microsoft.PowerShell.MarkdownRender { @@ -16,7 +16,7 @@ namespace Microsoft.PowerShell.MarkdownRender { protected override void Write(VT100Renderer renderer, CodeInline obj) { - renderer.Write(renderer.EscapeSequences.FormatCode(obj.Content , isInline: true)); + renderer.Write(renderer.EscapeSequences.FormatCode(obj.Content, isInline : true)); } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs index a09b58b63a..b7e6bffba2 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/EmphasisInlineRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax.Inlines; using Markdig.Renderers; +using Markdig.Syntax.Inlines; namespace Microsoft.PowerShell.MarkdownRender { @@ -16,7 +16,7 @@ namespace Microsoft.PowerShell.MarkdownRender { protected override void Write(VT100Renderer renderer, EmphasisInline obj) { - renderer.Write(renderer.EscapeSequences.FormatEmphasis(obj.FirstChild.ToString() , isBold: obj.IsDouble ? true : false )); + renderer.Write(renderer.EscapeSequences.FormatEmphasis(obj.FirstChild.ToString(), isBold : obj.IsDouble ? true : false)); } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs index f7f4493b45..fcbdc22d6f 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -18,11 +18,11 @@ namespace Microsoft.PowerShell.MarkdownRender { foreach (var codeLine in obj.Lines.Lines) { - if (!String.IsNullOrWhiteSpace(codeLine.ToString())) + if (!string.IsNullOrWhiteSpace(codeLine.ToString())) { // If the code block is of type YAML, then tab to right to improve readability. // This specifically helps for parameters help content. - if (String.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) { renderer.WriteLine("\t" + codeLine.ToString()); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs index 29bfb76eb7..4aa78c6aea 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -17,7 +17,7 @@ namespace Microsoft.PowerShell.MarkdownRender protected override void Write(VT100Renderer renderer, HeadingBlock obj) { // Format header and then add blank line to improve readability. - switch(obj.Level) + switch (obj.Level) { case 1: renderer.WriteLine(renderer.EscapeSequences.FormatHeader1(obj.Inline.FirstChild.ToString())); diff --git a/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs index ff2a202bed..e59c108998 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/LeafInlineRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax.Inlines; using Markdig.Renderers; +using Markdig.Syntax.Inlines; namespace Microsoft.PowerShell.MarkdownRender { @@ -19,7 +19,7 @@ namespace Microsoft.PowerShell.MarkdownRender // If the next sibling is null, then this is the last line in the paragraph. // Add new line character at the end. // Else just write without newline at the end. - if(obj.NextSibling == null) + if (obj.NextSibling == null) { renderer.WriteLine(obj.ToString()); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs index 2f5a630306..81e787d2ab 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/LineBreakRenderer.cs @@ -4,12 +4,11 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax.Inlines; using Markdig.Renderers; +using Markdig.Syntax.Inlines; namespace Microsoft.PowerShell.MarkdownRender { - /// /// Renderer for adding VT100 escape sequences for line breaks. /// @@ -19,7 +18,7 @@ namespace Microsoft.PowerShell.MarkdownRender { // If it is a hard line break add new line at the end. // Else, add a space for after the last character to improve readability. - if(obj.IsHard) + if (obj.IsHard) { renderer.WriteLine(); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs index 2028b7488d..975efd519f 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax.Inlines; using Markdig.Renderers; +using Markdig.Syntax.Inlines; namespace Microsoft.PowerShell.MarkdownRender { @@ -17,7 +17,7 @@ namespace Microsoft.PowerShell.MarkdownRender protected override void Write(VT100Renderer renderer, LinkInline obj) { // Format link as image or link. - if(obj.IsImage) + if (obj.IsImage) { renderer.Write(renderer.EscapeSequences.FormatImage(obj.FirstChild.ToString())); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs index 5ee5dd5da8..d1f96a274a 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ListBlockRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -42,7 +42,7 @@ namespace Microsoft.PowerShell.MarkdownRender // For a numbered list, we need to make sure the index is incremented. foreach (var line in block) { - if(line is ParagraphBlock paragraphBlock) + if (line is ParagraphBlock paragraphBlock) { renderer.Write(index.ToString()).Write(". ").Write(paragraphBlock.Inline); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs index d96b744d65..eaea89b5b3 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs @@ -5,8 +5,8 @@ using System; using System.IO; using System.Threading; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -38,20 +38,21 @@ namespace Microsoft.PowerShell.MarkdownRender var paragraphBlock = block as ParagraphBlock; - if(paragraphBlock != null) + if (paragraphBlock != null) { renderer.Write(indent).Write(listBullet).Write(" ").Write(paragraphBlock.Inline); } - else //If there is a sublist, the block is a ListBlock instead of ParagraphBlock. + else { + // If there is a sublist, the block is a ListBlock instead of ParagraphBlock. var subList = block as ListBlock; if (subList != null) { - foreach(var subListItem in subList) + foreach (var subListItem in subList) { var subListItemBlock = subListItem as ListItemBlock; - if(subListItemBlock != null) + if (subListItemBlock != null) { foreach (var line in subListItemBlock) { @@ -67,16 +68,19 @@ namespace Microsoft.PowerShell.MarkdownRender // Typical padding is at most a screen's width, any more than that and we won't bother caching. private const int IndentCacheMax = 120; private static readonly string[] IndentCache = new string[IndentCacheMax]; + internal static string Padding(int countOfSpaces) { if (countOfSpaces >= IndentCacheMax) + { return new string(' ', countOfSpaces); + } var result = IndentCache[countOfSpaces]; if (result == null) { - Interlocked.CompareExchange(ref IndentCache[countOfSpaces], new string(' ', countOfSpaces), comparand:null); + Interlocked.CompareExchange(ref IndentCache[countOfSpaces], new string(' ', countOfSpaces), comparand : null); result = IndentCache[countOfSpaces]; } diff --git a/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs b/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs index 0ab0bf5412..a1041e7978 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -32,17 +32,17 @@ namespace Microsoft.PowerShell.MarkdownRender public class MarkdownInfo { /// - /// Html content after conversion. + /// Gets the Html content after conversion. /// - public string Html { get; internal set;} + public string Html { get; internal set; } /// - /// VT100 encoded string after conversion. + /// Gets the VT100 encoded string after conversion. /// - public string VT100EncodedString { get; internal set;} + public string VT100EncodedString { get; internal set; } /// - /// AST of the markdown string. + /// Gets the AST of the markdown string. /// public Markdig.Syntax.MarkdownDocument Tokens { get; internal set; } } @@ -55,25 +55,27 @@ namespace Microsoft.PowerShell.MarkdownRender /// /// Convert from markdown string to VT100 encoded string or HTML. Returns MarkdownInfo object. /// - /// string with markdown content to be converted - /// specifies type of conversion, either VT100 or HTML - /// specifies the rendering options for VT100 rendering + /// String with markdown content to be converted. + /// Specifies type of conversion, either VT100 or HTML. + /// Specifies the rendering options for VT100 rendering. + /// MarkdownInfo object with the converted output. public static MarkdownInfo Convert(string markdownString, MarkdownConversionType conversionType, MarkdownOptionInfo optionInfo) { var renderInfo = new MarkdownInfo(); var writer = new StringWriter(); MarkdownPipeline pipeline = null; - if(conversionType.HasFlag(MarkdownConversionType.HTML)) + if (conversionType.HasFlag(MarkdownConversionType.HTML)) { pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); var renderer = new Markdig.Renderers.HtmlRenderer(writer); renderInfo.Html = Markdig.Markdown.Convert(markdownString, renderer, pipeline).ToString(); } - if(conversionType.HasFlag(MarkdownConversionType.VT100)) + if (conversionType.HasFlag(MarkdownConversionType.VT100)) { pipeline = new MarkdownPipelineBuilder().Build(); + // Use the VT100 renderer. var renderer = new VT100Renderer(writer, optionInfo); renderInfo.VT100EncodedString = Markdig.Markdown.Convert(markdownString, renderer, pipeline).ToString(); diff --git a/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs index fa7043d07f..2e7eec16cb 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ParagraphBlockRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -18,6 +18,7 @@ namespace Microsoft.PowerShell.MarkdownRender { // Call the renderer for children, leaf inline or line breaks. renderer.WriteChildren(obj.Inline); + // Add new line at the end of the paragraph. renderer.WriteLine(); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs index a7e9217a72..f22f2ae8a4 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/QuoteBlockRenderer.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -17,7 +17,7 @@ namespace Microsoft.PowerShell.MarkdownRender protected override void Write(VT100Renderer renderer, QuoteBlock obj) { // Iterate through each item and add the quote character before the content. - foreach(var item in obj) + foreach (var item in obj) { renderer.Write(obj.QuoteChar).Write(" ").Write(item); } diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs index 360221bed3..32e9731af3 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs @@ -4,8 +4,8 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { @@ -14,60 +14,60 @@ namespace Microsoft.PowerShell.MarkdownRender /// public sealed class MarkdownOptionInfo { - private const char Esc = (char) 0x1b; + private const char Esc = (char)0x1b; /// - /// Current VT100 escape sequence for header 1. + /// Gets or sets current VT100 escape sequence for header 1. /// public string Header1 { get; set; } /// - /// Current VT100 escape sequence for header 2. + /// Gets or sets current VT100 escape sequence for header 2. /// public string Header2 { get; set; } /// - /// Current VT100 escape sequence for header 3. + /// Gets or sets current VT100 escape sequence for header 3. /// public string Header3 { get; set; } /// - /// Current VT100 escape sequence for header 4. + /// Gets or sets current VT100 escape sequence for header 4. /// public string Header4 { get; set; } /// - /// Current VT100 escape sequence for header 5. + /// Gets or sets current VT100 escape sequence for header 5. /// public string Header5 { get; set; } /// - /// Current VT100 escape sequence for header 6. + /// Gets or sets current VT100 escape sequence for header 6. /// public string Header6 { get; set; } /// - /// Current VT100 escape sequence for code inline and code blocks. + /// Gets or sets current VT100 escape sequence for code inline and code blocks. /// public string Code { get; set; } /// - /// Current VT100 escape sequence for links. + /// Gets or sets current VT100 escape sequence for links. /// public string Link { get; set; } /// - /// Current VT100 escape sequence for images. + /// Gets or sets current VT100 escape sequence for images. /// public string Image { get; set; } /// - /// Current VT100 escape sequence for bold text. + /// Gets or sets current VT100 escape sequence for bold text. /// public string EmphasisBold { get; set; } /// - /// Current VT100 escape sequence for italics text. + /// Gets or sets current VT100 escape sequence for italics text. /// public string EmphasisItalics { get; set; } @@ -75,11 +75,13 @@ namespace Microsoft.PowerShell.MarkdownRender /// Get the property as an rendered escape sequence. /// This is used for typesps1xml for displaying. /// + /// Name of the property to get as escape sequence. + /// Specified property name as escape sequence. public string AsEscapeSequence(string propertyName) { var propertyValue = this.GetType().GetProperty(propertyName)?.GetValue(this) as string; - if(!String.IsNullOrEmpty(propertyValue)) + if (!string.IsNullOrEmpty(propertyValue)) { return string.Concat(Esc, propertyValue, propertyValue, Esc, "[0m"); } @@ -90,7 +92,7 @@ namespace Microsoft.PowerShell.MarkdownRender } /// - /// Set dark as the default theme. + /// Initializes a new instance of class and sets dark as the default theme. /// public MarkdownOptionInfo() { @@ -134,23 +136,24 @@ namespace Microsoft.PowerShell.MarkdownRender } } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// public class VT100EscapeSequences { - private const char Esc = (char) 0x1B; + private const char Esc = (char)0x1B; - private string EndSequence = Esc + "[0m"; + private string endSequence = Esc + "[0m"; private MarkdownOptionInfo options; /// - /// Set the options as per + /// Initializes a new instance of the class. /// + /// MarkdownOptionInfo object to initialize with. public VT100EscapeSequences(MarkdownOptionInfo optionInfo) { - if(optionInfo == null) + if (optionInfo == null) { throw new ArgumentNullException("optionInfo"); } @@ -158,100 +161,124 @@ namespace Microsoft.PowerShell.MarkdownRender options = optionInfo; } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the header to format. + /// Formatted Header 1 string. public string FormatHeader1(string headerText) { - return String.Concat(Esc, options.Header1, headerText, EndSequence); + return string.Concat(Esc, options.Header1, headerText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the header to format. + /// Formatted Header 2 string. public string FormatHeader2(string headerText) { - return String.Concat(Esc, options.Header2, headerText, EndSequence); + return string.Concat(Esc, options.Header2, headerText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the header to format. + /// Formatted Header 3 string. public string FormatHeader3(string headerText) { - return String.Concat(Esc, options.Header3, headerText, EndSequence); + return string.Concat(Esc, options.Header3, headerText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the header to format. + /// Formatted Header 4 string. public string FormatHeader4(string headerText) { - return String.Concat(Esc, options.Header4, headerText, EndSequence); + return string.Concat(Esc, options.Header4, headerText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the header to format. + /// Formatted Header 5 string. public string FormatHeader5(string headerText) { - return String.Concat(Esc, options.Header5, headerText, EndSequence); + return string.Concat(Esc, options.Header5, headerText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the header to format. + /// Formatted Header 6 string. public string FormatHeader6(string headerText) { - return String.Concat(Esc, options.Header6, headerText, EndSequence); + return string.Concat(Esc, options.Header6, headerText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the code block to format. + /// True if it is a inline code block, false otherwise. + /// Formatted code block string. public string FormatCode(string codeText, bool isInline) { - if(isInline) + if (isInline) { - return String.Concat(Esc, options.Code, codeText, EndSequence); + return string.Concat(Esc, options.Code, codeText, endSequence); } else { // For code blocks, [500@ make sure that the whole line has background color. - return String.Concat(Esc, options.Code, codeText, Esc, "[500@", EndSequence); + return string.Concat(Esc, options.Code, codeText, Esc, "[500@", endSequence); } } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the link to format. + /// URL of the link. + /// True url should be hidden, false otherwise. Default is true. + /// Formatted link string. public string FormatLink(string linkText, string url, bool hideUrl = true) { - if(hideUrl) + if (hideUrl) { - return String.Concat(Esc, options.Link, "\"", linkText, "\"", EndSequence); + return string.Concat(Esc, options.Link, "\"", linkText, "\"", endSequence); } else { - return String.Concat("\"", linkText, "\" (", Esc, options.Link, url, EndSequence, ")"); + return string.Concat("\"", linkText, "\" (", Esc, options.Link, url, endSequence, ")"); } } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text to format as emphasis. + /// True if it is to be formatted as bold, false to format it as italics. + /// Formatted emphasis string. public string FormatEmphasis(string emphasisText, bool isBold) { var sequence = isBold ? options.EmphasisBold : options.EmphasisItalics; - return String.Concat(Esc, sequence, emphasisText, EndSequence); + return string.Concat(Esc, sequence, emphasisText, endSequence); } - /// - /// Class to represent default VT100 escape sequences - /// + /// + /// Class to represent default VT100 escape sequences. + /// + /// Text of the image to format. + /// Formatted image string. public string FormatImage(string altText) { - return String.Concat(Esc, options.Image, "[", altText, "]", EndSequence); + return string.Concat(Esc, options.Image, "[", altText, "]", endSequence); } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs index 1cfd1bb04a..47373a50e1 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100ObjectRenderer.cs @@ -4,14 +4,15 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { /// /// Implement the MarkdownObjectRenderer with VT100Renderer. /// + /// The element type of the renderer. public abstract class VT100ObjectRenderer : MarkdownObjectRenderer where T : MarkdownObject { } diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs index ff085b06f8..59b8234d9c 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100Renderer.cs @@ -4,19 +4,21 @@ using System; using System.IO; using Markdig; -using Markdig.Syntax; using Markdig.Renderers; +using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { /// - /// Implementation of the VT100 renderer. + /// Initializes an instance of the VT100 renderer. /// public sealed class VT100Renderer : TextRendererBase { /// - /// Initialize the VT100 renderer with and write the output . + /// Initializes a new instance of the class. /// + /// TextWriter to write to. + /// MarkdownOptionInfo object with options. public VT100Renderer(TextWriter writer, MarkdownOptionInfo optionInfo) : base(writer) { EscapeSequences = new VT100EscapeSequences(optionInfo); @@ -36,8 +38,8 @@ namespace Microsoft.PowerShell.MarkdownRender } /// - /// Get the current escape sequences. + /// Gets the current escape sequences. /// - public VT100EscapeSequences EscapeSequences { get; private set;} + public VT100EscapeSequences EscapeSequences { get; private set; } } } From a78d006ac1e48d7a17a6b5ddc3bcf3b2f06c90c4 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 11 Jul 2018 15:39:36 -0700 Subject: [PATCH 06/12] Added missing error messages --- .../commands/utility/ShowMarkdownCommand.cs | 17 +++++++---------- .../resources/ConvertMarkdownStrings.resx | 3 +++ .../MarkdownCmdlets.Tests.ps1 | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index d2d4601698..34a58a96cb 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -8,6 +8,7 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Management.Automation; +using System.Management.Automation.Internal; using Microsoft.PowerShell.MarkdownRender; namespace Microsoft.PowerShell.Commands @@ -61,8 +62,9 @@ namespace Microsoft.PowerShell.Commands if (markdownInfo == null) { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, inpObj.GetType()); var errorRecord = new ErrorRecord( - new ArgumentException(), + new ArgumentException(errorMessage), "InvalidInputObject", ErrorCategory.InvalidArgument, InputObject); @@ -90,21 +92,15 @@ namespace Microsoft.PowerShell.Commands } ProcessStartInfo startInfo = new ProcessStartInfo(); - -#if UNIX - startInfo.FileName = Platform.IsLinux ? "xdg-open" : /* macOS */ "open"; - startInfo.Arguments = tmpFilePath; -#else startInfo.FileName = tmpFilePath; startInfo.UseShellExecute = true; -#endif - Process.Start(startInfo); } else { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "Html"); var errorRecord = new ErrorRecord( - new InvalidDataException(), + new InvalidDataException(errorMessage), "HtmlIsNullOrEmpty", ErrorCategory.InvalidData, html); @@ -131,8 +127,9 @@ namespace Microsoft.PowerShell.Commands } else { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "VT100EncodedString"); var errorRecord = new ErrorRecord( - new InvalidDataException(), + new InvalidDataException(errorMessage), "VT100EncodedStringIsNullOrEmpty", ErrorCategory.InvalidData, vt100String); diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx index f9aacc5d6f..3b17b564f5 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -126,4 +126,7 @@ Only FileSystem Provider paths are supported. The given path '{0}' is not supported. + + The property {0} of the given object is null or empty. + diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 index 551eb5bf94..8764b340db 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -316,5 +316,20 @@ bool function()`n{`n} $result = $mdText | ConvertFrom-Markdown | Show-Markdown -UseBrowser $result | Should -BeExactly $expectedString } + + It "Gets an error if the input object is missing the property." -TestCases @(@{propertyname = 'Html'}, @{propertyname = 'VT100EncodedString'}) { + param($propertyname) + + $markdownInfo = [Microsoft.PowerShell.MarkdownRender.MarkdownInfo]::new() + + if($propertyname -eq 'VT100EncodedString') + { + { Show-Markdown -InputObject $markdownInfo -ErrorAction Stop } | Should -Throw -ErrorId 'VT100EncodedStringIsNullOrEmpty,Microsoft.PowerShell.Commands.ShowMarkdownCommand' + } + else + { + { Show-Markdown -UseBrowser -InputObject $markdownInfo -ErrorAction Stop } | Should -Throw -ErrorId 'HtmlIsNullOrEmpty,Microsoft.PowerShell.Commands.ShowMarkdownCommand' + } + } } } From 731afbf14d9e3af9a2eb251d919b8b7b9f8f4eaf Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 11 Jul 2018 15:53:44 -0700 Subject: [PATCH 07/12] [Feature] Add markdown cmdlets to default commands list --- test/powershell/engine/Basic/DefaultCommands.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index 0fcdef96c7..84043c93a0 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -191,6 +191,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Connect-WSMan", , $($FullCLR -or $CoreWindows ) "Cmdlet", "ConvertFrom-Csv", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "ConvertFrom-Json", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "ConvertFrom-Markdown", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "ConvertFrom-SecureString", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "ConvertFrom-String", , $($FullCLR ) "Cmdlet", "ConvertFrom-StringData", , $($FullCLR -or $CoreWindows -or $CoreUnix) @@ -270,6 +271,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Get-ItemPropertyValue", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Job", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Location", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "Get-MarkdownOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Member", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Module", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PfxCertificate", , $($FullCLR -or $CoreWindows -or $CoreUnix) @@ -408,6 +410,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Set-Item", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-ItemProperty", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-Location", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "Set-MarkdownOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSBreakpoint", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSDebug", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSSessionConfiguration", , $($FullCLR -or $CoreWindows ) @@ -422,6 +425,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Show-Command", , $($FullCLR ) "Cmdlet", "Show-ControlPanelItem", , $($FullCLR ) "Cmdlet", "Show-EventLog", , $($FullCLR ) +"Cmdlet", "Show-Markdown", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Sort-Object", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Split-Path", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Start-Job", , $($FullCLR -or $CoreWindows -or $CoreUnix) From e711c5dcc35034570d5e8416d9741d6e7bffe05e Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Thu, 12 Jul 2018 09:00:52 -0700 Subject: [PATCH 08/12] [Feature] Address code review comments --- .../utility/ConvertFromMarkdownCommand.cs | 46 +++++-- .../utility/MarkdownOptionCommands.cs | 9 +- .../commands/utility/ShowMarkdownCommand.cs | 85 +++++++----- .../resources/ConvertMarkdownStrings.resx | 4 +- .../FencedCodeBlockRenderer.cs | 2 +- .../ListItemBlockRenderer.cs | 11 +- ...Microsoft.PowerShell.MarkdownRender.csproj | 16 --- .../VT100EscapeSequences.cs | 130 +++++++++++++----- .../engine/Utils.cs | 2 + .../MarkdownCmdlets.Tests.ps1 | 8 +- .../engine/Basic/DefaultCommands.Tests.ps1 | 8 +- 11 files changed, 212 insertions(+), 109 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs index b5698603a0..d1052cfc42 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -8,6 +8,7 @@ using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; using System.Threading.Tasks; +using System.Security; using Microsoft.PowerShell.MarkdownRender; using Dbg = System.Management.Automation; @@ -58,7 +59,7 @@ namespace Microsoft.PowerShell.Commands private MarkdownOptionInfo mdOption = null; /// - /// Override BeginProcessing. + /// Read the MarkdownOptionInfo set in SessionState. /// protected override void BeginProcessing() { @@ -89,7 +90,7 @@ namespace Microsoft.PowerShell.Commands { WriteObject( MarkdownConverter.Convert( - ReadContentFromFile(fileInfo.FullName).Result, + ReadContentFromFile(fileInfo.FullName)?.Result, conversionType, mdOption)); } @@ -131,7 +132,7 @@ namespace Microsoft.PowerShell.Commands { WriteObject( MarkdownConverter.Convert( - ReadContentFromFile(resolvedPath).Result, + ReadContentFromFile(resolvedPath)?.Result, conversionType, optionInfo)); } @@ -140,13 +141,43 @@ namespace Microsoft.PowerShell.Commands private async Task ReadContentFromFile(string filePath) { - Dbg.Diagnostics.Assert(File.Exists(filePath), "Caller should make sure the file exists."); + ErrorRecord errorRecord = null; - using (StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))) + try { - string mdContent = await reader.ReadToEndAsync(); - return mdContent; + using (StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))) + { + string mdContent = await reader.ReadToEndAsync(); + return mdContent; + } } + catch(FileNotFoundException fnfe) + { + errorRecord = new ErrorRecord( + fnfe, + "FileNotFound", + ErrorCategory.ResourceUnavailable, + filePath); + } + catch(SecurityException se) + { + errorRecord = new ErrorRecord( + se, + "FileSecurityError", + ErrorCategory.SecurityError, + filePath); + } + catch(UnauthorizedAccessException uae) + { + errorRecord = new ErrorRecord( + uae, + "FileUnauthorizedAccess", + ErrorCategory.SecurityError, + filePath); + } + + WriteError(errorRecord); + return null; } private List ResolvePath(string path, bool isLiteral) @@ -168,7 +199,6 @@ namespace Microsoft.PowerShell.Commands } catch (ItemNotFoundException infe) { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InputFileNotFound, path); var errorRecord = new ErrorRecord( infe, "FileNotFound", diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs index 2f7cbc631b..4c849a8be4 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Management.Automation; +using System.Management.Automation.Internal; using System.Threading.Tasks; using Microsoft.PowerShell.MarkdownRender; @@ -154,7 +155,13 @@ namespace Microsoft.PowerShell.Commands if (mdOptionInfo == null) { - throw new ArgumentException(); + var errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, baseObj.GetType()); + + ErrorRecord errorRecord = new ErrorRecord( + new ArgumentException(errorMessage), + "InvalidObject", + ErrorCategory.InvalidArgument, + InputObject); } break; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index 34a58a96cb..c44cea4b50 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -44,7 +44,8 @@ namespace Microsoft.PowerShell.Commands /// protected override void BeginProcessing() { - if (!this.MyInvocation.BoundParameters.ContainsKey("UseBrowser")) + //if (!this.MyInvocation.BoundParameters.ContainsKey("UseBrowser")) + if(!UseBrowser.IsPresent) { // Since UseBrowser is not bound, we use proxy to Out-Default stepPipe = ScriptBlock.Create(@"Microsoft.PowerShell.Core\Out-Default @PSBoundParameters").GetSteppablePipeline(this.MyInvocation.CommandOrigin); @@ -58,20 +59,8 @@ namespace Microsoft.PowerShell.Commands protected override void ProcessRecord() { object inpObj = InputObject.BaseObject; - var markdownInfo = inpObj as MarkdownInfo; - if (markdownInfo == null) - { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, inpObj.GetType()); - var errorRecord = new ErrorRecord( - new ArgumentException(errorMessage), - "InvalidInputObject", - ErrorCategory.InvalidArgument, - InputObject); - - WriteError(errorRecord); - } - else + if(inpObj is MarkdownInfo markdownInfo) { if (UseBrowser) { @@ -80,21 +69,50 @@ namespace Microsoft.PowerShell.Commands if (!string.IsNullOrEmpty(html)) { string tmpFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".html"); - using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) + + try { - writer.Write(html); + using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) + { + writer.Write(html); + } + } + catch (Exception e) + { + var errorRecord = new ErrorRecord( + e, + "ErrorWritingTempFile", + ErrorCategory.WriteError, + tmpFilePath); + + WriteError(errorRecord); + return; } - if (outputBypassTestHook) + if (InternalTestHooks.ShowMarkdownOutputBypass) { WriteObject(html); return; } - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.FileName = tmpFilePath; - startInfo.UseShellExecute = true; - Process.Start(startInfo); + try + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.FileName = tmpFilePath; + startInfo.UseShellExecute = true; + Process.Start(startInfo); + } + catch (Exception e) + { + var errorRecord = new ErrorRecord( + e, + "ErrorLaunchingDefaultApplication", + ErrorCategory.InvalidOperation, + targetObject : null); + + WriteError(errorRecord); + return; + } } else { @@ -114,7 +132,7 @@ namespace Microsoft.PowerShell.Commands if (!string.IsNullOrEmpty(vt100String)) { - if (outputBypassTestHook) + if (InternalTestHooks.ShowMarkdownOutputBypass) { WriteObject(vt100String); return; @@ -138,6 +156,17 @@ namespace Microsoft.PowerShell.Commands } } } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, inpObj.GetType()); + var errorRecord = new ErrorRecord( + new ArgumentException(errorMessage), + "InvalidInputObject", + ErrorCategory.InvalidArgument, + InputObject); + + WriteError(errorRecord); + } } /// @@ -150,17 +179,5 @@ namespace Microsoft.PowerShell.Commands stepPipe.End(); } } - - private static bool outputBypassTestHook = false; - - /// - /// Test hook to enable or disable launching of browser. - /// When set, the converted output is returned. - /// - /// True to enable test hook, false to disable. - public static void SetOutputBypassTestHook(bool value) - { - outputBypassTestHook = value; - } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx index 3b17b564f5..14d745ee5f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -121,10 +121,10 @@ The type of the input object '{0}' is invalid. - The given file path '{0}' is not found. + The file is not found: '{0}'. - Only FileSystem Provider paths are supported. The given path '{0}' is not supported. + Only FileSystem Provider paths are supported. The file path is not supported: '{0}'. The property {0} of the given object is null or empty. diff --git a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs index fcbdc22d6f..8926f226a3 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs @@ -24,7 +24,7 @@ namespace Microsoft.PowerShell.MarkdownRender // This specifically helps for parameters help content. if (string.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) { - renderer.WriteLine("\t" + codeLine.ToString()); + renderer.Write("\t").WriteLine(codeLine.ToString()); } else { diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs index eaea89b5b3..cc9f38cdaa 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs @@ -17,9 +17,7 @@ namespace Microsoft.PowerShell.MarkdownRender { protected override void Write(VT100Renderer renderer, ListItemBlock obj) { - var parent = obj.Parent as ListBlock; - - if (parent != null) + if (obj.Parent is ListBlock parent) { if (!parent.IsOrdered) { @@ -36,17 +34,14 @@ namespace Microsoft.PowerShell.MarkdownRender // Indent left by 2 for each level on list. string indent = Padding(indentLevel * 2); - var paragraphBlock = block as ParagraphBlock; - - if (paragraphBlock != null) + if (block is ParagraphBlock paragraphBlock) { renderer.Write(indent).Write(listBullet).Write(" ").Write(paragraphBlock.Inline); } else { // If there is a sublist, the block is a ListBlock instead of ParagraphBlock. - var subList = block as ListBlock; - if (subList != null) + if (block is ListBlock subList) { foreach (var subListItem in subList) { diff --git a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj index bae34827c7..c00c90b959 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj +++ b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj @@ -6,22 +6,6 @@ Microsoft.PowerShell.MarkdownRender - - diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs index 32e9731af3..7b1f051ae0 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs @@ -15,6 +15,7 @@ namespace Microsoft.PowerShell.MarkdownRender public sealed class MarkdownOptionInfo { private const char Esc = (char)0x1b; + private const string EndSequence = "[0m"; /// /// Gets or sets current VT100 escape sequence for header 1. @@ -79,16 +80,48 @@ namespace Microsoft.PowerShell.MarkdownRender /// Specified property name as escape sequence. public string AsEscapeSequence(string propertyName) { - var propertyValue = this.GetType().GetProperty(propertyName)?.GetValue(this) as string; + var propName = propertyName?.ToLower(); - if (!string.IsNullOrEmpty(propertyValue)) + switch(propName) { - return string.Concat(Esc, propertyValue, propertyValue, Esc, "[0m"); - } - else - { - throw new InvalidOperationException(); + case "header1": + return string.Concat(Esc, Header1, Header1, Esc, EndSequence); + + case "header2": + return string.Concat(Esc, Header2, Header2, Esc, EndSequence); + + case "header3": + return string.Concat(Esc, Header3, Header3, Esc, EndSequence); + + case "header4": + return string.Concat(Esc, Header4, Header4, Esc, EndSequence); + + case "header5": + return string.Concat(Esc, Header5, Header5, Esc, EndSequence); + + case "header6": + return string.Concat(Esc, Header6, Header6, Esc, EndSequence); + + case "code": + return string.Concat(Esc, Code, Code, Esc, EndSequence); + + case "link": + return string.Concat(Esc, Link, Link, Esc, EndSequence); + + case "image": + return string.Concat(Esc, Image, Image, Esc, EndSequence); + + case "emphasisbold": + return string.Concat(Esc, EmphasisBold, EmphasisBold, Esc, EndSequence); + + case "emphasisitalics": + return string.Concat(Esc, EmphasisItalics, EmphasisItalics, Esc, EndSequence); + + default: + break; } + + return null; } /// @@ -99,22 +132,46 @@ namespace Microsoft.PowerShell.MarkdownRender SetDarkTheme(); } + private const string Header1Dark = "[7m"; + private const string Header2Dark = "[4;93m"; + private const string Header3Dark = "[4;94m"; + private const string Header4Dark = "[4;95m"; + private const string Header5Dark = "[4;96m"; + private const string Header6Dark = "[4;97m"; + private const string CodeDark = "[48;2;155;155;155;38;2;30;30;30m"; + private const string LinkDark = "[4;38;5;117m"; + private const string ImageDark = "[33m"; + private const string EmphasisBoldDark = "[1m"; + private const string EmphasisItalicsDark = "[36m"; + + private const string Header1Light = "[7m"; + private const string Header2Light = "[4;33m"; + private const string Header3Light = "[4;34m"; + private const string Header4Light = "[4;35m"; + private const string Header5Light = "[4;36m"; + private const string Header6Light = "[4;30m"; + private const string CodeLight = "[48;2;155;155;155;38;2;30;30;30m"; + private const string LinkLight = "[4;38;5;117m"; + private const string ImageLight = "[33m"; + private const string EmphasisBoldLight = "[1m"; + private const string EmphasisItalicsLight = "[36m"; + /// /// Set all preference for dark theme. /// public void SetDarkTheme() { - Header1 = "[7m"; - Header2 = "[4;93m"; - Header3 = "[4;94m"; - Header4 = "[4;95m"; - Header5 = "[4;96m"; - Header6 = "[4;97m"; - Code = "[48;2;155;155;155;38;2;30;30;30m"; - Link = "[4;38;5;117m"; - Image = "[33m"; - EmphasisBold = "[1m"; - EmphasisItalics = "[36m"; + Header1 = Header1Dark; + Header2 = Header2Dark; + Header3 = Header3Dark; + Header4 = Header4Dark; + Header5 = Header5Dark; + Header6 = Header6Dark; + Code = CodeDark; + Link = LinkDark; + Image = ImageDark; + EmphasisBold = EmphasisBoldDark; + EmphasisItalics = EmphasisItalicsDark; } /// @@ -122,17 +179,17 @@ namespace Microsoft.PowerShell.MarkdownRender /// public void SetLightTheme() { - Header1 = "[7m"; - Header2 = "[4;33m"; - Header3 = "[4;34m"; - Header4 = "[4;35m"; - Header5 = "[4;36m"; - Header6 = "[4;30m"; - Code = "[48;2;155;155;155;38;2;30;30;30m"; - Link = "[4;38;5;117m"; - Image = "[33m"; - EmphasisBold = "[1m"; - EmphasisItalics = "[36m"; + Header1 = Header1Light; + Header2 = Header2Light; + Header3 = Header3Light; + Header4 = Header4Light; + Header5 = Header5Light; + Header6 = Header6Light; + Code = CodeLight; + Link = LinkLight; + Image = ImageLight; + EmphasisBold = EmphasisBoldLight; + EmphasisItalics = EmphasisItalicsLight; } } @@ -142,9 +199,10 @@ namespace Microsoft.PowerShell.MarkdownRender public class VT100EscapeSequences { private const char Esc = (char)0x1B; - private string endSequence = Esc + "[0m"; + // For code blocks, [500@ make sure that the whole line has background color. + private const string LongBackgroundCodeBlock = "[500@"; private MarkdownOptionInfo options; /// @@ -235,8 +293,7 @@ namespace Microsoft.PowerShell.MarkdownRender } else { - // For code blocks, [500@ make sure that the whole line has background color. - return string.Concat(Esc, options.Code, codeText, Esc, "[500@", endSequence); + return string.Concat(Esc, options.Code, codeText, Esc, LongBackgroundCodeBlock, endSequence); } } @@ -278,7 +335,14 @@ namespace Microsoft.PowerShell.MarkdownRender /// Formatted image string. public string FormatImage(string altText) { - return string.Concat(Esc, options.Image, "[", altText, "]", endSequence); + var text = altText; + + if(string.IsNullOrEmpty(altText)) + { + text = "Image"; + } + + return string.Concat(Esc, options.Image, "[", text, "]", endSequence); } } } diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 2cc4b9602c..d17e04fd33 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1449,6 +1449,8 @@ namespace System.Management.Automation.Internal internal static bool StopwatchIsNotHighResolution; internal static bool DisableGACLoading; + internal static bool ShowMarkdownOutputBypass; + /// This member is used for internal test purposes. public static void SetTestHook(string property, object value) { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 index 8764b340db..ac568ba6aa 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -212,6 +212,10 @@ bool function()`n{`n} It "Gets an error if input object type is not correct" { { ConvertFrom-Markdown -InputObject 1 -ErrorAction Stop } | Should -Throw -ErrorId 'InvalidInputObject,Microsoft.PowerShell.Commands.ConvertFromMarkdownCommand' } + + It "Gets an error if input file does not exist" { + { [System.IO.FileInfo]::new("IDoNoExist") | ConvertFrom-Markdown -ErrorAction Stop } | Should -Throw -ErrorId 'FileNotFound,Microsoft.PowerShell.Commands.ConvertFromMarkdownCommand' + } } Context "Get/Set-MarkdownOption tests" { @@ -292,11 +296,11 @@ bool function()`n{`n} Context "Show-Markdown tests" { BeforeEach { - [Microsoft.PowerShell.Commands.ShowMarkdownCommand]::SetOutputBypassTestHook($true) + [System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("ShowMarkdownOutputBypass", $true) } AfterEach { - [Microsoft.PowerShell.Commands.ShowMarkdownCommand]::SetOutputBypassTestHook($false) + [System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("ShowMarkdownOutputBypass", $false) } It "can show VT100 converted from markdown" { diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index 84043c93a0..cc60c28641 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -191,7 +191,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Connect-WSMan", , $($FullCLR -or $CoreWindows ) "Cmdlet", "ConvertFrom-Csv", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "ConvertFrom-Json", , $($FullCLR -or $CoreWindows -or $CoreUnix) -"Cmdlet", "ConvertFrom-Markdown", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "ConvertFrom-Markdown", , $($CoreWindows -or $CoreUnix) "Cmdlet", "ConvertFrom-SecureString", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "ConvertFrom-String", , $($FullCLR ) "Cmdlet", "ConvertFrom-StringData", , $($FullCLR -or $CoreWindows -or $CoreUnix) @@ -271,7 +271,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Get-ItemPropertyValue", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Job", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Location", , $($FullCLR -or $CoreWindows -or $CoreUnix) -"Cmdlet", "Get-MarkdownOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "Get-MarkdownOption", , $($CoreWindows -or $CoreUnix) "Cmdlet", "Get-Member", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-Module", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PfxCertificate", , $($FullCLR -or $CoreWindows -or $CoreUnix) @@ -410,7 +410,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Set-Item", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-ItemProperty", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-Location", , $($FullCLR -or $CoreWindows -or $CoreUnix) -"Cmdlet", "Set-MarkdownOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "Set-MarkdownOption", , $($CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSBreakpoint", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSDebug", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSSessionConfiguration", , $($FullCLR -or $CoreWindows ) @@ -425,7 +425,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Show-Command", , $($FullCLR ) "Cmdlet", "Show-ControlPanelItem", , $($FullCLR ) "Cmdlet", "Show-EventLog", , $($FullCLR ) -"Cmdlet", "Show-Markdown", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "Show-Markdown", , $($CoreWindows -or $CoreUnix) "Cmdlet", "Sort-Object", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Split-Path", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Start-Job", , $($FullCLR -or $CoreWindows -or $CoreUnix) From 67b29151f67fdcfe04c720b52360cbff71a33352 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Thu, 12 Jul 2018 10:20:14 -0700 Subject: [PATCH 09/12] Style fixes --- .../commands/utility/ConvertFromMarkdownCommand.cs | 8 ++++---- .../commands/utility/ShowMarkdownCommand.cs | 5 ++--- .../VT100EscapeSequences.cs | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs index d1052cfc42..a30d9b7a78 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -7,8 +7,8 @@ using System.Collections.ObjectModel; using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Threading.Tasks; using System.Security; +using System.Threading.Tasks; using Microsoft.PowerShell.MarkdownRender; using Dbg = System.Management.Automation; @@ -151,7 +151,7 @@ namespace Microsoft.PowerShell.Commands return mdContent; } } - catch(FileNotFoundException fnfe) + catch (FileNotFoundException fnfe) { errorRecord = new ErrorRecord( fnfe, @@ -159,7 +159,7 @@ namespace Microsoft.PowerShell.Commands ErrorCategory.ResourceUnavailable, filePath); } - catch(SecurityException se) + catch (SecurityException se) { errorRecord = new ErrorRecord( se, @@ -167,7 +167,7 @@ namespace Microsoft.PowerShell.Commands ErrorCategory.SecurityError, filePath); } - catch(UnauthorizedAccessException uae) + catch (UnauthorizedAccessException uae) { errorRecord = new ErrorRecord( uae, diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index c44cea4b50..53b8338cb9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -44,8 +44,7 @@ namespace Microsoft.PowerShell.Commands /// protected override void BeginProcessing() { - //if (!this.MyInvocation.BoundParameters.ContainsKey("UseBrowser")) - if(!UseBrowser.IsPresent) + if (!UseBrowser.IsPresent) { // Since UseBrowser is not bound, we use proxy to Out-Default stepPipe = ScriptBlock.Create(@"Microsoft.PowerShell.Core\Out-Default @PSBoundParameters").GetSteppablePipeline(this.MyInvocation.CommandOrigin); @@ -60,7 +59,7 @@ namespace Microsoft.PowerShell.Commands { object inpObj = InputObject.BaseObject; - if(inpObj is MarkdownInfo markdownInfo) + if (inpObj is MarkdownInfo markdownInfo) { if (UseBrowser) { diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs index 7b1f051ae0..dbe128a9ac 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs @@ -82,7 +82,7 @@ namespace Microsoft.PowerShell.MarkdownRender { var propName = propertyName?.ToLower(); - switch(propName) + switch (propName) { case "header1": return string.Concat(Esc, Header1, Header1, Esc, EndSequence); @@ -337,7 +337,7 @@ namespace Microsoft.PowerShell.MarkdownRender { var text = altText; - if(string.IsNullOrEmpty(altText)) + if (string.IsNullOrEmpty(altText)) { text = "Image"; } From b5601cfb66cb54d804fdeb63abb05537a7ab9c66 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Thu, 12 Jul 2018 10:43:53 -0700 Subject: [PATCH 10/12] [Feature] Added wix component updates --- assets/files.wxs | 16 ++++++++++++++++ .../nativemsh/pwrshcommon/pwrshcommon.cpp | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/assets/files.wxs b/assets/files.wxs index 1f593873d2..0974decdef 100644 --- a/assets/files.wxs +++ b/assets/files.wxs @@ -1192,6 +1192,18 @@ + + + + + + + + + + + + @@ -2318,6 +2330,10 @@ + + + + diff --git a/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp b/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp index abb8c60ca1..79344c6bef 100644 --- a/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp +++ b/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp @@ -682,7 +682,6 @@ namespace NativeMsh "Microsoft.PowerShell.Commands.Utility", "Microsoft.PowerShell.ConsoleHost", "Microsoft.PowerShell.CoreCLR.Eventing", - "Microsoft.PowerShell.MarkdownRender", "Microsoft.PowerShell.SDK", "Microsoft.PowerShell.Security", "Microsoft.VisualBasic", From ce5f9f9af71dc62cf8e210e16c41620aa72850be Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Fri, 13 Jul 2018 11:18:14 -0700 Subject: [PATCH 11/12] [Feature] Fix comments and other code review comments --- .../commands/utility/ConvertFromMarkdownCommand.cs | 10 +++++----- .../commands/utility/MarkdownOptionCommands.cs | 2 +- .../ListItemBlockRenderer.cs | 4 +--- .../MarkdownConverter.cs | 12 ++++++------ .../VT100EscapeSequences.cs | 2 +- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs index a30d9b7a78..4ae70ac4fd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertFromMarkdownCommand.cs @@ -15,7 +15,7 @@ using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// Converts a markdown string to a MarkdownInfo object. + /// Converts a Markdown string to a MarkdownInfo object. /// The conversion can be done into a HTML text or VT100 encoding string. /// [Cmdlet( @@ -26,28 +26,28 @@ namespace Microsoft.PowerShell.Commands public class ConvertFromMarkdownCommand : PSCmdlet { /// - /// Gets or sets path to the file to convert from markdown to MarkdownInfo. + /// Gets or sets path to the file to convert from Markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = PathParameterSet, Mandatory = true)] public string[] Path { get; set; } /// - /// Gets or sets the path to the file to convert from markdown to MarkdownInfo. + /// Gets or sets the path to the file to convert from Markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true)] public string[] LiteralPath { get; set; } /// - /// Gets or sets the InputObject of type System.IO.FileInfo or string with content to convert from markdown to MarkdownInfo. + /// Gets or sets the InputObject of type System.IO.FileInfo or string with content to convert from Markdown to MarkdownInfo. /// [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = InputObjParamSet, Mandatory = true, ValueFromPipeline = true)] public PSObject InputObject { get; set; } /// - /// Gets or sets if the markdown document should be converted to a VT100 encoded string. + /// Gets or sets if the Markdown document should be converted to a VT100 encoded string. /// [Parameter] public SwitchParameter AsVT100EncodedString { get; set; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs index 4c849a8be4..88c6cd8e66 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs @@ -241,7 +241,7 @@ namespace Microsoft.PowerShell.Commands } /// - /// Implements the cmdlet for getting the markdown options that are set. + /// Implements the cmdlet for getting the Markdown options that are set. /// [Cmdlet( VerbsCommon.Get, "MarkdownOption", diff --git a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs index cc9f38cdaa..857192ae68 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/ListItemBlockRenderer.cs @@ -45,9 +45,7 @@ namespace Microsoft.PowerShell.MarkdownRender { foreach (var subListItem in subList) { - var subListItemBlock = subListItem as ListItemBlock; - - if (subListItemBlock != null) + if (subListItem is ListItemBlock subListItemBlock) { foreach (var line in subListItemBlock) { diff --git a/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs b/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs index a1041e7978..fae600d23b 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/MarkdownConverter.cs @@ -10,7 +10,7 @@ using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { /// - /// Type of conversion from markdown. + /// Type of conversion from Markdown. /// [Flags] public enum MarkdownConversionType @@ -27,7 +27,7 @@ namespace Microsoft.PowerShell.MarkdownRender } /// - /// Object representing the conversion from markdown. + /// Object representing the conversion from Markdown. /// public class MarkdownInfo { @@ -42,20 +42,20 @@ namespace Microsoft.PowerShell.MarkdownRender public string VT100EncodedString { get; internal set; } /// - /// Gets the AST of the markdown string. + /// Gets the AST of the Markdown string. /// public Markdig.Syntax.MarkdownDocument Tokens { get; internal set; } } /// - /// Class to convert a markdown string to VT100, HTML or AST. + /// Class to convert a Markdown string to VT100, HTML or AST. /// public sealed class MarkdownConverter { /// - /// Convert from markdown string to VT100 encoded string or HTML. Returns MarkdownInfo object. + /// Convert from Markdown string to VT100 encoded string or HTML. Returns MarkdownInfo object. /// - /// String with markdown content to be converted. + /// String with Markdown content to be converted. /// Specifies type of conversion, either VT100 or HTML. /// Specifies the rendering options for VT100 rendering. /// MarkdownInfo object with the converted output. diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs index dbe128a9ac..8d3a283566 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs @@ -10,7 +10,7 @@ using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { /// - /// Class to represent color preference options for various markdown elements. + /// Class to represent color preference options for various Markdown elements. /// public sealed class MarkdownOptionInfo { From a1627a396d7ff770b6ba980e5f48590d3c254b9e Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Fri, 13 Jul 2018 12:41:29 -0700 Subject: [PATCH 12/12] [Feature] Changed AsEscapeSequence to accept an enum and update Markdig version to latest --- ...Microsoft.PowerShell.MarkdownRender.csproj | 2 +- .../VT100EscapeSequences.cs | 89 +++++++++++++++---- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj index c00c90b959..723dee057a 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj +++ b/src/Microsoft.PowerShell.MarkdownRender/Microsoft.PowerShell.MarkdownRender.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs index 8d3a283566..0e18d48347 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/VT100EscapeSequences.cs @@ -9,6 +9,67 @@ using Markdig.Syntax; namespace Microsoft.PowerShell.MarkdownRender { + /// + /// Enum to name all the properties of MarkdownOptionInfo. + /// + public enum MarkdownOptionInfoProperty + { + /// + /// Property name Header1. + /// + Header1, + + /// + /// Property name Header2. + /// + Header2, + + /// + /// Property name Header3. + /// + Header3, + + /// + /// Property name Header4. + /// + Header4, + + /// + /// Property name Header5. + /// + Header5, + + /// + /// Property name Header6. + /// + Header6, + + /// + /// Property name Code. + /// + Code, + + /// + /// Property name Link. + /// + Link, + + /// + /// Property name Image. + /// + Image, + + /// + /// Property name EmphasisBold. + /// + EmphasisBold, + + /// + /// Property name EmphasisItalics. + /// + EmphasisItalics + } + /// /// Class to represent color preference options for various Markdown elements. /// @@ -78,43 +139,41 @@ namespace Microsoft.PowerShell.MarkdownRender /// /// Name of the property to get as escape sequence. /// Specified property name as escape sequence. - public string AsEscapeSequence(string propertyName) + public string AsEscapeSequence(MarkdownOptionInfoProperty propertyName) { - var propName = propertyName?.ToLower(); - - switch (propName) + switch (propertyName) { - case "header1": + case MarkdownOptionInfoProperty.Header1: return string.Concat(Esc, Header1, Header1, Esc, EndSequence); - case "header2": + case MarkdownOptionInfoProperty.Header2: return string.Concat(Esc, Header2, Header2, Esc, EndSequence); - case "header3": + case MarkdownOptionInfoProperty.Header3: return string.Concat(Esc, Header3, Header3, Esc, EndSequence); - case "header4": + case MarkdownOptionInfoProperty.Header4: return string.Concat(Esc, Header4, Header4, Esc, EndSequence); - case "header5": + case MarkdownOptionInfoProperty.Header5: return string.Concat(Esc, Header5, Header5, Esc, EndSequence); - case "header6": + case MarkdownOptionInfoProperty.Header6: return string.Concat(Esc, Header6, Header6, Esc, EndSequence); - case "code": + case MarkdownOptionInfoProperty.Code: return string.Concat(Esc, Code, Code, Esc, EndSequence); - case "link": + case MarkdownOptionInfoProperty.Link: return string.Concat(Esc, Link, Link, Esc, EndSequence); - case "image": + case MarkdownOptionInfoProperty.Image: return string.Concat(Esc, Image, Image, Esc, EndSequence); - case "emphasisbold": + case MarkdownOptionInfoProperty.EmphasisBold: return string.Concat(Esc, EmphasisBold, EmphasisBold, Esc, EndSequence); - case "emphasisitalics": + case MarkdownOptionInfoProperty.EmphasisItalics: return string.Concat(Esc, EmphasisItalics, EmphasisItalics, Esc, EndSequence); default: