From 1cf14bd596ef760154b24c6b2faf802034ef200e Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 21 Mar 2018 12:33:44 -0700 Subject: [PATCH] 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; } } }