├── .gitignore ├── LICENSE ├── MarkdownLog.sln ├── MarkdownLog.snk ├── MarkdownLog ├── BarChart.cs ├── BarChartDataPoint.cs ├── BlockQuote.cs ├── BulletedList.cs ├── CodeBlock.cs ├── EmptyTableCell.cs ├── GanttChart.cs ├── GanttChartActivity.cs ├── Header.cs ├── HeaderBase.cs ├── HorizontalRule.cs ├── IIosTableViewCell.cs ├── IIosTableViewHeaderCell.cs ├── IMarkdownElement.cs ├── ITableCell.cs ├── IosTableViewCell.cs ├── IosTableViewCheckmarkCell.cs ├── IosTableViewHeaderCell.cs ├── ListBase.cs ├── ListItem.cs ├── MarkDownBuilderExtensions.cs ├── MarkdownContainer.cs ├── MarkdownDeep │ ├── Abbreviation.cs │ ├── Block.cs │ ├── BlockProcessor.cs │ ├── FootnoteReference.cs │ ├── HtmlTag.cs │ ├── LinkDefinition.cs │ ├── LinkInfo.cs │ ├── MardownDeep.cs │ ├── SpanFormatter.cs │ ├── StringScanner.cs │ ├── TableSpec.cs │ ├── Token.cs │ └── Utils.cs ├── MarkdownElement.cs ├── MarkdownLog.csproj ├── MarkdownToHtmlConverter.cs ├── MarkdownToHtmlExtensions.cs ├── NumberExtensions.cs ├── NumberedList.cs ├── Paragraph.cs ├── Properties │ └── AssemblyInfo.cs ├── RawMarkdown.cs ├── ReflectionExtensions.cs ├── StringExtensions.cs ├── SubHeader.cs ├── Table.cs ├── TableCell.cs ├── TableCellRenderSpecification.cs ├── TableColumn.cs ├── TableColumnAlignment.cs ├── TableOptions.cs ├── TableRow.cs ├── TableView.cs ├── TableViewCellAccessory.cs └── TableViewSection.cs ├── README.md ├── UnitTests ├── BarChartTests.cs ├── BlockquoteTests.cs ├── CodeBlockTests.cs ├── DocumentationExamples.cs ├── GanttChartTests.cs ├── HeaderTests.cs ├── HorizontalRuleTests.cs ├── IosTableViewTests.cs ├── ListTests.cs ├── MarkdownLog.Tests.csproj ├── NumberExtensionsTests.cs ├── ParagraphTests.cs ├── ReflectionExtensionsTests.cs ├── StringExtensionsTests.cs ├── TableTests.cs └── TestExtensions.cs └── appveyor.yml /.gitignore: -------------------------------------------------------------------------------- 1 | #OS junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | 5 | #Visual Studio files 6 | *.[Oo]bj 7 | *.user 8 | *.aps 9 | *.pch 10 | *.vspscc 11 | *.vssscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.[Cc]ache 20 | *.ilk 21 | *.log 22 | *.lib 23 | *.sbr 24 | *.sdf 25 | *.opensdf 26 | *.unsuccessfulbuild 27 | ipch/ 28 | [Oo]bj/ 29 | [Bb]in 30 | [Dd]ebug*/ 31 | [Rr]elease*/ 32 | Ankh.NoLoad 33 | 34 | #MonoDevelop 35 | *.pidb 36 | *.userprefs 37 | 38 | #Tooling 39 | _ReSharper*/ 40 | *.resharper 41 | [Tt]est[Rr]esult* 42 | *.sass-cache 43 | 44 | #Project files 45 | [Bb]uild/ 46 | 47 | #Subversion files 48 | .svn 49 | 50 | # Office Temp Files 51 | ~$* 52 | 53 | # vim Temp Files 54 | *~ 55 | 56 | #NuGet 57 | packages/ 58 | *.nupkg 59 | 60 | #ncrunch 61 | *ncrunch* 62 | *crunch*.local.xml 63 | 64 | # visual studio database projects 65 | *.dbmdl 66 | 67 | #Test files 68 | *.testsettings -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 BlackJet Software Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MarkdownLog.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.27920.1 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownLog", "MarkdownLog\MarkdownLog.csproj", "{58003E79-47E0-47D6-BF62-82051EFC1E8B}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownLog.Tests", "UnitTests\MarkdownLog.Tests.csproj", "{115E4845-076E-4615-9FA1-2FFEBE4D6042}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Any CPU = Debug|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {58003E79-47E0-47D6-BF62-82051EFC1E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 16 | {58003E79-47E0-47D6-BF62-82051EFC1E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU 17 | {58003E79-47E0-47D6-BF62-82051EFC1E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU 18 | {58003E79-47E0-47D6-BF62-82051EFC1E8B}.Release|Any CPU.Build.0 = Release|Any CPU 19 | {115E4845-076E-4615-9FA1-2FFEBE4D6042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {115E4845-076E-4615-9FA1-2FFEBE4D6042}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {115E4845-076E-4615-9FA1-2FFEBE4D6042}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {115E4845-076E-4615-9FA1-2FFEBE4D6042}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {FBF00EA3-31F1-41FA-8576-E264D75F910F} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /MarkdownLog.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wheelies/MarkdownLog/783459d28a6ca189f6ff33040a4bc6771943c9b2/MarkdownLog.snk -------------------------------------------------------------------------------- /MarkdownLog/BarChart.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace MarkdownLog 32 | { 33 | public class BarChart : MarkdownElement 34 | { 35 | private IEnumerable _dataPoints = new List(); 36 | private int _maximumChartWidth = 80; 37 | private int _maximumDecimalPlaces = 2; 38 | 39 | public bool ScaleAlways { get; set; } 40 | 41 | public int MaximumChartWidth 42 | { 43 | get { return _maximumChartWidth; } 44 | set { _maximumChartWidth = Math.Max(1, value); } 45 | } 46 | 47 | public int MaximumDecimalPlaces 48 | { 49 | get { return _maximumDecimalPlaces; } 50 | set { _maximumDecimalPlaces = Math.Max(0, value); } 51 | } 52 | 53 | public IEnumerable DataPoints 54 | { 55 | get { return _dataPoints; } 56 | set { _dataPoints = value ?? Enumerable.Empty(); } 57 | } 58 | 59 | public override string ToMarkdown() 60 | { 61 | if (!_dataPoints.Any()) return ""; 62 | 63 | var builder = new StringBuilder(); 64 | var indent = new string(' ', 4); 65 | 66 | var maxValue = _dataPoints.Max(i => i.Value); 67 | var minValue = Math.Min(0, _dataPoints.Min(i => i.Value)); 68 | 69 | var width = Math.Max(Math.Min(maxValue - minValue, _maximumChartWidth), ScaleAlways || maxValue < 1 ? _maximumChartWidth : 0); 70 | var unitLength = width / (maxValue - minValue); 71 | 72 | var longestCategoryName = _dataPoints.Max(i => i.CategoryName.EscapeCSharpString().Length); 73 | 74 | var longestNegativeBar = GetLongestNegativeBar(unitLength); 75 | var longestPositiveBar = GetLongestPositiveBar(unitLength); 76 | 77 | foreach (var dataPoint in _dataPoints) 78 | { 79 | var barLength = GetBarLength(dataPoint, unitLength); 80 | builder.Append(indent); 81 | builder.Append(dataPoint.CategoryName.EscapeCSharpString().PadRight(longestCategoryName)); 82 | 83 | builder.Append(" "); 84 | 85 | builder.Append(new string('#', Math.Max(0, -barLength)).PadLeft(-longestNegativeBar)); 86 | builder.Append("|"); 87 | builder.Append(new string('#', Math.Max(0, barLength))); 88 | builder.Append(" "); 89 | builder.Append(FormatValue(dataPoint.Value)); 90 | builder.AppendLine(); 91 | } 92 | 93 | builder.Append(indent); 94 | builder.Append(new string(' ', longestCategoryName + 1)); 95 | builder.Append(new string('-', -longestNegativeBar + 1 + longestPositiveBar)); 96 | builder.AppendLine(); 97 | 98 | return builder.ToString(); 99 | } 100 | 101 | private int GetLongestPositiveBar(double unitLength) 102 | { 103 | var positiveValues = _dataPoints.Where(i => i.Value > 0).ToList(); 104 | return positiveValues.Any() ? positiveValues.Max(i => GetBarLength(i, unitLength)) : 0; 105 | } 106 | 107 | private int GetLongestNegativeBar(double unitLength) 108 | { 109 | var negativeValues = _dataPoints.Where(i => i.Value < 0).ToList(); 110 | var longestNegativeBar = negativeValues.Any() ? negativeValues.Min(i => GetBarLength(i, unitLength)) : 0; 111 | return longestNegativeBar; 112 | } 113 | 114 | private string FormatValue(double value) 115 | { 116 | var format = "{0:0." + new string('#', _maximumDecimalPlaces) + "}"; 117 | return string.Format(format, value); 118 | } 119 | 120 | private static int GetBarLength(BarChartDataPoint dataPoint, double unitLength) 121 | { 122 | return (int) Math.Round(unitLength * dataPoint.Value); 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /MarkdownLog/BarChartDataPoint.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class BarChartDataPoint 29 | { 30 | private string _categoryName = ""; 31 | 32 | public string CategoryName 33 | { 34 | get { return _categoryName; } 35 | set { _categoryName = value ?? ""; } 36 | } 37 | 38 | public double Value { get; set; } 39 | } 40 | } -------------------------------------------------------------------------------- /MarkdownLog/BlockQuote.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Text; 28 | 29 | namespace MarkdownLog 30 | { 31 | public class Blockquote : MarkdownElement 32 | { 33 | private readonly StringBuilder _builder = new StringBuilder(); 34 | 35 | public Blockquote() 36 | { 37 | } 38 | 39 | public Blockquote(string text) 40 | { 41 | Append(text ?? ""); 42 | } 43 | 44 | public void AppendLine() 45 | { 46 | AppendLine(""); 47 | } 48 | 49 | public void AppendLine(string text) 50 | { 51 | Append(text ?? ""); 52 | Append(Environment.NewLine); 53 | } 54 | 55 | public void Append(string text) 56 | { 57 | text = text ?? ""; 58 | 59 | if (_builder.Length > 0) 60 | _builder.AppendLine(); 61 | 62 | _builder.Append(text.EscapeMarkdownCharacters().PrependAllLines("> ")); 63 | } 64 | 65 | public void Append(IMarkdownElement element) 66 | { 67 | if (_builder.Length > 0) 68 | _builder.AppendLine(); 69 | 70 | _builder.Append(element.ToMarkdown().PrependAllLines("> ")); 71 | } 72 | 73 | public override string ToMarkdown() 74 | { 75 | return _builder + Environment.NewLine; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /MarkdownLog/BulletedList.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System.Collections.Generic; 27 | 28 | namespace MarkdownLog 29 | { 30 | public class BulletedList : ListBase 31 | { 32 | public BulletedList(params string[] items) : base(items) 33 | { 34 | } 35 | 36 | public BulletedList(IEnumerable items) : base(items) 37 | { 38 | } 39 | 40 | protected override string GetListItemFirstLinePrefix(int itemNumber) 41 | { 42 | return " * "; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /MarkdownLog/CodeBlock.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Text; 28 | 29 | namespace MarkdownLog 30 | { 31 | public class CodeBlock : MarkdownElement 32 | { 33 | private readonly StringBuilder _builder = new StringBuilder(); 34 | 35 | public CodeBlock() 36 | { 37 | } 38 | 39 | public CodeBlock(string text) 40 | { 41 | Append(text); 42 | } 43 | 44 | public void AppendLine() 45 | { 46 | AppendLine(""); 47 | } 48 | 49 | public void AppendLine(string text) 50 | { 51 | Append(text); 52 | Append(Environment.NewLine); 53 | } 54 | 55 | public void Append(string text) 56 | { 57 | text = text ?? ""; 58 | _builder.Append(text); 59 | } 60 | 61 | public override string ToMarkdown() 62 | { 63 | return _builder.ToString().PrependAllLines(" ").TrimEnd(' '); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /MarkdownLog/EmptyTableCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | internal class EmptyTableCell : ITableCell 29 | { 30 | public int RequiredWidth { get { return 0; } } 31 | 32 | public string BuildCodeFormattedString(TableCellRenderSpecification spec) 33 | { 34 | return ""; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /MarkdownLog/GanttChart.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace MarkdownLog 32 | { 33 | public class GanttChart : MarkdownElement 34 | { 35 | private const string ZeroLengthActivityIndicator = "+"; 36 | private IEnumerable _activities = new List(); 37 | private int _maximumChartWidth = 80; 38 | private int _maximumDecimalPlaces = 2; 39 | 40 | public bool ScaleAlways { get; set; } 41 | 42 | public int MaximumChartWidth 43 | { 44 | get { return _maximumChartWidth; } 45 | set { _maximumChartWidth = Math.Max(1, value); } 46 | } 47 | 48 | public int MaximumDecimalPlaces 49 | { 50 | get { return _maximumDecimalPlaces; } 51 | set { _maximumDecimalPlaces = Math.Max(0, value); } 52 | } 53 | 54 | public IEnumerable Activities 55 | { 56 | get { return _activities; } 57 | set { _activities = value ?? Enumerable.Empty(); } 58 | } 59 | 60 | internal class Range 61 | { 62 | private static readonly Range NullRange = new Range(Double.NaN, Double.NaN); 63 | 64 | private readonly double _start; 65 | private readonly double _end; 66 | 67 | internal Range(double start, double end) 68 | { 69 | _start = Math.Min(start, end); 70 | _end = Math.Max(start, end); 71 | } 72 | 73 | public double Start 74 | { 75 | get { return _start; } 76 | } 77 | 78 | public double End 79 | { 80 | get { return _end; } 81 | } 82 | 83 | public double Length 84 | { 85 | get { return _end - _start; } 86 | } 87 | 88 | public Range Scale(double unitLength) 89 | { 90 | return new Range(Scale(_start, unitLength), Scale(_end, unitLength)); 91 | } 92 | 93 | private static int Scale(double value, double unitLength) 94 | { 95 | return (int)Math.Round(unitLength * value); 96 | } 97 | 98 | private Range GetIntersect(Range range) 99 | { 100 | if (End < range.Start || range.End < Start) 101 | return NullRange; 102 | 103 | var max = Math.Min(End, range.End); 104 | var min = Math.Max(Start, range.Start); 105 | 106 | return new Range(min, max); 107 | } 108 | 109 | public double GetOverlap(Range range) 110 | { 111 | var intersect = GetIntersect(range); 112 | 113 | return (intersect == NullRange) ? 0 : intersect.Length; 114 | } 115 | 116 | public double GetStartOffset(Range range) 117 | { 118 | var intersect = GetIntersect(range); 119 | return intersect == NullRange ? 0 : intersect.Start - Start; 120 | } 121 | 122 | public double GetEndOffset(Range range) 123 | { 124 | var intersect = GetIntersect(range); 125 | return intersect == NullRange ? Length : End - intersect.End; 126 | } 127 | 128 | public override string ToString() 129 | { 130 | return string.Format("{0} -> {1}", _start, _end); 131 | } 132 | } 133 | 134 | private class PreparedActivity 135 | { 136 | public string Name { get; set; } 137 | public string StartText { get; set; } 138 | public string EndText { get; set; } 139 | public string LengthText { get; set; } 140 | public Range Range { get; set; } 141 | } 142 | 143 | public override string ToMarkdown() 144 | { 145 | if (!_activities.Any()) return ""; 146 | 147 | var activities = (from i in _activities 148 | let range = new Range(i.StartValue, i.EndValue) 149 | select new PreparedActivity 150 | { 151 | Name = i.Name.EscapeCSharpString(), 152 | StartText = FormatValue(i.StartValue), 153 | EndText = FormatValue(i.EndValue), 154 | LengthText = string.Format("({0})", FormatValue(range.End - range.Start)), 155 | Range = range, 156 | }).ToList(); 157 | 158 | var builder = new StringBuilder(); 159 | var indent = new string(' ', 4); 160 | 161 | var maxEnd = activities.Max(i => i.Range.End); 162 | var minStart = activities.Min(i => i.Range.Start); 163 | var maxActivityRangeWidth = ( maxEnd - minStart ) + "|".Length; 164 | 165 | var minimumChartWidth = ScaleAlways || maxActivityRangeWidth < 1 ? _maximumChartWidth : 0; 166 | var chartWidth = (int)Math.Max(Math.Min(maxActivityRangeWidth, _maximumChartWidth), minimumChartWidth); 167 | var unitLength = chartWidth / maxActivityRangeWidth; 168 | 169 | var longestName = activities.Max(i => i.Name.Length); 170 | var longestStartText = activities.Max(i => i.StartText.Length); 171 | var longestEndText = activities.Max(i => i.EndText.Length); 172 | var longestLengthText = activities.Max(i => i.LengthText.Length); 173 | 174 | var scaledNegativeRange = new Range(Math.Min(0, minStart), 0).Scale(unitLength); 175 | var scaledPositiveRange = new Range(0, Math.Max(0, maxEnd)).Scale(unitLength); 176 | 177 | foreach (var activity in activities) 178 | { 179 | builder.Append(indent); 180 | builder.Append(activity.Name.PadRight(longestName)); 181 | 182 | builder.Append(" "); 183 | 184 | var scaledRange = activity.Range.Scale(unitLength); 185 | bool isZeroLengthActivity = scaledRange.Length < unitLength; 186 | bool isZeroLengthNegative = isZeroLengthActivity && scaledRange.Start < 0; 187 | 188 | 189 | builder.Append(Spaces(scaledNegativeRange.GetStartOffset(scaledRange))); 190 | builder.Append(isZeroLengthNegative ? ZeroLengthActivityIndicator : Bar(scaledNegativeRange.GetOverlap(scaledRange))); 191 | 192 | double spacesAfterNegative = scaledNegativeRange.GetEndOffset(scaledRange); 193 | if (isZeroLengthNegative) spacesAfterNegative--; 194 | 195 | builder.Append(Spaces(spacesAfterNegative)); 196 | builder.Append(isZeroLengthActivity && Math.Abs(scaledRange.Start) < unitLength ? ZeroLengthActivityIndicator : "|"); 197 | 198 | bool isZeroLengthPositive = isZeroLengthActivity && scaledRange.Start > 0; 199 | double spacesBeforePositive = scaledPositiveRange.GetStartOffset(scaledRange); 200 | if (isZeroLengthPositive) spacesBeforePositive--; 201 | 202 | builder.Append(Spaces(spacesBeforePositive)); 203 | 204 | builder.Append(isZeroLengthPositive ? ZeroLengthActivityIndicator : Bar(scaledPositiveRange.GetOverlap(scaledRange))); 205 | builder.Append(Spaces(scaledPositiveRange.GetEndOffset(scaledRange))); 206 | builder.Append(Spaces(2)); 207 | 208 | builder.Append(activity.StartText.PadLeft(longestStartText)); 209 | builder.Append(" -> "); 210 | builder.Append(activity.EndText.PadLeft(longestEndText)); 211 | builder.Append(" "); 212 | builder.Append(activity.LengthText.PadLeft(longestLengthText)); 213 | builder.AppendLine(); 214 | } 215 | 216 | builder.Append(indent); 217 | builder.Append(new string(' ', longestName + 1)); 218 | 219 | builder.Append(new string('-', (int) Math.Round(scaledNegativeRange.Length + 1 + scaledPositiveRange.Length))); 220 | builder.AppendLine(); 221 | 222 | return builder.ToString(); 223 | } 224 | 225 | private string Spaces(double count) 226 | { 227 | if (count < 0) 228 | throw new ArgumentOutOfRangeException("count", string.Format("Cannot be negative (value was {0})", count)); 229 | 230 | return new string(' ', (int) Math.Round(count)); 231 | } 232 | 233 | private string Bar(double length) 234 | { 235 | return new string('=', (int)Math.Round(length)); 236 | } 237 | 238 | private string FormatValue(double value) 239 | { 240 | var format = "{0:0." + new string('#', _maximumDecimalPlaces) + "}"; 241 | return string.Format(format, value); 242 | } 243 | } 244 | } -------------------------------------------------------------------------------- /MarkdownLog/GanttChartActivity.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class GanttChartActivity 29 | { 30 | private string _name = ""; 31 | 32 | public string Name 33 | { 34 | get { return _name; } 35 | set { _name = value ?? ""; } 36 | } 37 | 38 | public double StartValue { get; set; } 39 | public double EndValue { get; set; } 40 | } 41 | } -------------------------------------------------------------------------------- /MarkdownLog/Header.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class Header : HeaderBase 29 | { 30 | public Header(string format, params object[] args) : this(string.Format(format, args)) { } 31 | 32 | public Header(string text) : base(text, '=') 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /MarkdownLog/HeaderBase.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System.Text; 27 | 28 | namespace MarkdownLog 29 | { 30 | public abstract class HeaderBase : MarkdownElement 31 | { 32 | private readonly char _underlineChar; 33 | private readonly string _text = ""; 34 | 35 | protected HeaderBase(string text, char underlineChar) 36 | { 37 | _text = text ?? ""; 38 | _underlineChar = underlineChar; 39 | } 40 | 41 | public override string ToMarkdown() 42 | { 43 | var builder = new StringBuilder(); 44 | 45 | var textLines = _text.SplitByLine(); 46 | 47 | foreach(var textLine in textLines) 48 | { 49 | if (builder.Length > 0) 50 | builder.AppendLine(); 51 | 52 | var markdown = textLine.EscapeMarkdownCharacters(); 53 | builder.Append(markdown); 54 | builder.AppendLine(); 55 | builder.Append(new string(_underlineChar, markdown.Length)); 56 | builder.AppendLine(); 57 | } 58 | 59 | return builder.ToString(); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /MarkdownLog/HorizontalRule.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | 28 | namespace MarkdownLog 29 | { 30 | public class HorizontalRule : MarkdownElement 31 | { 32 | public override string ToMarkdown() 33 | { 34 | return new string('-', 80) + Environment.NewLine; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /MarkdownLog/IIosTableViewCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public interface IIosTableViewCell 29 | { 30 | int RequiredWidth { get; } 31 | string BuildCodeFormattedString(int maximumWidth); 32 | } 33 | } -------------------------------------------------------------------------------- /MarkdownLog/IIosTableViewHeaderCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public interface IIosTableViewHeaderCell : IIosTableViewCell 29 | { 30 | } 31 | } -------------------------------------------------------------------------------- /MarkdownLog/IMarkdownElement.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public interface IMarkdownElement 29 | { 30 | string ToMarkdown(); 31 | } 32 | } -------------------------------------------------------------------------------- /MarkdownLog/ITableCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public interface ITableCell 29 | { 30 | int RequiredWidth { get; } 31 | string BuildCodeFormattedString(TableCellRenderSpecification spec); 32 | } 33 | } -------------------------------------------------------------------------------- /MarkdownLog/IosTableViewCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Text; 28 | 29 | namespace MarkdownLog 30 | { 31 | public class IosTableViewCell : IIosTableViewCell 32 | { 33 | private const int SpacesBeforeText = 1; 34 | private const int SpacesBetweenTextAndAccessory = 1; 35 | private const int SpacesAfterAccessory = 1; 36 | private string _text; 37 | 38 | public IosTableViewCell() : this("", TableViewCellAccessory.None) 39 | { 40 | } 41 | 42 | public IosTableViewCell(string text) 43 | : this(text, TableViewCellAccessory.None) 44 | { 45 | } 46 | 47 | public IosTableViewCell(string text, TableViewCellAccessory accessory) 48 | { 49 | _text = text; 50 | Accessory = accessory; 51 | } 52 | 53 | public string Text 54 | { 55 | get { return _text; } 56 | set { _text = value ?? ""; } 57 | } 58 | 59 | public TableViewCellAccessory Accessory { get; set; } 60 | 61 | public int RequiredWidth 62 | { 63 | get 64 | { 65 | return SpacesBeforeText + 66 | Text.Length + 67 | SpacesBetweenTextAndAccessory + 68 | GetAccessorySymbol().Length + 69 | SpacesAfterAccessory; 70 | } 71 | } 72 | 73 | public string BuildCodeFormattedString(int maximumWidth) 74 | { 75 | var sb = new StringBuilder(); 76 | var accessoryText = GetAccessorySymbol(); 77 | var textPadding = maximumWidth - SpacesBeforeText - SpacesBetweenTextAndAccessory - accessoryText.Length - SpacesAfterAccessory; 78 | 79 | sb.Append(new String(' ', SpacesBeforeText)); 80 | sb.Append(Text.PadRight(textPadding)); 81 | sb.Append(new String(' ', SpacesBetweenTextAndAccessory)); 82 | sb.Append(accessoryText); 83 | sb.Append(new String(' ', SpacesAfterAccessory)); 84 | 85 | return sb.ToString(); 86 | } 87 | 88 | private string GetAccessorySymbol() 89 | { 90 | switch (Accessory) 91 | { 92 | case TableViewCellAccessory.None: 93 | return ""; 94 | case TableViewCellAccessory.DisclosureIndicator: 95 | return ">"; 96 | case TableViewCellAccessory.DetailDisclosureButton: 97 | return "(>)"; 98 | case TableViewCellAccessory.Checkmark: 99 | return "/"; 100 | case TableViewCellAccessory.DetailButton: 101 | return "(i)"; 102 | default: 103 | throw new NotSupportedException("Unsupported TableViewCellAccessory: " + Accessory); 104 | } 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /MarkdownLog/IosTableViewCheckmarkCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | // Custom cell 29 | public class IosTableViewCheckmarkCell : IIosTableViewCell 30 | { 31 | private string _text; 32 | 33 | public string Text 34 | { 35 | get { return _text; } 36 | set { _text = value ?? ""; } 37 | } 38 | 39 | public bool IsChecked { get; set; } 40 | 41 | public int RequiredWidth 42 | { 43 | get { return BuildCodeFormattedString(0).Length; } 44 | } 45 | 46 | public string BuildCodeFormattedString(int maximumWidth) 47 | { 48 | return string.Format(" [{0}] {1} ", CheckCharacter, Text); 49 | } 50 | 51 | private char CheckCharacter 52 | { 53 | get { return IsChecked ? 'X' : ' '; } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /MarkdownLog/IosTableViewHeaderCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class IosTableViewHeaderCell : IIosTableViewHeaderCell 29 | { 30 | private string _text; 31 | 32 | public string Text 33 | { 34 | get { return _text; } 35 | set { _text = value ?? ""; } 36 | } 37 | 38 | public int RequiredWidth 39 | { 40 | get { return Text.Length; } 41 | } 42 | 43 | public string BuildCodeFormattedString(int maximumWidth) 44 | { 45 | return Text; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /MarkdownLog/ListBase.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace MarkdownLog 32 | { 33 | public abstract class ListBase : MarkdownElement 34 | { 35 | private readonly IEnumerable _items = new List(); 36 | private int _wordWrapColumn; 37 | 38 | protected ListBase(params string[] items) : this((IEnumerable) items) 39 | { 40 | } 41 | 42 | protected ListBase(IEnumerable items) 43 | { 44 | items = items ?? Enumerable.Empty(); 45 | 46 | _items = items.Select(ListItem.FromText); 47 | WordWrap = true; 48 | WordWrapColumn = 80; 49 | } 50 | 51 | public bool WordWrap { get; set; } 52 | 53 | public int WordWrapColumn 54 | { 55 | get { return _wordWrapColumn; } 56 | set { _wordWrapColumn = Math.Max(0, value); } 57 | } 58 | 59 | public override string ToMarkdown() 60 | { 61 | var builder = new StringBuilder(); 62 | 63 | var number = 1; 64 | foreach (var item in _items) 65 | { 66 | var firstLinePrefix = GetListItemFirstLinePrefix(number); 67 | 68 | var markdown = item.ToMarkdown(); 69 | var wrappedMarkdown = WordWrap ? markdown.WrapAt(WordWrapColumn - firstLinePrefix.Length) : markdown; 70 | 71 | builder.AppendFormat("{0}{1}", firstLinePrefix, wrappedMarkdown.IndentAllExceptFirst(firstLinePrefix.Length)); 72 | builder.AppendLine(); 73 | number ++; 74 | } 75 | return builder.ToString(); 76 | } 77 | 78 | protected abstract string GetListItemFirstLinePrefix(int itemNumber); 79 | } 80 | } -------------------------------------------------------------------------------- /MarkdownLog/ListItem.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | internal class ListItem : MarkdownElement 29 | { 30 | private string _markdown = ""; 31 | 32 | public static ListItem FromText(string text) 33 | { 34 | text = text ?? ""; 35 | return new ListItem{_markdown = text.EscapeMarkdownCharacters()}; 36 | } 37 | 38 | public override string ToMarkdown() 39 | { 40 | return _markdown; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /MarkdownLog/MarkDownBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | using System.Reflection; 30 | 31 | namespace MarkdownLog 32 | { 33 | public static class MarkDownBuilderExtensions 34 | { 35 | public static Header ToMarkdownHeader(this string text) 36 | { 37 | return new Header(text); 38 | } 39 | 40 | public static SubHeader ToMarkdownSubHeader(this string text) 41 | { 42 | return new SubHeader(text); 43 | } 44 | 45 | public static Paragraph ToMarkdownParagraph(this string text) 46 | { 47 | return new Paragraph(text); 48 | } 49 | 50 | public static Blockquote ToMarkdownBlockquote(this string text) 51 | { 52 | var blockquote = new Blockquote(); 53 | blockquote.Append(text); 54 | return blockquote; 55 | } 56 | 57 | public static Blockquote ToMarkdownBlockquote(this IMarkdownElement element) 58 | { 59 | var blockquote = new Blockquote(); 60 | blockquote.Append(element); 61 | return blockquote; 62 | } 63 | 64 | public static CodeBlock ToMarkdownCodeBlock(this string code) 65 | { 66 | return new CodeBlock(code); 67 | } 68 | 69 | 70 | public static NumberedList ToMarkdownNumberedList(this IEnumerable items) 71 | { 72 | return items.ToMarkdownNumberedList(i => i.ToString()); 73 | } 74 | 75 | public static NumberedList ToMarkdownNumberedList(this IEnumerable items, Func toText) 76 | { 77 | return new NumberedList(items.Select(toText)); 78 | } 79 | 80 | public static BulletedList ToMarkdownBulletedList(this IEnumerable items) 81 | { 82 | return items.ToMarkdownBulletedList(i => i.ToString()); 83 | } 84 | 85 | public static BulletedList ToMarkdownBulletedList(this IEnumerable items, Func toText) 86 | { 87 | return new BulletedList(items.Select(toText)); 88 | } 89 | 90 | public static BarChart ToMarkdownBarChart(this IEnumerable> series) 91 | { 92 | return ToMarkdownBarChart(series, i => i.Key, i => i.Value); 93 | } 94 | 95 | public static BarChart ToMarkdownBarChart(this IEnumerable> series) 96 | { 97 | return ToMarkdownBarChart(series, i => i.Key, i => i.Value); 98 | } 99 | 100 | public static BarChart ToMarkdownBarChart(this IEnumerable> series) 101 | { 102 | return ToMarkdownBarChart(series, i => i.Key, i => i.Value); 103 | } 104 | 105 | public static BarChart ToMarkdownBarChart(this IEnumerable> series) 106 | { 107 | return ToMarkdownBarChart(series, i => i.Item1, i => i.Item2); 108 | } 109 | 110 | public static BarChart ToMarkdownBarChart(this IEnumerable> series) 111 | { 112 | return ToMarkdownBarChart(series, i => i.Item1, i => i.Item2); 113 | } 114 | 115 | public static BarChart ToMarkdownBarChart(this IEnumerable> series) 116 | { 117 | return ToMarkdownBarChart(series, i => i.Item1, i => i.Item2); 118 | } 119 | 120 | public static BarChart ToMarkdownBarChart(this IEnumerable dataPoints, Func getCategoryName, Func getValue) 121 | { 122 | return new BarChart {DataPoints = dataPoints.Select(i => new BarChartDataPoint {CategoryName = getCategoryName(i), Value = getValue(i)})}; 123 | } 124 | 125 | public static IDictionary ToPropertyValues(this T obj) 126 | { 127 | var properties = typeof(T).GetProperties().ToList(); 128 | return properties.ToDictionary(i => i.Name, i => i.GetValue(obj, null)); 129 | } 130 | 131 | public static Table ToMarkdownTable(this IEnumerable rows) 132 | { 133 | return ToMarkdownTable(rows, TableOptions.Default); 134 | } 135 | 136 | public static Table ToMarkdownTable(this IEnumerable rows, TableOptions options) 137 | { 138 | var allProperties = typeof (T).GetProperties(); 139 | 140 | var properties = options == TableOptions.ExcludeCollectionProperties 141 | ? allProperties.Where(p => !p.PropertyType.IsEnumerable() || (p.PropertyType == typeof (string))).ToList() 142 | : allProperties.ToList(); 143 | 144 | return ToMarkdownTable(rows, properties.Select(property => (Func) (r => r.GetFormattedValue(property))).ToArray()) 145 | .WithHeaders(properties.Select(i => i.Name).ToArray()); 146 | } 147 | 148 | private static string GetFormattedValue(this T obj, PropertyInfo property) 149 | { 150 | try 151 | { 152 | var value = property.GetValue(obj, null); 153 | 154 | if (value == null) 155 | return ""; 156 | 157 | if(value.GetType().IsWholeNumber()) 158 | return value.ToString(); 159 | 160 | if (value is float) 161 | return ((float)value).ToString("0.00"); 162 | 163 | if (value is double) 164 | return ((double)value).ToString("0.00"); 165 | 166 | if (value is decimal) 167 | return ((decimal)value).ToString("0.00"); 168 | 169 | if (value is DateTime) 170 | return ((DateTime)value).ToString("r"); 171 | 172 | return value.ToString(); 173 | } 174 | catch (Exception exception) 175 | { 176 | return "{" +exception.GetType().Name + "}"; 177 | } 178 | } 179 | 180 | 181 | public static Table WithHeaders(this Table table, params string[] titles) 182 | { 183 | var newColumns = new List(); 184 | for (int i = 0; i < titles.Length; i++) 185 | { 186 | var newTitle = titles[i]; 187 | var column = table.Columns.ElementAtOrDefault(i) ?? new TableColumn(); 188 | column.HeaderCell = new TableCell{Text = newTitle}; 189 | newColumns.Add(column); 190 | } 191 | 192 | table.Columns = newColumns; 193 | return table; 194 | } 195 | 196 | public static Table ToMarkdownTable(this IEnumerable rows, params Func[] getCellValueFuncs) 197 | { 198 | var columnCount = getCellValueFuncs.Count(); 199 | List> rowValues = rows.Select(r => getCellValueFuncs.Select(i => i(r) ?? "").ToList()).ToList(); 200 | 201 | return new Table 202 | { 203 | Columns = Enumerable.Range(0, columnCount).Select(i => new TableColumn {Alignment = Alignment(rowValues, i)}), 204 | Rows = rowValues.Select(r => new TableRow {Cells = Enumerable.Range(0, columnCount).Select(i => new TableCell {Text = r[i].ToString()})}) 205 | }; 206 | } 207 | 208 | private static TableColumnAlignment Alignment(IEnumerable> rowValues, int columnIndex) 209 | { 210 | return EntireColumnIsNumericOrNull(rowValues, columnIndex) ? TableColumnAlignment.Right : TableColumnAlignment.Unspecified; 211 | } 212 | 213 | private static bool EntireColumnIsNumericOrNull(IEnumerable> rowValues, int column) 214 | { 215 | return rowValues.All(r => IsNumericOrNull(r[column])); 216 | } 217 | 218 | private static bool IsNumericOrNull(object obj) 219 | { 220 | if (obj == null) return true; 221 | 222 | var isNumeric = obj.GetType().IsNumeric(); 223 | var isBlank = obj.ToString().Trim() == ""; 224 | 225 | decimal decimalValue; 226 | var isDecimal = decimal.TryParse(obj.ToString(), out decimalValue); 227 | 228 | return isNumeric || isBlank || isDecimal; 229 | } 230 | 231 | public static Table ToMarkdownTable(this IEnumerable rows, params Func[] getCellValueFuncs) 232 | { 233 | return new Table {Rows = rows.Select(row => new TableRow {Cells = getCellValueFuncs.Select(i => new TableCell {Text = i(row)})})}; 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownContainer.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | 30 | namespace MarkdownLog 31 | { 32 | public class MarkdownContainer : MarkdownElement 33 | { 34 | private readonly List _elements = new List(); 35 | 36 | public void Append(IMarkdownElement element) 37 | { 38 | _elements.Add(element); 39 | } 40 | 41 | public override string ToMarkdown() 42 | { 43 | return string.Join(Environment.NewLine, _elements.Select(i => i.ToMarkdown())); 44 | } 45 | 46 | public override string ToString() 47 | { 48 | return ToMarkdown(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/Abbreviation.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | class Abbreviation 23 | { 24 | public Abbreviation(string abbr, string title) 25 | { 26 | Abbr = abbr; 27 | Title = title; 28 | } 29 | public string Abbr; 30 | public string Title; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/FootnoteReference.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | class FootnoteReference 23 | { 24 | public FootnoteReference(int index, string id) 25 | { 26 | this.index = index; 27 | this.id = id; 28 | } 29 | public int index; 30 | public string id; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/HtmlTag.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | [Flags] 23 | public enum HtmlTagFlags 24 | { 25 | Block = 0x0001, // Block tag 26 | Inline = 0x0002, // Inline tag 27 | NoClosing = 0x0004, // No closing tag (eg:
and ) 28 | ContentAsSpan = 0x0008, // When markdown=1 treat content as span, not block 29 | }; 30 | 31 | public class HtmlTag 32 | { 33 | public HtmlTag(string name) 34 | { 35 | m_name = name; 36 | } 37 | 38 | // Get the tag name eg: "div" 39 | public string name 40 | { 41 | get { return m_name; } 42 | } 43 | 44 | // Get a dictionary of attribute values (no decoding done) 45 | public Dictionary attributes 46 | { 47 | get { return m_attributes; } 48 | } 49 | 50 | // Is this tag closed eg;
51 | public bool closed 52 | { 53 | get { return m_closed; } 54 | set { m_closed = value; } 55 | } 56 | 57 | // Is this a closing tag eg: 58 | public bool closing 59 | { 60 | get { return m_closing; } 61 | } 62 | 63 | string m_name; 64 | Dictionary m_attributes = new Dictionary(StringComparer.CurrentCultureIgnoreCase); 65 | bool m_closed; 66 | bool m_closing; 67 | HtmlTagFlags m_flags = 0; 68 | 69 | public HtmlTagFlags Flags 70 | { 71 | get 72 | { 73 | if (m_flags == 0) 74 | { 75 | if (!m_tag_flags.TryGetValue(name.ToLower(), out m_flags)) 76 | { 77 | m_flags |= HtmlTagFlags.Inline; 78 | } 79 | } 80 | 81 | return m_flags; 82 | } 83 | } 84 | 85 | static string[] m_allowed_tags = new string [] { 86 | "b","blockquote","code","dd","dt","dl","del","em","h1","h2","h3","h4","h5","h6","i","kbd","li","ol","ul", 87 | "p", "pre", "s", "sub", "sup", "strong", "strike", "img", "a" 88 | }; 89 | 90 | static Dictionary m_allowed_attributes = new Dictionary() { 91 | { "a", new string[] { "href", "title", "class" } }, 92 | { "img", new string[] { "src", "width", "height", "alt", "title", "class" } }, 93 | }; 94 | 95 | static Dictionary m_tag_flags = new Dictionary() { 96 | { "p", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan }, 97 | { "div", HtmlTagFlags.Block }, 98 | { "h1", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan }, 99 | { "h2", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, 100 | { "h3", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, 101 | { "h4", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, 102 | { "h5", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, 103 | { "h6", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, 104 | { "blockquote", HtmlTagFlags.Block }, 105 | { "pre", HtmlTagFlags.Block }, 106 | { "table", HtmlTagFlags.Block }, 107 | { "dl", HtmlTagFlags.Block }, 108 | { "ol", HtmlTagFlags.Block }, 109 | { "ul", HtmlTagFlags.Block }, 110 | { "form", HtmlTagFlags.Block }, 111 | { "fieldset", HtmlTagFlags.Block }, 112 | { "iframe", HtmlTagFlags.Block }, 113 | { "script", HtmlTagFlags.Block | HtmlTagFlags.Inline }, 114 | { "noscript", HtmlTagFlags.Block | HtmlTagFlags.Inline }, 115 | { "math", HtmlTagFlags.Block | HtmlTagFlags.Inline }, 116 | { "ins", HtmlTagFlags.Block | HtmlTagFlags.Inline }, 117 | { "del", HtmlTagFlags.Block | HtmlTagFlags.Inline }, 118 | { "img", HtmlTagFlags.Block | HtmlTagFlags.Inline }, 119 | { "li", HtmlTagFlags.ContentAsSpan}, 120 | { "dd", HtmlTagFlags.ContentAsSpan}, 121 | { "dt", HtmlTagFlags.ContentAsSpan}, 122 | { "td", HtmlTagFlags.ContentAsSpan}, 123 | { "th", HtmlTagFlags.ContentAsSpan}, 124 | { "legend", HtmlTagFlags.ContentAsSpan}, 125 | { "address", HtmlTagFlags.ContentAsSpan}, 126 | { "hr", HtmlTagFlags.Block | HtmlTagFlags.NoClosing}, 127 | { "!", HtmlTagFlags.Block | HtmlTagFlags.NoClosing}, 128 | { "head", HtmlTagFlags.Block }, 129 | }; 130 | 131 | // Check if this tag is safe 132 | public bool IsSafe() 133 | { 134 | string name_lower=m_name.ToLowerInvariant(); 135 | 136 | // Check if tag is in whitelist 137 | if (!Utils.IsInList(name_lower, m_allowed_tags)) 138 | return false; 139 | 140 | // Find allowed attributes 141 | string[] allowed_attributes; 142 | if (!m_allowed_attributes.TryGetValue(name_lower, out allowed_attributes)) 143 | { 144 | // No allowed attributes, check we don't have any 145 | return m_attributes.Count == 0; 146 | } 147 | 148 | // Check all are allowed 149 | foreach (var i in m_attributes) 150 | { 151 | if (!Utils.IsInList(i.Key.ToLowerInvariant(), allowed_attributes)) 152 | return false; 153 | } 154 | 155 | // Check href attribute is ok 156 | string href; 157 | if (m_attributes.TryGetValue("href", out href)) 158 | { 159 | if (!Utils.IsSafeUrl(href)) 160 | return false; 161 | } 162 | 163 | string src; 164 | if (m_attributes.TryGetValue("src", out src)) 165 | { 166 | if (!Utils.IsSafeUrl(src)) 167 | return false; 168 | } 169 | 170 | 171 | // Passed all white list checks, allow it 172 | return true; 173 | } 174 | 175 | // Render opening tag (eg: 176 | public void RenderOpening(StringBuilder dest) 177 | { 178 | dest.Append("<"); 179 | dest.Append(name); 180 | foreach (var i in m_attributes) 181 | { 182 | dest.Append(" "); 183 | dest.Append(i.Key); 184 | dest.Append("=\""); 185 | dest.Append(i.Value); 186 | dest.Append("\""); 187 | } 188 | 189 | if (m_closed) 190 | dest.Append(" />"); 191 | else 192 | dest.Append(">"); 193 | } 194 | 195 | // Render closing tag (eg: ) 196 | public void RenderClosing(StringBuilder dest) 197 | { 198 | dest.Append(""); 201 | } 202 | 203 | 204 | public static HtmlTag Parse(string str, ref int pos) 205 | { 206 | StringScanner sp = new StringScanner(str, pos); 207 | var ret = Parse(sp); 208 | 209 | if (ret!=null) 210 | { 211 | pos = sp.position; 212 | return ret; 213 | } 214 | 215 | return null; 216 | } 217 | 218 | public static HtmlTag Parse(StringScanner p) 219 | { 220 | // Save position 221 | int savepos = p.position; 222 | 223 | // Parse it 224 | var ret = ParseHelper(p); 225 | if (ret!=null) 226 | return ret; 227 | 228 | // Rewind if failed 229 | p.position = savepos; 230 | return null; 231 | } 232 | 233 | private static HtmlTag ParseHelper(StringScanner p) 234 | { 235 | // Does it look like a tag? 236 | if (p.current != '<') 237 | return null; 238 | 239 | // Skip '<' 240 | p.SkipForward(1); 241 | 242 | // Is it a comment? 243 | if (p.SkipString("!--")) 244 | { 245 | p.Mark(); 246 | 247 | if (p.Find("-->")) 248 | { 249 | var t = new HtmlTag("!"); 250 | t.m_attributes.Add("content", p.Extract()); 251 | t.m_closed = true; 252 | p.SkipForward(3); 253 | return t; 254 | } 255 | } 256 | 257 | // Is it a closing tag eg: 258 | bool bClosing = p.SkipChar('/'); 259 | 260 | // Get the tag name 261 | string tagName=null; 262 | if (!p.SkipIdentifier(ref tagName)) 263 | return null; 264 | 265 | // Probably a tag, create the HtmlTag object now 266 | HtmlTag tag = new HtmlTag(tagName); 267 | tag.m_closing = bClosing; 268 | 269 | 270 | // If it's a closing tag, no attributes 271 | if (bClosing) 272 | { 273 | if (p.current != '>') 274 | return null; 275 | 276 | p.SkipForward(1); 277 | return tag; 278 | } 279 | 280 | 281 | while (!p.eof) 282 | { 283 | // Skip whitespace 284 | p.SkipWhitespace(); 285 | 286 | // Check for closed tag eg:
287 | if (p.SkipString("/>")) 288 | { 289 | tag.m_closed=true; 290 | return tag; 291 | } 292 | 293 | // End of tag? 294 | if (p.SkipChar('>')) 295 | { 296 | return tag; 297 | } 298 | 299 | // attribute name 300 | string attributeName = null; 301 | if (!p.SkipIdentifier(ref attributeName)) 302 | return null; 303 | 304 | // Skip whitespace 305 | p.SkipWhitespace(); 306 | 307 | // Skip equal sign 308 | if (p.SkipChar('=')) 309 | { 310 | // Skip whitespace 311 | p.SkipWhitespace(); 312 | 313 | // Optional quotes 314 | if (p.SkipChar('\"')) 315 | { 316 | // Scan the value 317 | p.Mark(); 318 | if (!p.Find('\"')) 319 | return null; 320 | 321 | // Store the value 322 | tag.m_attributes.Add(attributeName, p.Extract()); 323 | 324 | // Skip closing quote 325 | p.SkipForward(1); 326 | } 327 | else 328 | { 329 | // Scan the value 330 | p.Mark(); 331 | while (!p.eof && !char.IsWhiteSpace(p.current) && p.current != '>' && p.current != '/') 332 | p.SkipForward(1); 333 | 334 | if (!p.eof) 335 | { 336 | // Store the value 337 | tag.m_attributes.Add(attributeName, p.Extract()); 338 | } 339 | } 340 | } 341 | else 342 | { 343 | tag.m_attributes.Add(attributeName, ""); 344 | } 345 | } 346 | 347 | return null; 348 | } 349 | 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/LinkDefinition.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | public class LinkDefinition 23 | { 24 | public LinkDefinition(string id) 25 | { 26 | this.id= id; 27 | } 28 | 29 | public LinkDefinition(string id, string url) 30 | { 31 | this.id = id; 32 | this.url = url; 33 | } 34 | 35 | public LinkDefinition(string id, string url, string title) 36 | { 37 | this.id = id; 38 | this.url = url; 39 | this.title = title; 40 | } 41 | 42 | public string id 43 | { 44 | get; 45 | set; 46 | } 47 | 48 | public string url 49 | { 50 | get; 51 | set; 52 | } 53 | 54 | public string title 55 | { 56 | get; 57 | set; 58 | } 59 | 60 | 61 | internal void RenderLink(Markdown m, StringBuilder b, string link_text) 62 | { 63 | if (url.StartsWith("mailto:")) 64 | { 65 | b.Append("'); 75 | Utils.HtmlRandomize(b, link_text); 76 | b.Append(""); 77 | } 78 | else 79 | { 80 | HtmlTag tag = new HtmlTag("a"); 81 | 82 | // encode url 83 | StringBuilder sb = m.GetStringBuilder(); 84 | Utils.SmartHtmlEncodeAmpsAndAngles(sb, url); 85 | tag.attributes["href"] = sb.ToString(); 86 | 87 | // encode title 88 | if (!String.IsNullOrEmpty(title )) 89 | { 90 | sb.Length = 0; 91 | Utils.SmartHtmlEncodeAmpsAndAngles(sb, title); 92 | tag.attributes["title"] = sb.ToString(); 93 | } 94 | 95 | // Do user processing 96 | m.OnPrepareLink(tag); 97 | 98 | // Render the opening tag 99 | tag.RenderOpening(b); 100 | 101 | b.Append(link_text); // Link text already escaped by SpanFormatter 102 | b.Append(""); 103 | } 104 | } 105 | 106 | internal void RenderImg(Markdown m, StringBuilder b, string alt_text) 107 | { 108 | HtmlTag tag = new HtmlTag("img"); 109 | 110 | // encode url 111 | StringBuilder sb = m.GetStringBuilder(); 112 | Utils.SmartHtmlEncodeAmpsAndAngles(sb, url); 113 | tag.attributes["src"] = sb.ToString(); 114 | 115 | // encode alt text 116 | if (!String.IsNullOrEmpty(alt_text)) 117 | { 118 | sb.Length = 0; 119 | Utils.SmartHtmlEncodeAmpsAndAngles(sb, alt_text); 120 | tag.attributes["alt"] = sb.ToString(); 121 | } 122 | 123 | // encode title 124 | if (!String.IsNullOrEmpty(title)) 125 | { 126 | sb.Length = 0; 127 | Utils.SmartHtmlEncodeAmpsAndAngles(sb, title); 128 | tag.attributes["title"] = sb.ToString(); 129 | } 130 | 131 | tag.closed = true; 132 | 133 | m.OnPrepareImage(tag, m.RenderingTitledImage); 134 | 135 | tag.RenderOpening(b); 136 | } 137 | 138 | 139 | // Parse a link definition from a string (used by test cases) 140 | internal static LinkDefinition ParseLinkDefinition(string str, bool ExtraMode) 141 | { 142 | StringScanner p = new StringScanner(str); 143 | return ParseLinkDefinitionInternal(p, ExtraMode); 144 | } 145 | 146 | // Parse a link definition 147 | internal static LinkDefinition ParseLinkDefinition(StringScanner p, bool ExtraMode) 148 | { 149 | int savepos=p.position; 150 | var l = ParseLinkDefinitionInternal(p, ExtraMode); 151 | if (l==null) 152 | p.position = savepos; 153 | return l; 154 | 155 | } 156 | 157 | internal static LinkDefinition ParseLinkDefinitionInternal(StringScanner p, bool ExtraMode) 158 | { 159 | // Skip leading white space 160 | p.SkipWhitespace(); 161 | 162 | // Must start with an opening square bracket 163 | if (!p.SkipChar('[')) 164 | return null; 165 | 166 | // Extract the id 167 | p.Mark(); 168 | if (!p.Find(']')) 169 | return null; 170 | string id = p.Extract(); 171 | if (id.Length == 0) 172 | return null; 173 | if (!p.SkipString("]:")) 174 | return null; 175 | 176 | // Parse the url and title 177 | var link=ParseLinkTarget(p, id, ExtraMode); 178 | 179 | // and trailing whitespace 180 | p.SkipLinespace(); 181 | 182 | // Trailing crap, not a valid link reference... 183 | if (!p.eol) 184 | return null; 185 | 186 | return link; 187 | } 188 | 189 | // Parse just the link target 190 | // For reference link definition, this is the bit after "[id]: thisbit" 191 | // For inline link, this is the bit in the parens: [link text](thisbit) 192 | internal static LinkDefinition ParseLinkTarget(StringScanner p, string id, bool ExtraMode) 193 | { 194 | // Skip whitespace 195 | p.SkipWhitespace(); 196 | 197 | // End of string? 198 | if (p.eol) 199 | return null; 200 | 201 | // Create the link definition 202 | var r = new LinkDefinition(id); 203 | 204 | // Is the url enclosed in angle brackets 205 | if (p.SkipChar('<')) 206 | { 207 | // Extract the url 208 | p.Mark(); 209 | 210 | // Find end of the url 211 | while (p.current != '>') 212 | { 213 | if (p.eof) 214 | return null; 215 | p.SkipEscapableChar(ExtraMode); 216 | } 217 | 218 | string url = p.Extract(); 219 | if (!p.SkipChar('>')) 220 | return null; 221 | 222 | // Unescape it 223 | r.url = Utils.UnescapeString(url.Trim(), ExtraMode); 224 | 225 | // Skip whitespace 226 | p.SkipWhitespace(); 227 | } 228 | else 229 | { 230 | // Find end of the url 231 | p.Mark(); 232 | int paren_depth = 1; 233 | while (!p.eol) 234 | { 235 | char ch=p.current; 236 | if (char.IsWhiteSpace(ch)) 237 | break; 238 | if (id == null) 239 | { 240 | if (ch == '(') 241 | paren_depth++; 242 | else if (ch == ')') 243 | { 244 | paren_depth--; 245 | if (paren_depth==0) 246 | break; 247 | } 248 | } 249 | 250 | p.SkipEscapableChar(ExtraMode); 251 | } 252 | 253 | r.url = Utils.UnescapeString(p.Extract().Trim(), ExtraMode); 254 | } 255 | 256 | p.SkipLinespace(); 257 | 258 | // End of inline target 259 | if (p.DoesMatch(')')) 260 | return r; 261 | 262 | bool bOnNewLine = p.eol; 263 | int posLineEnd = p.position; 264 | if (p.eol) 265 | { 266 | p.SkipEol(); 267 | p.SkipLinespace(); 268 | } 269 | 270 | // Work out what the title is delimited with 271 | char delim; 272 | switch (p.current) 273 | { 274 | case '\'': 275 | case '\"': 276 | delim = p.current; 277 | break; 278 | 279 | case '(': 280 | delim = ')'; 281 | break; 282 | 283 | default: 284 | if (bOnNewLine) 285 | { 286 | p.position = posLineEnd; 287 | return r; 288 | } 289 | else 290 | return null; 291 | } 292 | 293 | // Skip the opening title delimiter 294 | p.SkipForward(1); 295 | 296 | // Find the end of the title 297 | p.Mark(); 298 | while (true) 299 | { 300 | if (p.eol) 301 | return null; 302 | 303 | if (p.current == delim) 304 | { 305 | 306 | if (delim != ')') 307 | { 308 | int savepos = p.position; 309 | 310 | // Check for embedded quotes in title 311 | 312 | // Skip the quote and any trailing whitespace 313 | p.SkipForward(1); 314 | p.SkipLinespace(); 315 | 316 | // Next we expect either the end of the line for a link definition 317 | // or the close bracket for an inline link 318 | if ((id == null && p.current != ')') || 319 | (id != null && !p.eol)) 320 | { 321 | continue; 322 | } 323 | 324 | p.position = savepos; 325 | } 326 | 327 | // End of title 328 | break; 329 | } 330 | 331 | p.SkipEscapableChar(ExtraMode); 332 | } 333 | 334 | // Store the title 335 | r.title = Utils.UnescapeString(p.Extract(), ExtraMode); 336 | 337 | // Skip closing quote 338 | p.SkipForward(1); 339 | 340 | // Done! 341 | return r; 342 | } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/LinkInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | internal class LinkInfo 23 | { 24 | public LinkInfo(LinkDefinition def, string link_text) 25 | { 26 | this.def = def; 27 | this.link_text = link_text; 28 | } 29 | 30 | public LinkDefinition def; 31 | public string link_text; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/StringScanner.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | /* 23 | * StringScanner is a simple class to help scan through an input string. 24 | * 25 | * Maintains a current position with various operations to inspect the current 26 | * character, skip forward, check for matches, skip whitespace etc... 27 | */ 28 | public class StringScanner 29 | { 30 | // Constructor 31 | public StringScanner() 32 | { 33 | } 34 | 35 | // Constructor 36 | public StringScanner(string str) 37 | { 38 | Reset(str); 39 | } 40 | 41 | // Constructor 42 | public StringScanner(string str, int pos) 43 | { 44 | Reset(str, pos); 45 | } 46 | 47 | // Constructor 48 | public StringScanner(string str, int pos, int len) 49 | { 50 | Reset(str, pos, len); 51 | } 52 | 53 | // Reset 54 | public void Reset(string str) 55 | { 56 | Reset(str, 0, str!=null ? str.Length : 0); 57 | } 58 | 59 | // Reset 60 | public void Reset(string str, int pos) 61 | { 62 | Reset(str, pos, str!=null ? str.Length - pos : 0); 63 | } 64 | 65 | // Reset 66 | public void Reset(string str, int pos, int len) 67 | { 68 | if (str == null) 69 | str = ""; 70 | if (len < 0) 71 | len = 0; 72 | if (pos < 0) 73 | pos = 0; 74 | if (pos > str.Length) 75 | pos = str.Length; 76 | 77 | this.str = str; 78 | this.start = pos; 79 | this.pos = pos; 80 | this.end = pos + len; 81 | 82 | if (end > str.Length) 83 | end = str.Length; 84 | } 85 | 86 | // Get the entire input string 87 | public string input 88 | { 89 | get 90 | { 91 | return str; 92 | } 93 | } 94 | 95 | // Get the character at the current position 96 | public char current 97 | { 98 | get 99 | { 100 | if (pos < start || pos >= end) 101 | return '\0'; 102 | else 103 | return str[pos]; 104 | } 105 | } 106 | 107 | // Get/set the current position 108 | public int position 109 | { 110 | get 111 | { 112 | return pos; 113 | } 114 | set 115 | { 116 | pos = value; 117 | } 118 | } 119 | 120 | // Get the remainder of the input 121 | // (use this in a watch window while debugging :) 122 | public string remainder 123 | { 124 | get 125 | { 126 | return Substring(position); 127 | } 128 | } 129 | 130 | // Skip to the end of file 131 | public void SkipToEof() 132 | { 133 | pos = end; 134 | } 135 | 136 | 137 | // Skip to the end of the current line 138 | public void SkipToEol() 139 | { 140 | while (pos < end) 141 | { 142 | char ch=str[pos]; 143 | if (ch=='\r' || ch=='\n') 144 | break; 145 | pos++; 146 | } 147 | } 148 | 149 | // Skip if currently at a line end 150 | public bool SkipEol() 151 | { 152 | if (pos < end) 153 | { 154 | char ch = str[pos]; 155 | if (ch == '\r') 156 | { 157 | pos++; 158 | if (pos < end && str[pos] == '\n') 159 | pos++; 160 | return true; 161 | } 162 | 163 | else if (ch == '\n') 164 | { 165 | pos++; 166 | if (pos < end && str[pos] == '\r') 167 | pos++; 168 | return true; 169 | } 170 | } 171 | 172 | return false; 173 | } 174 | 175 | // Skip to the next line 176 | public void SkipToNextLine() 177 | { 178 | SkipToEol(); 179 | SkipEol(); 180 | } 181 | 182 | // Get the character at offset from current position 183 | // Or, \0 if out of range 184 | public char CharAtOffset(int offset) 185 | { 186 | int index = pos + offset; 187 | 188 | if (index < start) 189 | return '\0'; 190 | if (index >= end) 191 | return '\0'; 192 | return str[index]; 193 | } 194 | 195 | // Skip a number of characters 196 | public void SkipForward(int characters) 197 | { 198 | pos += characters; 199 | } 200 | 201 | // Skip a character if present 202 | public bool SkipChar(char ch) 203 | { 204 | if (current == ch) 205 | { 206 | SkipForward(1); 207 | return true; 208 | } 209 | 210 | return false; 211 | } 212 | 213 | // Skip a matching string 214 | public bool SkipString(string str) 215 | { 216 | if (DoesMatch(str)) 217 | { 218 | SkipForward(str.Length); 219 | return true; 220 | } 221 | 222 | return false; 223 | } 224 | 225 | // Skip a matching string 226 | public bool SkipStringI(string str) 227 | { 228 | if (DoesMatchI(str)) 229 | { 230 | SkipForward(str.Length); 231 | return true; 232 | } 233 | 234 | return false; 235 | } 236 | 237 | // Skip any whitespace 238 | public bool SkipWhitespace() 239 | { 240 | if (!char.IsWhiteSpace(current)) 241 | return false; 242 | SkipForward(1); 243 | 244 | while (char.IsWhiteSpace(current)) 245 | SkipForward(1); 246 | 247 | return true; 248 | } 249 | 250 | // Check if a character is space or tab 251 | public static bool IsLineSpace(char ch) 252 | { 253 | return ch == ' ' || ch == '\t'; 254 | } 255 | 256 | // Skip spaces and tabs 257 | public bool SkipLinespace() 258 | { 259 | if (!IsLineSpace(current)) 260 | return false; 261 | SkipForward(1); 262 | 263 | while (IsLineSpace(current)) 264 | SkipForward(1); 265 | 266 | return true; 267 | } 268 | 269 | // Does current character match something 270 | public bool DoesMatch(char ch) 271 | { 272 | return current == ch; 273 | } 274 | 275 | // Does character at offset match a character 276 | public bool DoesMatch(int offset, char ch) 277 | { 278 | return CharAtOffset(offset) == ch; 279 | } 280 | 281 | // Does current character match any of a range of characters 282 | public bool DoesMatchAny(char[] chars) 283 | { 284 | for (int i = 0; i < chars.Length; i++) 285 | { 286 | if (DoesMatch(chars[i])) 287 | return true; 288 | } 289 | return false; 290 | } 291 | 292 | // Does current character match any of a range of characters 293 | public bool DoesMatchAny(int offset, char[] chars) 294 | { 295 | for (int i = 0; i < chars.Length; i++) 296 | { 297 | if (DoesMatch(offset, chars[i])) 298 | return true; 299 | } 300 | return false; 301 | } 302 | 303 | // Does current string position match a string 304 | public bool DoesMatch(string str) 305 | { 306 | for (int i = 0; i < str.Length; i++) 307 | { 308 | if (str[i] != CharAtOffset(i)) 309 | return false; 310 | } 311 | return true; 312 | } 313 | 314 | // Does current string position match a string 315 | public bool DoesMatchI(string str) 316 | { 317 | return string.Compare(str, Substring(position, str.Length), StringComparison.OrdinalIgnoreCase) == 0; 318 | } 319 | 320 | // Extract a substring 321 | public string Substring(int start) 322 | { 323 | return str.Substring(start, end-start); 324 | } 325 | 326 | // Extract a substring 327 | public string Substring(int start, int len) 328 | { 329 | if (start + len > end) 330 | len = end - start; 331 | 332 | return str.Substring(start, len); 333 | } 334 | 335 | // Scan forward for a character 336 | public bool Find(char ch) 337 | { 338 | if (pos >= end) 339 | return false; 340 | 341 | // Find it 342 | int index = str.IndexOf(ch, pos); 343 | if (index < 0 || index>=end) 344 | return false; 345 | 346 | // Store new position 347 | pos = index; 348 | return true; 349 | } 350 | 351 | // Find any of a range of characters 352 | public bool FindAny(char[] chars) 353 | { 354 | if (pos >= end) 355 | return false; 356 | 357 | // Find it 358 | int index = str.IndexOfAny(chars, pos); 359 | if (index < 0 || index>=end) 360 | return false; 361 | 362 | // Store new position 363 | pos = index; 364 | return true; 365 | } 366 | 367 | // Forward scan for a string 368 | public bool Find(string find) 369 | { 370 | if (pos >= end) 371 | return false; 372 | 373 | int index = str.IndexOf(find, pos); 374 | if (index < 0 || index > end-find.Length) 375 | return false; 376 | 377 | pos = index; 378 | return true; 379 | } 380 | 381 | // Forward scan for a string (case insensitive) 382 | public bool FindI(string find) 383 | { 384 | if (pos >= end) 385 | return false; 386 | 387 | int index = str.IndexOf(find, pos, StringComparison.OrdinalIgnoreCase); 388 | if (index < 0 || index >= end - find.Length) 389 | return false; 390 | 391 | pos = index; 392 | return true; 393 | } 394 | 395 | // Are we at eof? 396 | public bool eof 397 | { 398 | get 399 | { 400 | return pos >= end; 401 | } 402 | } 403 | 404 | // Are we at eol? 405 | public bool eol 406 | { 407 | get 408 | { 409 | return IsLineEnd(current); 410 | } 411 | } 412 | 413 | // Are we at bof? 414 | public bool bof 415 | { 416 | get 417 | { 418 | return pos == start; 419 | } 420 | } 421 | 422 | // Mark current position 423 | public void Mark() 424 | { 425 | mark = pos; 426 | } 427 | 428 | // Extract string from mark to current position 429 | public string Extract() 430 | { 431 | if (mark >= pos) 432 | return ""; 433 | 434 | return str.Substring(mark, pos - mark); 435 | } 436 | 437 | // Skip an identifier 438 | public bool SkipIdentifier(ref string identifier) 439 | { 440 | int savepos = position; 441 | if (!Utils.ParseIdentifier(this.str, ref pos, ref identifier)) 442 | return false; 443 | if (pos >= end) 444 | { 445 | pos = savepos; 446 | return false; 447 | } 448 | return true; 449 | } 450 | 451 | public bool SkipFootnoteID(out string id) 452 | { 453 | int savepos = position; 454 | 455 | SkipLinespace(); 456 | 457 | Mark(); 458 | 459 | while (true) 460 | { 461 | char ch = current; 462 | if (char.IsLetterOrDigit(ch) || ch == '-' || ch == '_' || ch == ':' || ch == '.' || ch == ' ') 463 | SkipForward(1); 464 | else 465 | break; 466 | } 467 | 468 | if (position > mark) 469 | { 470 | id = Extract().Trim(); 471 | if (!String.IsNullOrEmpty(id)) 472 | { 473 | SkipLinespace(); 474 | return true; 475 | } 476 | } 477 | 478 | position = savepos; 479 | id = null; 480 | return false; 481 | } 482 | 483 | // Skip a Html entity (eg: &) 484 | public bool SkipHtmlEntity(ref string entity) 485 | { 486 | int savepos = position; 487 | if (!Utils.SkipHtmlEntity(this.str, ref pos, ref entity)) 488 | return false; 489 | if (pos > end) 490 | { 491 | pos = savepos; 492 | return false; 493 | } 494 | return true; 495 | } 496 | 497 | // Check if a character marks end of line 498 | public static bool IsLineEnd(char ch) 499 | { 500 | return ch == '\r' || ch == '\n' || ch=='\0'; 501 | } 502 | 503 | bool IsUrlChar(char ch) 504 | { 505 | switch (ch) 506 | { 507 | case '+': 508 | case '&': 509 | case '@': 510 | case '#': 511 | case '/': 512 | case '%': 513 | case '?': 514 | case '=': 515 | case '~': 516 | case '_': 517 | case '|': 518 | case '[': 519 | case ']': 520 | case '(': 521 | case ')': 522 | case '!': 523 | case ':': 524 | case ',': 525 | case '.': 526 | case ';': 527 | return true; 528 | 529 | default: 530 | return Char.IsLetterOrDigit(ch); 531 | } 532 | } 533 | 534 | // Attributes 535 | string str; 536 | int start; 537 | int pos; 538 | int end; 539 | int mark; 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/TableSpec.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | internal enum ColumnAlignment 23 | { 24 | NA, 25 | Left, 26 | Right, 27 | Center, 28 | } 29 | internal class TableSpec 30 | { 31 | public TableSpec() 32 | { 33 | } 34 | 35 | public bool LeadingBar; 36 | public bool TrailingBar; 37 | 38 | public List Columns=new List(); 39 | 40 | public List Headers; 41 | public List> Rows=new List>(); 42 | 43 | public List ParseRow(StringScanner p) 44 | { 45 | p.SkipLinespace(); 46 | 47 | if (p.eol) 48 | return null; // Blank line ends the table 49 | 50 | bool bAnyBars=LeadingBar; 51 | if (LeadingBar && !p.SkipChar('|')) 52 | { 53 | return null; 54 | } 55 | 56 | // Create the row 57 | List row = new List(); 58 | 59 | // Parse all columns except the last 60 | 61 | while (!p.eol) 62 | { 63 | // Find the next vertical bar 64 | p.Mark(); 65 | while (!p.eol && p.current != '|') 66 | p.SkipEscapableChar(true); 67 | 68 | row.Add(p.Extract().Trim()); 69 | 70 | bAnyBars|=p.SkipChar('|'); 71 | } 72 | 73 | // Require at least one bar to continue the table 74 | if (!bAnyBars) 75 | return null; 76 | 77 | // Add missing columns 78 | while (row.Count < Columns.Count) 79 | { 80 | row.Add(" "); 81 | } 82 | 83 | p.SkipEol(); 84 | return row; 85 | } 86 | 87 | internal void RenderRow(Markdown m, StringBuilder b, List row, string type) 88 | { 89 | for (int i=0; i"); 111 | m.SpanFormatter.Format(b, row[i]); 112 | b.Append("\n"); 115 | } 116 | } 117 | 118 | public void Render(Markdown m, StringBuilder b) 119 | { 120 | b.Append("\n"); 121 | if (Headers != null) 122 | { 123 | b.Append("\n\n"); 124 | RenderRow(m, b, Headers, "th"); 125 | b.Append("\n\n"); 126 | } 127 | 128 | b.Append("\n"); 129 | foreach (var row in Rows) 130 | { 131 | b.Append("\n"); 132 | RenderRow(m, b, row, "td"); 133 | b.Append("\n"); 134 | } 135 | b.Append("\n"); 136 | 137 | b.Append("
\n"); 138 | } 139 | 140 | public static TableSpec Parse(StringScanner p) 141 | { 142 | // Leading line space allowed 143 | p.SkipLinespace(); 144 | 145 | // Quick check for typical case 146 | if (p.current != '|' && p.current != ':' && p.current != '-') 147 | return null; 148 | 149 | // Don't create the spec until it at least looks like one 150 | TableSpec spec = null; 151 | 152 | // Leading bar, looks like a table spec 153 | if (p.SkipChar('|')) 154 | { 155 | spec=new TableSpec(); 156 | spec.LeadingBar=true; 157 | } 158 | 159 | 160 | // Process all columns 161 | while (true) 162 | { 163 | // Parse column spec 164 | p.SkipLinespace(); 165 | 166 | // Must have something in the spec 167 | if (p.current == '|') 168 | return null; 169 | 170 | bool AlignLeft = p.SkipChar(':'); 171 | while (p.current == '-') 172 | p.SkipForward(1); 173 | bool AlignRight = p.SkipChar(':'); 174 | p.SkipLinespace(); 175 | 176 | // Work out column alignment 177 | ColumnAlignment col = ColumnAlignment.NA; 178 | if (AlignLeft && AlignRight) 179 | col = ColumnAlignment.Center; 180 | else if (AlignLeft) 181 | col = ColumnAlignment.Left; 182 | else if (AlignRight) 183 | col = ColumnAlignment.Right; 184 | 185 | if (p.eol) 186 | { 187 | // Not a spec? 188 | if (spec == null) 189 | return null; 190 | 191 | // Add the final spec? 192 | spec.Columns.Add(col); 193 | return spec; 194 | } 195 | 196 | // We expect a vertical bar 197 | if (!p.SkipChar('|')) 198 | return null; 199 | 200 | // Create the table spec 201 | if (spec==null) 202 | spec=new TableSpec(); 203 | 204 | // Add the column 205 | spec.Columns.Add(col); 206 | 207 | // Check for trailing vertical bar 208 | p.SkipLinespace(); 209 | if (p.eol) 210 | { 211 | spec.TrailingBar = true; 212 | return spec; 213 | } 214 | 215 | // Next column 216 | } 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownDeep/Token.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownDeep - http://www.toptensoftware.com/markdowndeep 3 | // Copyright (C) 2010-2011 Topten Software 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License is 11 | // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and limitations under the License. 13 | // 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | namespace MarkdownDeep 21 | { 22 | /* 23 | * Token is used to mark out various special parts of a string being 24 | * formatted by SpanFormatter. 25 | * 26 | * Strings aren't actually stored in the token - just their offset 27 | * and length in the input string. 28 | * 29 | * For performance, Token's are pooled and reused. 30 | * See SpanFormatter.CreateToken() 31 | */ 32 | 33 | // TokenType - what sort of token? 34 | internal enum TokenType 35 | { 36 | Text, // Plain text, should be htmlencoded 37 | HtmlTag, // Valid html tag, write out directly but escape &s; 38 | Html, // Valid html, write out directly 39 | open_em, // 40 | close_em, // 41 | open_strong, // 42 | close_strong, // 43 | code_span, // 44 | br, //
45 | 46 | link, // , data = LinkInfo 47 | img, // , data = LinkInfo 48 | footnote, // Footnote reference 49 | abbreviation, // An abbreviation, data is a reference to Abbrevation instance 50 | 51 | // These are used during construction of and tokens 52 | opening_mark, // opening '*' or '_' 53 | closing_mark, // closing '*' or '_' 54 | internal_mark, // internal '*' or '_' 55 | } 56 | 57 | // Token 58 | internal class Token 59 | { 60 | // Constructor 61 | public Token(TokenType type, int startOffset, int length) 62 | { 63 | this.type = type; 64 | this.startOffset = startOffset; 65 | this.length = length; 66 | } 67 | 68 | // Constructor 69 | public Token(TokenType type, object data) 70 | { 71 | this.type = type; 72 | this.data = data; 73 | } 74 | 75 | public override string ToString() 76 | { 77 | if (true || data == null) 78 | { 79 | return string.Format("{0} - {1} - {2}", type.ToString(), startOffset, length); 80 | } 81 | else 82 | { 83 | return string.Format("{0} - {1} - {2} -> {3}", type.ToString(), startOffset, length, data.ToString()); 84 | } 85 | } 86 | 87 | public TokenType type; 88 | public int startOffset; 89 | public int length; 90 | public object data; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownElement.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public abstract class MarkdownElement : IMarkdownElement 29 | { 30 | public abstract string ToMarkdown(); 31 | 32 | public override string ToString() 33 | { 34 | return ToMarkdown(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /MarkdownLog/MarkdownLog.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MarkdownLog 5 | MarkdownLog 6 | netstandard2.0 7 | 0.9.0 8 | wheelies 9 | https://github.com/Wheelies/MarkdownLog/blob/master/LICENSE 10 | https://github.com/Wheelies/MarkdownLog 11 | Lightweight .NET component for programmatically generating Markdown. Useful for producing rich diagnostic logs with minimal dependencies. 12 | Copyright 2014 BlackJet Software Ltd 13 | markdown;diagnostics;logging;pcl 14 | true 15 | true 16 | ..\MarkdownLog.snk 17 | true 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /MarkdownLog/MarkdownToHtmlConverter.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class MarkdownToHtmlConverter 29 | { 30 | public string Transform(string markdown) 31 | { 32 | var markdownDeep = new MarkdownDeep.Markdown { ExtraMode = true }; 33 | return markdownDeep.Transform(markdown); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /MarkdownLog/MarkdownToHtmlExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public static class MarkdownToHtmlExtensions 29 | { 30 | public static string ToHtml(this IMarkdownElement markdownElement) 31 | { 32 | var md = markdownElement.ToMarkdown(); 33 | 34 | return MarkdownToHtml(md); 35 | } 36 | 37 | public static string MarkdownToHtml(this string markdown) 38 | { 39 | var converter = new MarkdownToHtmlConverter(); 40 | return converter.Transform(markdown); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /MarkdownLog/NumberExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | 28 | namespace MarkdownLog 29 | { 30 | public static class NumberExtensions 31 | { 32 | public static string ToColumnTitle(this int columnIndex) 33 | { 34 | int dividend = columnIndex + 1; 35 | string columnName = String.Empty; 36 | 37 | while (dividend > 0) 38 | { 39 | int modulo = (dividend - 1) % 26; 40 | columnName = Convert.ToChar(65 + modulo) + columnName; 41 | dividend = (dividend - modulo) / 26; 42 | } 43 | 44 | return columnName; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /MarkdownLog/NumberedList.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System.Collections.Generic; 27 | 28 | namespace MarkdownLog 29 | { 30 | public class NumberedList : ListBase 31 | { 32 | public NumberedList(params string[] items) : base(items) 33 | { 34 | } 35 | 36 | public NumberedList(IEnumerable items) : base(items) 37 | { 38 | } 39 | 40 | protected override string GetListItemFirstLinePrefix(int itemNumber) 41 | { 42 | return string.Format("{0,4}. ", itemNumber); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /MarkdownLog/Paragraph.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Linq; 28 | 29 | namespace MarkdownLog 30 | { 31 | public class Paragraph : MarkdownElement 32 | { 33 | private readonly string _text; 34 | private int _wordWrapColumn; 35 | 36 | public Paragraph(string format, params object[] args) : this(string.Format(format, args)) 37 | { 38 | } 39 | 40 | public Paragraph(string text) 41 | { 42 | _text = text; 43 | WordWrap = true; 44 | WordWrapColumn = 80; 45 | } 46 | 47 | public bool WordWrap { get; set; } 48 | 49 | public int WordWrapColumn 50 | { 51 | get { return _wordWrapColumn; } 52 | set { _wordWrapColumn = Math.Max(0, value); } 53 | } 54 | 55 | public override string ToMarkdown() 56 | { 57 | var originalLines = _text.SplitByLine(); 58 | var linesWithoutFinalEmptyLine = (originalLines.Last() == "") 59 | ? originalLines.Take(originalLines.Count - 1) 60 | : originalLines; 61 | 62 | var lines = linesWithoutFinalEmptyLine.Select(line => 63 | { 64 | var escapedLine = line.EscapeMarkdownCharacters(); 65 | var wrapped = WordWrap ? escapedLine.WrapAt(WordWrapColumn) : escapedLine; 66 | return string.Join(Environment.NewLine, wrapped.SplitByLine()); 67 | }); 68 | 69 | return string.Join(" " + Environment.NewLine, lines) + Environment.NewLine; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /MarkdownLog/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("MarkdownLog.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100911cf1ef9ef937f2eb2da1bdc6fdb7f4eff0f9353c60bd0b3721aab344cafb69783d58443c29e68b7afe7740f8747127a8d5eb61abf74d71b290c068de0a868ab32b2563622608bb86cda358b49b6e91017a26631089894de4e1808a6f17413cb3811fcf5d75e95c524842a5808f97f25b41f039aa74ed7700fb6413eb5643c8")] -------------------------------------------------------------------------------- /MarkdownLog/RawMarkdown.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class RawMarkdown : MarkdownElement 29 | { 30 | private readonly string _markdown; 31 | 32 | public RawMarkdown(string markdown) 33 | { 34 | _markdown = markdown ?? ""; 35 | } 36 | 37 | public override string ToMarkdown() 38 | { 39 | return _markdown; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MarkdownLog/ReflectionExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections; 28 | using System.Collections.Generic; 29 | using System.Reflection; 30 | 31 | namespace MarkdownLog 32 | { 33 | public static class ReflectionExtensions 34 | { 35 | public static bool IsNumeric(this Type type) 36 | { 37 | return type.IsWholeNumber() || type.IsFloatingPointNumber(); 38 | } 39 | 40 | public static bool IsFloatingPointNumber(this Type type) 41 | { 42 | if (type == null) 43 | { 44 | return false; 45 | } 46 | 47 | switch (Type.GetTypeCode(type)) 48 | { 49 | case TypeCode.Decimal: 50 | case TypeCode.Double: 51 | case TypeCode.Single: 52 | return true; 53 | case TypeCode.Object: 54 | if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 55 | { 56 | return Nullable.GetUnderlyingType(type).IsFloatingPointNumber(); 57 | } 58 | return false; 59 | } 60 | return false; 61 | } 62 | 63 | public static bool IsWholeNumber(this Type type) 64 | { 65 | if (type == null) 66 | { 67 | return false; 68 | } 69 | 70 | switch (Type.GetTypeCode(type)) 71 | { 72 | case TypeCode.Byte: 73 | case TypeCode.Int16: 74 | case TypeCode.Int32: 75 | case TypeCode.Int64: 76 | case TypeCode.SByte: 77 | case TypeCode.UInt16: 78 | case TypeCode.UInt32: 79 | case TypeCode.UInt64: 80 | return true; 81 | case TypeCode.Object: 82 | if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 83 | { 84 | return Nullable.GetUnderlyingType(type).IsWholeNumber(); 85 | } 86 | return false; 87 | } 88 | return false; 89 | } 90 | 91 | public static bool IsEnumerable(this Type type) 92 | { 93 | var isGenericEnumerable = typeof(IEnumerable<>).IsAssignableFrom(type); 94 | var legacyEnumerable = typeof(IEnumerable).IsAssignableFrom(type); 95 | 96 | return isGenericEnumerable || 97 | legacyEnumerable; 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /MarkdownLog/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.IO; 29 | using System.Linq; 30 | using System.Text; 31 | using System.Text.RegularExpressions; 32 | 33 | namespace MarkdownLog 34 | { 35 | public static class StringExtensions 36 | { 37 | public static string Indent(this string text, int indentSize) 38 | { 39 | return text.PrependAllLines(new string(' ', indentSize)); 40 | } 41 | 42 | public static string IndentAllExceptFirst(this string text, int indentSize) 43 | { 44 | return text.PrependLines(new string(' ', indentSize), 1); 45 | } 46 | 47 | public static string PrependAllLines(this string text, string prefix) 48 | { 49 | return text.PrependLines(prefix); 50 | } 51 | 52 | private static string PrependLines(this string text, string prefix, int numberToSkip = 0) 53 | { 54 | var lines = text.SplitByLine(); 55 | var prependedLines = lines.Skip(numberToSkip).Select(i => prefix + i); 56 | var prefixedText = string.Join(Environment.NewLine, lines.Take(numberToSkip).Concat(prependedLines)); 57 | return prefixedText; 58 | } 59 | 60 | public static IList SplitByLine(this string text) 61 | { 62 | return text.Split(new[] {"\r\n", "\n\r", "\n", "\r"}, StringSplitOptions.None); 63 | } 64 | 65 | public static string WrapAt(this string text, int maxCharsPerLine) 66 | { 67 | var words = text.Split(' '); 68 | var sb = new StringBuilder(); 69 | var charsPerLine = 0; 70 | foreach (string word in words) 71 | { 72 | if (charsPerLine + word.Length < maxCharsPerLine) 73 | { 74 | sb.Append(word); 75 | 76 | if (word != words.Last()) 77 | { 78 | sb.Append(" "); 79 | } 80 | charsPerLine += word.Length + 1; 81 | } 82 | else 83 | { 84 | sb.Append(Environment.NewLine + word + " "); 85 | charsPerLine = word.Length + 1; 86 | } 87 | } 88 | 89 | return sb.ToString(); 90 | } 91 | 92 | public static string EscapeMarkdownCharacters(this string text) 93 | { 94 | var escapedText = text 95 | .Replace(@"\", @"\\") 96 | .Replace("`", @"\`") 97 | .Replace("*", @"\*") 98 | .Replace("_", @"\_"); 99 | 100 | if (escapedText.StartsWith("#")) 101 | { 102 | escapedText = @"\" + escapedText; 103 | } 104 | else 105 | { 106 | escapedText = Regex.Replace(escapedText, @"(?[0-9]+)\. ", @"${Number}\. "); 107 | } 108 | 109 | return escapedText; 110 | } 111 | 112 | public static string Align(this string text, TableColumnAlignment alignment, int width) 113 | { 114 | switch (alignment) 115 | { 116 | case TableColumnAlignment.Center: 117 | var leftPad = Math.Max(0, (width - text.Length) / 2); 118 | var rightPad = Math.Max(0, width - text.Length - leftPad); 119 | var paddedString = string.Format("{0}{1}{2}", new String(' ', leftPad), text, new String(' ', rightPad)); 120 | return paddedString; 121 | case TableColumnAlignment.Right: 122 | return text.PadLeft(width); 123 | case TableColumnAlignment.Left: 124 | case TableColumnAlignment.Unspecified: 125 | default: 126 | return text.PadRight(width); 127 | } 128 | } 129 | 130 | public static string EscapeCSharpString(this string text) 131 | { 132 | return text 133 | .Replace(@"\", @"\\") 134 | .Replace("\n", @"\n") 135 | .Replace("\r", @"\r") 136 | .Replace("\t", @"\t") 137 | .Replace("\f", @"\f") 138 | .Replace("\0", @"\0") 139 | .Replace("\a", @"\a") 140 | .Replace("\b", @"\b"); 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /MarkdownLog/SubHeader.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class SubHeader : HeaderBase 29 | { 30 | public SubHeader(string format, params object[] args) : this(string.Format(format, args)) { } 31 | 32 | public SubHeader(string text) : base(text, '-') 33 | { 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /MarkdownLog/Table.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace MarkdownLog 32 | { 33 | public class Table : MarkdownElement 34 | { 35 | private static readonly EmptyTableCell EmptyCell = new EmptyTableCell(); 36 | 37 | private IEnumerable _rows = new List(); 38 | private IEnumerable _columns = new List(); 39 | 40 | public IEnumerable Rows 41 | { 42 | get { return _rows; } 43 | set { _rows = value ?? Enumerable.Empty(); } 44 | } 45 | 46 | public IEnumerable Columns 47 | { 48 | get { return _columns; } 49 | set { _columns = value ?? new List(); } 50 | } 51 | 52 | public override string ToMarkdown() 53 | { 54 | var markdownBuilder = new MarkdownBuilder(this); 55 | return markdownBuilder.Build(); 56 | } 57 | 58 | private class MarkdownBuilder 59 | { 60 | private class Row 61 | { 62 | public IList Cells { get; set; } 63 | } 64 | 65 | private readonly List _rows; 66 | private readonly List _columns; 67 | private readonly StringBuilder _builder = new StringBuilder(); 68 | private readonly IList _columnRenderSpecs; 69 | 70 | internal MarkdownBuilder(Table table) 71 | { 72 | _columns = table.Columns.ToList(); 73 | _rows = table.Rows.Select(row => new Row {Cells = row.Cells.ToList()}).ToList(); 74 | 75 | var columnCount = Math.Max(_columns.Count, _rows.Any() ? _rows.Max(r => r.Cells.Count) : 0); 76 | _columnRenderSpecs = Enumerable.Range(0, columnCount).Select(BuildColumnSpecification).ToList(); 77 | } 78 | 79 | private TableCellRenderSpecification BuildColumnSpecification(int column) 80 | { 81 | return new TableCellRenderSpecification(GetColumnAt(column).Alignment, GetMaximumCellWidth(column)); 82 | } 83 | 84 | internal string Build() 85 | { 86 | BuildHeaderRow(); 87 | BuildDividerRow(); 88 | 89 | foreach (var row in _rows) 90 | { 91 | BuildBodyRow(row); 92 | } 93 | 94 | return _builder.ToString(); 95 | } 96 | 97 | private void BuildHeaderRow() 98 | { 99 | var headerCells = (from column in Enumerable.Range(0, _columnRenderSpecs.Count) 100 | let cell = GetColumnAt(column).HeaderCell 101 | let text = BuildCellMarkdownCode(column, cell) 102 | select text).ToList(); 103 | 104 | _builder.Append(" "); 105 | _builder.AppendLine(" " + string.Join(" | ", headerCells)); 106 | } 107 | 108 | private void BuildDividerRow() 109 | { 110 | _builder.Append(" "); 111 | _builder.AppendLine(string.Join("|", _columnRenderSpecs.Select(BuildDividerCell))); 112 | } 113 | 114 | private static string BuildDividerCell(TableCellRenderSpecification spec) 115 | { 116 | var dashes = new string('-', spec.MaximumWidth); 117 | 118 | switch (spec.Alignment) 119 | { 120 | case TableColumnAlignment.Left: 121 | return ":" + dashes + " "; 122 | case TableColumnAlignment.Center: 123 | return ":" + dashes + ":"; 124 | case TableColumnAlignment.Right: 125 | return " " + dashes + ":"; 126 | default: 127 | return " " + dashes + " "; 128 | 129 | } 130 | } 131 | 132 | private void BuildBodyRow(Row row) 133 | { 134 | var rowCells = (from column in Enumerable.Range(0, _columnRenderSpecs.Count) 135 | let cell = GetCellAt(row.Cells, column) 136 | select BuildCellMarkdownCode(column, cell)).ToList(); 137 | 138 | _builder.Append(" "); 139 | _builder.AppendLine(" " + string.Join(" | ", rowCells)); 140 | } 141 | 142 | private string BuildCellMarkdownCode(int column, ITableCell cell) 143 | { 144 | var columnSpec = _columnRenderSpecs[column]; 145 | var maximumWidth = columnSpec.MaximumWidth; 146 | var cellText = cell.BuildCodeFormattedString(new TableCellRenderSpecification(columnSpec.Alignment, maximumWidth)); 147 | var truncatedCellText = cellText.Length > maximumWidth ? cellText.Substring(0, maximumWidth) : cellText.PadRight(maximumWidth); 148 | 149 | return truncatedCellText; 150 | } 151 | 152 | private int GetMaximumCellWidth(int column) 153 | { 154 | var headerCells = new[] {GetColumnAt(column).HeaderCell}; 155 | var bodyCells = _rows.Select(row => GetCellAt(row.Cells, column)); 156 | var columnCells = headerCells.Concat(bodyCells); 157 | return columnCells.Max(i => i.RequiredWidth); 158 | } 159 | 160 | private TableColumn GetColumnAt(int index) 161 | { 162 | return index < _columns.Count 163 | ? _columns[index] 164 | : CreateDefaultHeaderCell(index); 165 | } 166 | 167 | private static TableColumn CreateDefaultHeaderCell(int columnIndex) 168 | { 169 | // GitHub Flavoured Markdown requires a header cell. If header text isn't provided 170 | // use an Excel-like naming scheme (e.g. A, B, C, .., AA, AB, etc) 171 | 172 | return new TableColumn {HeaderCell = new TableCell {Text = columnIndex.ToColumnTitle()}}; 173 | } 174 | 175 | private ITableCell GetCellAt(IList cells, int index) 176 | { 177 | return index < cells.Count ? cells[index] : EmptyCell; 178 | } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /MarkdownLog/TableCell.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | 28 | namespace MarkdownLog 29 | { 30 | public class TableCell : ITableCell 31 | { 32 | private string _text; 33 | 34 | public string Text 35 | { 36 | get { return _text; } 37 | set { _text = value ?? ""; } 38 | } 39 | 40 | public int RequiredWidth 41 | { 42 | get { return GetEncodedText().Length; } 43 | } 44 | 45 | public string BuildCodeFormattedString(TableCellRenderSpecification spec) 46 | { 47 | var alignment = spec.Alignment; 48 | var maximumWidth = spec.MaximumWidth; 49 | var encodedText = GetEncodedText(); 50 | 51 | return encodedText.Align(alignment, maximumWidth); 52 | } 53 | 54 | private string GetEncodedText() 55 | { 56 | return _text.Trim().EscapeCSharpString(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /MarkdownLog/TableCellRenderSpecification.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class TableCellRenderSpecification 29 | { 30 | private readonly TableColumnAlignment _alignment; 31 | private readonly int _maximumWidth; 32 | 33 | internal TableCellRenderSpecification(TableColumnAlignment alignment, int maximumWidth) 34 | { 35 | _alignment = alignment; 36 | _maximumWidth = maximumWidth; 37 | } 38 | 39 | public TableColumnAlignment Alignment 40 | { 41 | get { return _alignment; } 42 | } 43 | 44 | public int MaximumWidth 45 | { 46 | get { return _maximumWidth; } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /MarkdownLog/TableColumn.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public class TableColumn 29 | { 30 | private ITableCell _headerCell; 31 | 32 | public TableColumn() 33 | { 34 | _headerCell = new EmptyTableCell(); 35 | Alignment = TableColumnAlignment.Unspecified; 36 | } 37 | 38 | public ITableCell HeaderCell 39 | { 40 | get { return _headerCell; } 41 | set { _headerCell = value ?? new EmptyTableCell(); } 42 | } 43 | 44 | public TableColumnAlignment Alignment { get; set; } 45 | } 46 | } -------------------------------------------------------------------------------- /MarkdownLog/TableColumnAlignment.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public enum TableColumnAlignment 29 | { 30 | Unspecified, 31 | Left, 32 | Center, 33 | Right 34 | } 35 | } -------------------------------------------------------------------------------- /MarkdownLog/TableOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarkdownLog 4 | { 5 | [Flags] 6 | public enum TableOptions 7 | { 8 | Default = 0, 9 | ExcludeCollectionProperties = 1 10 | } 11 | } -------------------------------------------------------------------------------- /MarkdownLog/TableRow.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System.Collections.Generic; 27 | using System.Linq; 28 | 29 | namespace MarkdownLog 30 | { 31 | public class TableRow 32 | { 33 | private IEnumerable _cells = Enumerable.Empty(); 34 | 35 | public IEnumerable Cells 36 | { 37 | get { return _cells; } 38 | set { _cells = value ?? Enumerable.Empty(); } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /MarkdownLog/TableView.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace MarkdownLog 32 | { 33 | public class TableView : MarkdownElement 34 | { 35 | private IEnumerable _sections = new List(); 36 | 37 | public TableView() 38 | { 39 | } 40 | 41 | public TableView(IEnumerable cells) 42 | { 43 | Sections = new[] {new TableViewSection {Cells = cells}}; 44 | } 45 | 46 | public IEnumerable Sections 47 | { 48 | get { return _sections; } 49 | set { _sections = value ?? Enumerable.Empty(); } 50 | } 51 | 52 | public override string ToMarkdown() 53 | { 54 | var builder = new StringBuilder(); 55 | var indent = new string(' ', 4); 56 | 57 | var rows = Sections 58 | .Where(i => i.Header != null).Select(i => i.Header.RequiredWidth) 59 | .Concat(Sections.SelectMany(i => i.Cells.Select(j => j.RequiredWidth))) 60 | .ToList(); 61 | 62 | if (!rows.Any()) return ""; 63 | 64 | var widestCell = rows.Max(); 65 | 66 | var horizontalLine = " " + new String('_', widestCell); 67 | var containedHorizontalLine = "|" + new String('_', widestCell) + "|"; 68 | 69 | builder.Append(indent); 70 | builder.AppendLine(horizontalLine); 71 | 72 | foreach (var section in Sections) 73 | { 74 | var isFirstSection = Sections.ElementAt(0) == section; 75 | if (!isFirstSection) 76 | { 77 | builder.Append(indent); 78 | builder.AppendLine(containedHorizontalLine); 79 | } 80 | 81 | if (section.Header != null) 82 | { 83 | builder.Append(indent); 84 | builder.AppendFormat("|{0}|", section.Header.BuildCodeFormattedString(widestCell).PadRight(widestCell)); 85 | builder.AppendLine(); 86 | builder.Append(indent); 87 | builder.AppendLine(containedHorizontalLine); 88 | } 89 | 90 | foreach (var cell in section.Cells) 91 | { 92 | builder.Append(indent); 93 | builder.AppendFormat("|{0}|", cell.BuildCodeFormattedString(widestCell).PadRight(widestCell)); 94 | builder.AppendLine(); 95 | } 96 | } 97 | 98 | builder.Append(indent); 99 | builder.AppendLine(containedHorizontalLine); 100 | 101 | return builder.ToString(); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /MarkdownLog/TableViewCellAccessory.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | namespace MarkdownLog 27 | { 28 | public enum TableViewCellAccessory 29 | { 30 | None, 31 | DisclosureIndicator, 32 | DetailDisclosureButton, 33 | Checkmark, 34 | DetailButton, 35 | } 36 | } -------------------------------------------------------------------------------- /MarkdownLog/TableViewSection.cs: -------------------------------------------------------------------------------- 1 | #region Copyright and license 2 | // /* 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2014 BlackJet Software Ltd 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // */ 25 | #endregion 26 | using System.Collections.Generic; 27 | using System.Linq; 28 | 29 | namespace MarkdownLog 30 | { 31 | public class TableViewSection 32 | { 33 | private IEnumerable _cells = new List(); 34 | 35 | public IIosTableViewHeaderCell Header { get; set; } 36 | 37 | public IEnumerable Cells 38 | { 39 | get { return _cells; } 40 | set { _cells = value ?? Enumerable.Empty(); } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/utok6islevjr35le)](https://ci.appveyor.com/project/Wheelies/markdownlog) 2 | 3 | Markdown is a great format for representing an application's internal data structures for debugging and diagnostic purposes. It is a flexible format that is readable in its raw form yet capable of being transformed into HTML for documentation or reporting. 4 | 5 | MarkdownLog can produce all features described in John Gruber's [original spec](http://daringfireball.net/projects/markdown/) from common .NET types. 6 | 7 | Tables 8 | ------ 9 | 10 | A table can be built from a List of objects: 11 | 12 | ```csharp 13 | var data = new[] 14 | { 15 | new { Year = 1991, Album = "Out of Time", Songs = 11, Rating = "* * * *" }, 16 | new { Year = 1992, Album = "Automatic for the People", Songs = 12, Rating = "* * * * *" }, 17 | new { Year = 1994, Album = "Monster", Songs = 12, Rating = "* * *" } 18 | }; 19 | 20 | Console.Write(data.ToMarkdownTable()); 21 | ``` 22 | 23 | Produces: 24 | 25 |
 26 |      Year | Album                    | Songs | Rating   
 27 |      ----:| ------------------------ | -----:| --------- 
 28 |      1991 | Out of Time              |    11 | * * * *  
 29 |      1992 | Automatic for the People |    12 | * * * * *
 30 |      1994 | Monster                  |    12 | * * *    
 31 | 
32 | 33 | 34 | Once passed through a GitHub-flavoured parser, you get a HTML table, complete with headings and alignments: 35 | 36 | Year | Album | Songs | Rating 37 | ----:| ------------------------ | -----:| --------- 38 | 1991 | Out of Time | 11 | * * * * 39 | 1992 | Automatic for the People | 12 | * * * * * 40 | 1994 | Monster | 12 | * * * 41 | 42 | Lists 43 | ----- 44 | 45 | A collection can be output as a numbered list: 46 | 47 | ```csharp 48 | var planets = new[] { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" }; 49 | 50 | Console.Write(planets.ToMarkdownNumberedList()); 51 | ``` 52 | 53 | Produces: 54 | 55 |
 56 |    1. Mercury
 57 |    2. Venus
 58 |    3. Earth
 59 |    4. Mars
 60 |    5. Jupiter
 61 |    6. Saturn
 62 |    7. Uranus
 63 |    8. Neptune
 64 | 
65 | 66 | When passed through a Markdown parser, this becomes: 67 | 68 | 1. Mercury 69 | 2. Venus 70 | 3. Earth 71 | 4. Mars 72 | 5. Jupiter 73 | 6. Saturn 74 | 7. Uranus 75 | 8. Neptune 76 | 77 | Instead of numbers, you can use bullets: 78 | 79 | ```csharp 80 | var beatles = new[] { "John", "Paul", "Ringo", "George" }; 81 | 82 | Console.Write(beatles.ToMarkdownBulettedList()); 83 | ``` 84 | 85 | Produces: 86 | 87 |
 88 |    * John
 89 |    * Paul
 90 |    * Ringo
 91 |    * George
 92 | 
93 | 94 | And is parsed to: 95 | 96 | * John 97 | * Paul 98 | * Ringo 99 | * George 100 | 101 | 102 | Bar Chart example 103 | ---------------- 104 | 105 | A barchart can be produced from a collection of KeyValue or Tuple objects 106 | 107 | ```csharp 108 | var worldCup = new Dictionary 109 | { 110 | { "Brazil", 5 }, 111 | { "Italy", 4 }, 112 | { "Germany", 4 }, 113 | { "Argentina", 2 }, 114 | { "Uruguay", 2 }, 115 | { "France", 1 }, 116 | { "Spain", 1 }, 117 | { "England", 1 } 118 | }; 119 | 120 | Console.Write(worldCup.ToMarkdownBarChart()); 121 | ``` 122 | 123 | Produces: 124 | 125 |
126 |     Brazil    |#####  5
127 |     Italy     |####  4
128 |     Germany   |####  4
129 |     Argentina |##  2
130 |     Uruguay   |##  2
131 |     France    |#  1
132 |     Spain     |#  1
133 |     England   |#  1
134 |               ------
135 | 
136 | 137 | Bar charts are not supported by standard Markdown. When a barchart is passed through a Markdown parser, it is rendered as a code block that retains its structure. 138 | 139 | A bar chart can be produced from floating point and negative numbers and scaling can be applied as desired: 140 | 141 | 142 | ```csharp 143 | const int valueCount = 20; 144 | var chart = new BarChart 145 | { 146 | ScaleAlways = true, 147 | MaximumChartWidth = 40, 148 | DataPoints = from i in Enumerable.Range(0, valueCount) 149 | let rad = (i * 2.0 * Math.PI) / valueCount 150 | select new BarChartDataPoint 151 | { 152 | CategoryName = string.Format("Cos({0:0.0})", rad), 153 | Value = Math.Cos(rad) 154 | } 155 | }; 156 | ``` 157 | 158 | Produces: 159 | 160 |
161 |     Cos(0.0)                     |####################  1
162 |     Cos(0.3)                     |###################  0.95
163 |     Cos(0.6)                     |################  0.81
164 |     Cos(0.9)                     |############  0.59
165 |     Cos(1.3)                     |######  0.31
166 |     Cos(1.6)                     |  0
167 |     Cos(1.9)               ######|  -0.31
168 |     Cos(2.2)         ############|  -0.59
169 |     Cos(2.5)     ################|  -0.81
170 |     Cos(2.8)  ###################|  -0.95
171 |     Cos(3.1) ####################|  -1
172 |     Cos(3.5)  ###################|  -0.95
173 |     Cos(3.8)     ################|  -0.81
174 |     Cos(4.1)         ############|  -0.59
175 |     Cos(4.4)               ######|  -0.31
176 |     Cos(4.7)                     |  0
177 |     Cos(5.0)                     |######  0.31
178 |     Cos(5.3)                     |############  0.59
179 |     Cos(5.7)                     |################  0.81
180 |     Cos(6.0)                     |###################  0.95
181 |              -----------------------------------------
182 | 
183 | 184 | Paragraphs 185 | ---------- 186 | 187 | Strings can be written as a word-wrapped paragraph: 188 | 189 | ```csharp 190 | var text = "Lolita, light of my life, fire of my loins. My sin, my soul. Lo-lee-ta: the tip of the tongue taking a trip of three steps down the palate to tap, at three, on the teeth. Lo. Lee. Ta."; 191 | 192 | Console.Write(text.ToMarkdownParagraph()); 193 | ``` 194 | 195 | Produces: 196 | 197 |
198 | Lolita, light of my life, fire of my loins. My sin, my soul. Lo-lee-ta: the tip 
199 | of the tongue taking a trip of three steps down the palate to tap, at three, on 
200 | the teeth. Lo. Lee. Ta.
201 | 
202 | 203 | After parsing, this becomes: 204 | 205 | Lolita, light of my life, fire of my loins. My sin, my soul. Lo-lee-ta: the tip of the tongue taking a trip of three steps down the palate to tap, at three, on the teeth. Lo. Lee. Ta. 206 | 207 | --- 208 | 209 | MarkdownLog is developed and maintained by [BlackJet Software](http://blackjetsoftware.com). It was initially designed for use in [iOS app "Shopping UK"](http://shoppingukapp.com/) to assist with performance and smoke testing. It was released as open-source in 2014. 210 | -------------------------------------------------------------------------------- /UnitTests/BarChartTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MarkdownLog; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace UnitTests.MarkdownLog 8 | { 9 | [TestClass] 10 | public class BarChartTests 11 | { 12 | [TestMethod] 13 | public void TestCanPlotBarChart() 14 | { 15 | var chart = new BarChart 16 | { 17 | DataPoints = new[] 18 | { 19 | new BarChartDataPoint {CategoryName = "Brazil", Value = 5}, 20 | new BarChartDataPoint {CategoryName = "Italy", Value = 4}, 21 | new BarChartDataPoint {CategoryName = "Germany", Value = 3}, 22 | new BarChartDataPoint {CategoryName = "Argentina", Value = 2}, 23 | new BarChartDataPoint {CategoryName = "Uruguay", Value = 2}, 24 | new BarChartDataPoint {CategoryName = "France", Value = 1}, 25 | new BarChartDataPoint {CategoryName = "Spain", Value = 1}, 26 | new BarChartDataPoint {CategoryName = "England", Value = 1} 27 | 28 | } 29 | }; 30 | 31 | chart.AssertOutputEquals( 32 | " Brazil |##### 5\r\n" + 33 | " Italy |#### 4\r\n" + 34 | " Germany |### 3\r\n" + 35 | " Argentina |## 2\r\n" + 36 | " Uruguay |## 2\r\n" + 37 | " France |# 1\r\n" + 38 | " Spain |# 1\r\n" + 39 | " England |# 1\r\n" + 40 | " ------\r\n" 41 | , 42 | "
Brazil    |#####  5\n" +
 43 |                 "Italy     |####  4\n" +
 44 |                 "Germany   |###  3\n" +
 45 |                 "Argentina |##  2\n" +
 46 |                 "Uruguay   |##  2\n" +
 47 |                 "France    |#  1\n" +
 48 |                 "Spain     |#  1\n" +
 49 |                 "England   |#  1\n" +
 50 |                 "          ------\n" +
 51 |                 "
\n\n"); 52 | } 53 | 54 | [TestMethod] 55 | public void TestCanPlotBarChartFromDictionary() 56 | { 57 | var data = new Dictionary 58 | { 59 | 60 | {"Russia", 17.1}, 61 | {"Antartica", 14.0}, 62 | {"Canada", 10.0}, 63 | {"China", 9.7}, 64 | {"United States", 9.6} 65 | }; 66 | 67 | data.ToMarkdownBarChart().WriteToTrace(); 68 | } 69 | 70 | [TestMethod] 71 | public void TestDecimalValuesAreRoundedToNearestWhole() 72 | { 73 | var chart = new BarChart 74 | { 75 | DataPoints = new[] 76 | { 77 | new BarChartDataPoint {CategoryName = "United States", Value = 16.2}, 78 | new BarChartDataPoint {CategoryName = "China", Value = 8.4}, 79 | new BarChartDataPoint {CategoryName = "Japan", Value = 6}, 80 | new BarChartDataPoint {CategoryName = "Germany", Value = 3.4}, 81 | new BarChartDataPoint {CategoryName = "France", Value = 2.6}, 82 | new BarChartDataPoint {CategoryName = "United Kingdom", Value = 2.5} 83 | 84 | } 85 | }; 86 | 87 | chart.WriteToTrace(); 88 | } 89 | 90 | [TestMethod] 91 | public void TestVeryLargeValuesAreScaled() 92 | { 93 | var chart = new BarChart 94 | { 95 | DataPoints = new[] 96 | { 97 | new BarChartDataPoint {CategoryName = "China", Value = 1364}, 98 | new BarChartDataPoint {CategoryName = "India", Value = 1244}, 99 | new BarChartDataPoint {CategoryName = "United States", Value = 318}, 100 | new BarChartDataPoint {CategoryName = "Indonesia", Value = 247}, 101 | new BarChartDataPoint {CategoryName = "Brazil", Value = 203} 102 | } 103 | }; 104 | 105 | chart.WriteToTrace(); 106 | } 107 | 108 | [TestMethod] 109 | public void TestVerySmallValuesAreScaled() 110 | { 111 | var chart = new BarChart 112 | { 113 | MaximumDecimalPlaces = 8, 114 | DataPoints = new[] 115 | { 116 | new BarChartDataPoint {CategoryName = "Length of a mosquito", Value = 0.015}, 117 | new BarChartDataPoint {CategoryName = "Length of a red ant ", Value = 0.005}, 118 | new BarChartDataPoint {CategoryName = "Human Hair thickness", Value = 0.0001}, 119 | new BarChartDataPoint {CategoryName = "Length of red blood cell", Value = 0.000008} 120 | } 121 | }; 122 | 123 | chart.WriteToTrace(); 124 | } 125 | 126 | [TestMethod] 127 | public void TestNegativeValuesCanBePlotted() 128 | { 129 | var chart = new BarChart 130 | { 131 | DataPoints = new[] 132 | { 133 | new BarChartDataPoint {CategoryName = "United States", Value = -17.3}, 134 | new BarChartDataPoint {CategoryName = "United Kingdom ", Value = -10.1}, 135 | new BarChartDataPoint {CategoryName = "Germany", Value = -5.7}, 136 | new BarChartDataPoint {CategoryName = "France", Value = -5.3}, 137 | new BarChartDataPoint {CategoryName = "Japan", Value = -3} 138 | } 139 | }; 140 | 141 | chart.WriteToTrace(); 142 | } 143 | 144 | 145 | [TestMethod] 146 | public void TestChartWidthCanBeRestricted() 147 | { 148 | var chart = new BarChart 149 | { 150 | MaximumChartWidth = 10, 151 | DataPoints = new[] 152 | { 153 | new BarChartDataPoint {CategoryName = "Elvis Presley", Value = 21}, 154 | new BarChartDataPoint {CategoryName = "The Beatles ", Value = 17}, 155 | new BarChartDataPoint {CategoryName = "Westlife", Value = 14}, 156 | new BarChartDataPoint {CategoryName = "Cliff Richard", Value = 14}, 157 | new BarChartDataPoint {CategoryName = "Madonna", Value = 13} 158 | } 159 | }; 160 | 161 | chart.WriteToTrace(); 162 | } 163 | 164 | [TestMethod] 165 | public void TestScalingCanBeSpecified() 166 | { 167 | const int valueCount = 20; 168 | var chart = new BarChart 169 | { 170 | ScaleAlways = true, 171 | MaximumChartWidth = 40, 172 | DataPoints = from i in Enumerable.Range(0, valueCount) 173 | let rad = (i*2.0*Math.PI)/valueCount 174 | select new BarChartDataPoint 175 | { 176 | CategoryName = string.Format("Cos({0:0.0})", rad), 177 | Value = Math.Cos(rad) 178 | } 179 | }; 180 | 181 | chart.WriteToTrace(); 182 | } 183 | 184 | [TestMethod] 185 | public void TestCanPlotBarChartWithNoData() 186 | { 187 | var chart = new BarChart(); 188 | chart.WriteToTrace(); 189 | } 190 | 191 | [TestMethod] 192 | public void TestSpecialCharactersInCategoryNameAreEscaped() 193 | { 194 | var chart = new BarChart{DataPoints = new[] 195 | { 196 | new BarChartDataPoint{CategoryName = "\tLine1\rLine2", Value = 10} 197 | }}; 198 | 199 | chart.AssertOutputEquals( 200 | " \\tLine1\\rLine2 |########## 10\r\n" + 201 | " -----------\r\n", 202 | "
\\tLine1\\rLine2 |##########  10\n" +
203 |                 "               -----------\n" +
204 |                 "
\n\n"); 205 | } 206 | 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /UnitTests/BlockquoteTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class BlockquoteTests 8 | { 9 | [TestMethod] 10 | public void TestCanIncorporateTextInBlockQuote() 11 | { 12 | var blockQuote = new Blockquote(); 13 | blockQuote.Append("We are what we repeatedly do. Excellence, therefore, is not an act but a habit."); 14 | 15 | blockQuote.AssertOutputEquals( 16 | "> We are what we repeatedly do. Excellence, therefore, is not an act but a habit.\r\n" 17 | , 18 | "
\n" + 19 | "

We are what we repeatedly do. Excellence, therefore, is not an act but a habit.

\n" + 20 | "
\n"); 21 | } 22 | 23 | [TestMethod] 24 | public void TestLineBreaksAreAutomaticallyHandled() 25 | { 26 | var blockQuote = new Blockquote(); 27 | blockQuote.Append(@"You've gotta dance like there's nobody watching, 28 | Love like you'll never be hurt, 29 | Sing like there's nobody listening, 30 | And live like it's heaven on earth."); 31 | 32 | blockQuote.WriteToTrace(); 33 | } 34 | 35 | [TestMethod] 36 | public void TestOtherElementsCanBeIncorporatedIntoBlockquotes() 37 | { 38 | var blockQuote = new Blockquote(); 39 | blockQuote.Append(new HorizontalRule()); 40 | blockQuote.Append(new Header("COMPUTING MACHINERY AND INTELLIGENCE")); 41 | blockQuote.Append(new SubHeader("By A. M. Turing.")); 42 | blockQuote.Append(new Paragraph("...")); 43 | blockQuote.Append(new Paragraph("The idea behind digital computers may be explained by saying that these machines are intended to carry out any operations which could be done by a human computer. The human computer is supposed to be following fixed rules; he has no authority to deviate from them in any detail. We may suppose that these rules are supplied in a book, which is altered whenever he is put on to a new job. He has also an unlimited supply of paper on which he does his calculations. He may also do his multiplications and additions on a \"desk machine,\" but this is not important.")); 44 | blockQuote.Append(new Paragraph("If we use the above explanation as a definition we shall be in danger of circularity of argument. We avoid this by giving an outline. of the means by which the desired effect is achieved. A digital computer can usually be regarded as consisting of three parts:")); 45 | blockQuote.Append(new NumberedList("Store", "Executive unit", "Control")); 46 | 47 | blockQuote.WriteToTrace(); 48 | } 49 | 50 | [TestMethod] 51 | public void TestListsWithWordWrapWillAlsoWork() 52 | { 53 | var blockQuote = new Blockquote(); 54 | 55 | blockQuote.Append(new NumberedList( 56 | "Congress shall make no law respecting an establishment of religion, or prohibiting the free exercise thereof; or abridging the freedom of speech, or of the press; or the right of the people peaceably to assemble, and to petition the Government for a redress of grievances.", 57 | "A well regulated Militia, being necessary to the security of a free State, the right of the people to keep and bear Arms, shall not be infringed.", 58 | "No Soldier shall, in time of peace be quartered in any house, without the consent of the Owner, nor in time of war, but in a manner to be prescribed by law.")); 59 | 60 | blockQuote.WriteToTrace(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /UnitTests/CodeBlockTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class CodeBlockTests 8 | { 9 | [TestMethod] 10 | public void TestCodeBlockCanBeCreated() 11 | { 12 | var code = new CodeBlock(@" 13 | let rec qsort(xs : List) = 14 | match xs with 15 | | [] -> [] 16 | | x :: xs -> 17 | let smaller = qsort (xs |> List.filter(fun e -> e <= x)) 18 | let larger = qsort (xs |> List.filter(fun e -> e >= x)) 19 | smaller @ [x] @ larger 20 | "); 21 | code.WriteToTrace(); 22 | } 23 | 24 | [TestMethod] 25 | public void TestCodeBlockCanBeBuiltByLine() 26 | { 27 | var code = new CodeBlock(); 28 | code.AppendLine(@"m-config. symbol operations final m-config."); 29 | code.AppendLine(); 30 | code.AppendLine(@" / None P0 b"); 31 | code.AppendLine(@" b < 0 R, R, P1 b"); 32 | code.AppendLine(@" \ 1 R, R, P0 b"); 33 | 34 | code.AssertOutputEquals( 35 | " m-config. symbol operations final m-config.\r\n" + 36 | " \r\n" + 37 | " / None P0 b\r\n" + 38 | " b < 0 R, R, P1 b\r\n" + 39 | " \\ 1 R, R, P0 b\r\n" 40 | , 41 | "
m-config.      symbol         operations     final m-config.\n" +
42 |                 "\n" +
43 |                 "             /  None              P0                b\n" +
44 |                 "   b        <    0             R, R, P1             b\n" +
45 |                 "             \\   1             R, R, P0             b\n" +
46 |                 "
\n\n"); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /UnitTests/DocumentationExamples.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using MarkdownLog; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace UnitTests.MarkdownLog 9 | { 10 | [TestClass] 11 | public class DocumentationExamples 12 | { 13 | [TestMethod] 14 | public void TableExample() 15 | { 16 | var data = new[] 17 | { 18 | new {Year = 1991, Album = "Out of Time", Songs = 11, Rating = "* * * *"}, 19 | new {Year = 1992, Album = "Automatic for the People", Songs = 12, Rating = "* * * * *"}, 20 | new {Year = 1994, Album = "Monster", Songs = 12, Rating = "* * *"} 21 | }; 22 | 23 | Console.Write(data.ToMarkdownTable()); 24 | 25 | // Produces: 26 | // 27 | // Year | Album | Songs | Rating 28 | // ---- | ------------------------ | ----- | --------- 29 | // 1991 | Out of Time | 11 | * * * * 30 | // 1992 | Automatic for the People | 12 | * * * * * 31 | // 1994 | Monster | 12 | * * * 32 | } 33 | 34 | [TestMethod] 35 | public void ParagraphExample() 36 | { 37 | var text = "Lolita, light of my life, fire of my loins. My sin, my soul. Lo-lee-ta: the tip of the tongue taking a trip of three steps down the palate to tap, at three, on the teeth. Lo. Lee. Ta."; 38 | Console.Write(text.ToMarkdownParagraph()); 39 | } 40 | 41 | [TestMethod] 42 | public void ParagraphWithCustomWordWrapColumn() 43 | { 44 | var text = "Most people die of a sort of creeping common sense, and discover when it is too late that the only things one never regrets are one's mistakes."; 45 | var paragraph = new Paragraph(text) { WordWrapColumn = 30 }; 46 | Console.Write(paragraph); 47 | } 48 | 49 | [TestMethod] 50 | public void NumberedListExample() 51 | { 52 | var planets = new[] {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"}; 53 | Console.Write(planets.ToMarkdownNumberedList()); 54 | 55 | // Produces: 56 | // 57 | // 1. Mercury 58 | // 2. Venus 59 | // 3. Earth 60 | // 4. Mars 61 | // 5. Jupiter 62 | // 6. Saturn 63 | // 7. Uranus 64 | // 8. Neptune 65 | } 66 | 67 | [TestMethod] 68 | public void BulletedListExample() 69 | { 70 | var beatles = new[] { "John", "Paul", "Ringo", "George" }; 71 | Console.Write(beatles.ToMarkdownBulletedList()); 72 | } 73 | 74 | [TestMethod] 75 | public void BarChartExample() 76 | { 77 | var worldCup = new Dictionary 78 | { 79 | {"Brazil", 5}, 80 | {"Italy", 4}, 81 | {"Germany", 4}, 82 | {"Argentina", 2}, 83 | {"Uruguay", 2}, 84 | {"France", 1}, 85 | {"Spain", 1}, 86 | {"England", 1} 87 | }; 88 | 89 | Console.Write(worldCup.ToMarkdownBarChart()); 90 | } 91 | 92 | [TestMethod] 93 | public void BarChartWithNegativeAndFloatingPointValues() 94 | { 95 | const int valueCount = 20; 96 | var chart = new BarChart 97 | { 98 | ScaleAlways = true, 99 | MaximumChartWidth = 40, 100 | DataPoints = from i in Enumerable.Range(0, valueCount) 101 | let rad = (i * 2.0 * Math.PI) / valueCount 102 | select new BarChartDataPoint 103 | { 104 | CategoryName = string.Format("Cos({0:0.0})", rad), 105 | Value = Math.Cos(rad) 106 | } 107 | }; 108 | 109 | chart.WriteToTrace(); 110 | } 111 | 112 | [TestMethod] 113 | public void TableExample2() 114 | { 115 | var data = new[] 116 | { 117 | new {Name = "Meryl Streep", Nominations = 18, Awards = 3}, 118 | new {Name = "Katharine Hepburn", Nominations = 12, Awards = 4}, 119 | new {Name = "Jack Nicholson", Nominations = 12, Awards = 3} 120 | }; 121 | 122 | Console.Write(data.ToMarkdownTable()); 123 | 124 | var tableWithHeaders = data 125 | .ToMarkdownTable(i => i.Name, i => i.Nominations + i.Awards) 126 | .WithHeaders("Name", "Total"); 127 | 128 | Console.Write(tableWithHeaders); 129 | } 130 | 131 | [TestMethod] 132 | public void HeaderExamples() 133 | { 134 | Console.Write("The Origin of the Species".ToMarkdownHeader()); 135 | Console.Write("By Means of Natural Selection".ToMarkdownSubHeader()); 136 | } 137 | 138 | [TestMethod] 139 | public void BlockquoteExample() 140 | { 141 | const string text = "There are only two hard things in computer science:\n" + 142 | "cache invalidation,\n" + 143 | "naming things,\n" + 144 | "and off-by-one errors."; 145 | 146 | Console.Write(text.ToMarkdownBlockquote()); 147 | } 148 | 149 | [TestMethod] 150 | public void ContainerExample() 151 | { 152 | var log = new MarkdownContainer(); 153 | 154 | var countries = new[]{"Zimbabwe", "Italy", "Bolivia", "Finland", "Australia"}; 155 | 156 | log.Append("Countries (unsorted)".ToMarkdownHeader()); 157 | log.Append(countries.ToMarkdownNumberedList()); 158 | 159 | var sorted = countries.OrderBy(i => i); 160 | 161 | log.Append("Countries (sorted)".ToMarkdownHeader()); 162 | log.Append(sorted.ToMarkdownNumberedList()); 163 | 164 | Console.Write(log); 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /UnitTests/HeaderTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class HeaderTests 8 | { 9 | [TestMethod] 10 | public void TestHeadersCanBeUsed() 11 | { 12 | var header = new Header("The Origin of the Species"); 13 | 14 | header.WriteToTrace(); 15 | } 16 | 17 | [TestMethod] 18 | public void TestSubHeadersCanBeUsed() 19 | { 20 | var subHeader = new SubHeader("By Means of Natural Selection"); 21 | 22 | subHeader.WriteToTrace(); 23 | } 24 | 25 | [TestMethod] 26 | public void TestLineBreaksWillProduceMultipleHeaders() 27 | { 28 | var header = new Header( 29 | "Frankenstein;\r\n" + 30 | "or,\r\n" + 31 | "The Modern Prometheus"); 32 | 33 | header.WriteToTrace(); 34 | } 35 | 36 | [TestMethod] 37 | public void TestSpecialCharactersInHeaderAreEscaped() 38 | { 39 | var header = new Header("Value of _x = 245 = 5 *7* 7 \\"); 40 | 41 | header.WriteToTrace(); 42 | } 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /UnitTests/HorizontalRuleTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class HorizontalRuleTests 8 | { 9 | [TestMethod] 10 | public void TestHorizontalRuleCanBeDrawn() 11 | { 12 | var rule = new HorizontalRule(); 13 | 14 | rule.AssertOutputEquals( 15 | "--------------------------------------------------------------------------------\r\n" 16 | , 17 | "
\n"); 18 | } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /UnitTests/IosTableViewTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class IosTableViewTests 8 | { 9 | [TestMethod] 10 | public void TestCanPlotDisclosureIndicators() 11 | { 12 | var tableView = new TableView 13 | { 14 | Sections = new[] 15 | { 16 | new TableViewSection 17 | { 18 | Header = new IosTableViewHeaderCell {Text = "Mammals"}, Cells = new[] 19 | { 20 | new IosTableViewCell("Elephant", TableViewCellAccessory.DisclosureIndicator), 21 | new IosTableViewCell("Giraffe", TableViewCellAccessory.DisclosureIndicator), 22 | new IosTableViewCell("Monkey", TableViewCellAccessory.DisclosureIndicator), 23 | new IosTableViewCell("Cat", TableViewCellAccessory.DisclosureIndicator) 24 | } 25 | }, 26 | new TableViewSection 27 | { 28 | Header = new IosTableViewHeaderCell {Text = "Reptiles"}, Cells = new[] 29 | { 30 | new IosTableViewCell("Lizard", TableViewCellAccessory.DisclosureIndicator), 31 | new IosTableViewCell("Snake", TableViewCellAccessory.DisclosureIndicator), 32 | new IosTableViewCell("Crocodile", TableViewCellAccessory.DisclosureIndicator) 33 | } 34 | } 35 | } 36 | }; 37 | 38 | tableView.AssertOutputEquals( 39 | " _____________\r\n" + 40 | " |Mammals |\r\n" + 41 | " |_____________|\r\n" + 42 | " | Elephant > |\r\n" + 43 | " | Giraffe > |\r\n" + 44 | " | Monkey > |\r\n" + 45 | " | Cat > |\r\n" + 46 | " |_____________|\r\n" + 47 | " |Reptiles |\r\n" + 48 | " |_____________|\r\n" + 49 | " | Lizard > |\r\n" + 50 | " | Snake > |\r\n" + 51 | " | Crocodile > |\r\n" + 52 | " |_____________|\r\n", 53 | "
 _____________\n" +
 54 |                 "|Mammals      |\n" +
 55 |                 "|_____________|\n" +
 56 |                 "| Elephant  > |\n" +
 57 |                 "| Giraffe   > |\n" +
 58 |                 "| Monkey    > |\n" +
 59 |                 "| Cat       > |\n" +
 60 |                 "|_____________|\n" +
 61 |                 "|Reptiles     |\n" +
 62 |                 "|_____________|\n" +
 63 |                 "| Lizard    > |\n" +
 64 |                 "| Snake     > |\n" +
 65 |                 "| Crocodile > |\n" +
 66 |                 "|_____________|\n" +
 67 |                 "
\n\n"); 68 | } 69 | 70 | [TestMethod] 71 | public void TestCanPlotWithoutHeader() 72 | { 73 | var tableView = new TableView 74 | { 75 | Sections = new[] 76 | { 77 | new TableViewSection 78 | { 79 | Cells = new[] 80 | { 81 | new IosTableViewCell("Red"), 82 | new IosTableViewCell("Orange"), 83 | new IosTableViewCell("Yellow"), 84 | new IosTableViewCell("Green"), 85 | new IosTableViewCell("Blue") 86 | } 87 | } 88 | } 89 | }; 90 | 91 | tableView.WriteToTrace(); 92 | } 93 | 94 | [TestMethod] 95 | public void TestCanPlotMultipleSectionsWithOneSectionHavingNoHeader() 96 | { 97 | 98 | var tableView = new TableView 99 | { 100 | Sections = new[] 101 | { 102 | new TableViewSection 103 | { 104 | Header = new IosTableViewHeaderCell {Text = "Header"}, 105 | Cells = new[] 106 | { 107 | new IosTableViewCell("One"), 108 | new IosTableViewCell("Two"), 109 | new IosTableViewCell("Three") 110 | } 111 | }, 112 | new TableViewSection 113 | { 114 | Cells = new[] 115 | { 116 | new IosTableViewCell("Cat"), 117 | new IosTableViewCell("Dog"), 118 | new IosTableViewCell("Fish") 119 | } 120 | } 121 | } 122 | }; 123 | 124 | tableView.WriteToTrace(); 125 | } 126 | 127 | [TestMethod] 128 | public void TestCanPlotCheckmarks() 129 | { 130 | var tableView = new TableView 131 | { 132 | Sections = new[] 133 | { 134 | new TableViewSection 135 | { 136 | Header = new IosTableViewHeaderCell {Text = "Pizza Toppings"}, Cells = new[] 137 | { 138 | new IosTableViewCell("Extra cheese", TableViewCellAccessory.Checkmark), 139 | new IosTableViewCell("Pepperoni", TableViewCellAccessory.None), 140 | new IosTableViewCell("Black olive", TableViewCellAccessory.Checkmark), 141 | new IosTableViewCell("Sausage", TableViewCellAccessory.Checkmark), 142 | new IosTableViewCell("Mushroom", TableViewCellAccessory.Checkmark), 143 | new IosTableViewCell("Pepper", TableViewCellAccessory.None) 144 | } 145 | } 146 | } 147 | }; 148 | 149 | tableView.WriteToTrace(); 150 | } 151 | 152 | [TestMethod] 153 | public void TestCanPlotWithNoSections() 154 | { 155 | var tableView = new TableView {Sections = new TableViewSection[0]}; 156 | tableView.WriteToTrace(); 157 | } 158 | 159 | [TestMethod] 160 | public void TestCanPlotSingleSectionWithNoHeaderOrCells() 161 | { 162 | 163 | var tableView = new TableView {Sections = new[] {new TableViewSection()}}; 164 | tableView.WriteToTrace(); 165 | } 166 | 167 | } 168 | } -------------------------------------------------------------------------------- /UnitTests/ListTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class ListTests 8 | { 9 | [TestMethod] 10 | public void TestListsCanHaveBulletPoints() 11 | { 12 | var list = new BulletedList( 13 | "Apple", 14 | "Orange", 15 | "Banana", 16 | "Kiwi", 17 | "Plum"); 18 | 19 | list.AssertOutputEquals( 20 | " * Apple\r\n" + 21 | " * Orange\r\n" + 22 | " * Banana\r\n" + 23 | " * Kiwi\r\n" + 24 | " * Plum\r\n" 25 | , 26 | "
    \n" + 27 | "
  • Apple
  • \n" + 28 | "
  • Orange
  • \n" + 29 | "
  • Banana
  • \n" + 30 | "
  • Kiwi
  • \n" + 31 | "
  • Plum
  • \n" + 32 | "
\n"); 33 | } 34 | 35 | [TestMethod] 36 | public void TestListsCanBeNumbered() 37 | { 38 | var list = new NumberedList( 39 | "The Beatles", 40 | "Elvis Presley", 41 | "Michael Jackson", 42 | "Madonna", 43 | "Elton John"); 44 | 45 | list.AssertOutputEquals( 46 | " 1. The Beatles\r\n" + 47 | " 2. Elvis Presley\r\n" + 48 | " 3. Michael Jackson\r\n" + 49 | " 4. Madonna\r\n" + 50 | " 5. Elton John\r\n" 51 | , 52 | "
    \n" + 53 | "
  1. The Beatles
  2. \n" + 54 | "
  3. Elvis Presley
  4. \n" + 55 | "
  5. Michael Jackson
  6. \n" + 56 | "
  7. Madonna
  8. \n" + 57 | "
  9. Elton John
  10. \n" + 58 | "
\n"); 59 | } 60 | 61 | [TestMethod] 62 | public void TestLongListItemsAreAutomaticallyWrappedAt80CharactersByDefault() 63 | { 64 | var list = new NumberedList( 65 | "Every body persists in its state of being at rest or of moving uniformly straight forward, except insofar as it is compelled to change its state by force impressed.", 66 | "The change of momentum of a body is proportional to the impulse impressed on the body, and happens along the straight line on which that impulse is impressed.", 67 | "To every action there is always opposed an equal reaction: or the mutual actions of two bodies upon each other are always equal, and directed to contrary parts."); 68 | list.WriteToTrace(); 69 | } 70 | 71 | [TestMethod] 72 | public void TestWordWrapColumnCanBeSpecified() 73 | { 74 | var list = new NumberedList( 75 | "A straight line segment can be drawn joining any two points.", 76 | "Any straight line segment can be extended indefinitely in a straight line.", 77 | "Given any straight line segment, a circle can be drawn having the segment as radius and one endpoint as center.", 78 | "All right angles are congruent.", 79 | "If two lines are drawn which intersect a third in such a way that the sum of the inner angles on one side is less than two right angles, then the two lines inevitably must intersect each other on that side if extended far enough. This postulate is equivalent to what is known as the parallel postulate.") 80 | { 81 | WordWrapColumn = 40 82 | }; 83 | list.WriteToTrace(); 84 | } 85 | 86 | [TestMethod] 87 | public void TestSpecialMarkDownCharactersAreAutomaticallyEncoded() 88 | { 89 | var list = new BulletedList( 90 | @"The underscore (_), backtick (`), asterisk (*) and backslash (\) have special meanings in Unicode", 91 | "These will be encoded by default so there are no surprises caused by your automatically generated Markdown containing special characters", 92 | "For example, variable_names_with_underscores and formula x = 1*2**3**4"); 93 | list.WriteToTrace(); 94 | } 95 | 96 | [TestMethod] 97 | public void TestCanProduceListFromAnyEnumerable() 98 | { 99 | "Mathematical constants".ToMarkdownHeader().WriteToTrace(); 100 | new[] {3.14, 2.718, 1.618, 0.577215}.ToMarkdownBulletedList().WriteToTrace(); 101 | 102 | new[] { "John", "Paul", "Ringo", "George" }.ToMarkdownBulletedList().WriteToTrace(); 103 | 104 | new[] { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" }.ToMarkdownNumberedList().WriteToTrace(); 105 | } 106 | 107 | [TestMethod] 108 | public void TestListCanHaveNoItems() 109 | { 110 | var list = new BulletedList(); 111 | list.WriteToTrace(); 112 | } 113 | 114 | } 115 | } -------------------------------------------------------------------------------- /UnitTests/MarkdownLog.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | true 7 | ..\MarkdownLog.snk 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /UnitTests/NumberExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class NumberExtensionsTests 8 | { 9 | [TestMethod] 10 | public void TestToColumnTitle() 11 | { 12 | Assert.AreEqual("A", 0.ToColumnTitle()); 13 | Assert.AreEqual("B", 1.ToColumnTitle()); 14 | Assert.AreEqual("C", 2.ToColumnTitle()); 15 | 16 | Assert.AreEqual("Z", 25.ToColumnTitle()); 17 | Assert.AreEqual("AA", 26.ToColumnTitle()); 18 | Assert.AreEqual("AB", 27.ToColumnTitle()); 19 | Assert.AreEqual("AC", 28.ToColumnTitle()); 20 | 21 | Assert.AreEqual("AZ", 51.ToColumnTitle()); 22 | Assert.AreEqual("BA", 52.ToColumnTitle()); 23 | 24 | Assert.AreEqual("YZ", 675.ToColumnTitle()); 25 | Assert.AreEqual("ZA", 676.ToColumnTitle()); 26 | 27 | Assert.AreEqual("ZZ", 701.ToColumnTitle()); 28 | Assert.AreEqual("AAA", 702.ToColumnTitle()); 29 | Assert.AreEqual("AAB", 703.ToColumnTitle()); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /UnitTests/ParagraphTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class ParagraphTests 8 | { 9 | [TestMethod] 10 | public void TestParagraphsCanBeUsed() 11 | { 12 | var paragraph = new Paragraph("The quick brown fox jumps over the lazy dog."); 13 | 14 | paragraph.WriteToTrace(); 15 | } 16 | 17 | [TestMethod] 18 | public void TestLongParagraphsAreWrapped() 19 | { 20 | var paragraph = new Paragraph("Lolita, light of my life, fire of my loins. My sin, my soul. Lo-lee-ta: the tip of the tongue taking a trip of three steps down the palate to tap, at three, on the teeth. Lo. Lee. Ta."); 21 | 22 | paragraph.WriteToTrace(); 23 | } 24 | 25 | [TestMethod] 26 | public void TestWrappingCanBeDisabled() 27 | { 28 | var paragraph = new Paragraph("Whoever fights monsters should see to it that in the process he does not become a monster. And if you gaze long enough into an abyss, the abyss will gaze back into you.") 29 | { 30 | WordWrap = false 31 | }; 32 | 33 | paragraph.WriteToTrace(); 34 | } 35 | 36 | [TestMethod] 37 | public void TestWrappingColumnCanBeSpecified() 38 | { 39 | var paragraph = new Paragraph("Most people die of a sort of creeping common sense, and discover when it is too late that the only things one never regrets are one's mistakes.") 40 | { 41 | WordWrapColumn = 20 42 | }; 43 | 44 | paragraph.WriteToTrace(); 45 | } 46 | 47 | [TestMethod] 48 | public void TestLineBreaksAreHandled() 49 | { 50 | var paragraph = new Paragraph("I wandered lonely as a cloud\r\n" + 51 | "That floats on high o'er vales and hills,\r\n" + 52 | "When all at once I saw a crowd,\r\n" + 53 | "A host, of golden daffodils;\r\n" + 54 | "Beside the lake, beneath the trees,\r\n" + 55 | "Fluttering and dancing in the breeze."); 56 | 57 | 58 | paragraph.AssertOutputEquals( 59 | "I wandered lonely as a cloud \r\n" + 60 | "That floats on high o'er vales and hills, \r\n" + 61 | "When all at once I saw a crowd, \r\n" + 62 | "A host, of golden daffodils; \r\n" + 63 | "Beside the lake, beneath the trees, \r\n" + 64 | "Fluttering and dancing in the breeze.\r\n" 65 | , 66 | "

I wandered lonely as a cloud
\n" + 67 | "That floats on high o'er vales and hills,
\n" + 68 | "When all at once I saw a crowd,
\n" + 69 | "A host, of golden daffodils;
\n" + 70 | "Beside the lake, beneath the trees,
\n" + 71 | "Fluttering and dancing in the breeze.

\n"); 72 | } 73 | 74 | [TestMethod] 75 | public void TestDifferentTypesOfLineBreaksAreHandled() 76 | { 77 | var paragraph = new Paragraph("Unix style:\n" + 78 | "ZX Spectrum style:\r" + 79 | "Acorn BBC Spooled output:\n\r" + 80 | "Windows style:\r\n"); 81 | 82 | paragraph.AssertOutputEquals( 83 | "Unix style: \r\n" + 84 | "ZX Spectrum style: \r\n" + 85 | "Acorn BBC Spooled output: \r\n" + 86 | "Windows style:\r\n" 87 | , 88 | "

Unix style:
\n" + 89 | "ZX Spectrum style:
\n" + 90 | "Acorn BBC Spooled output:
\n" + 91 | "Windows style:

\n"); 92 | } 93 | 94 | [TestMethod] 95 | public void TestSpecialCharactersAreEscapedAutomatically() 96 | { 97 | new Paragraph("#This looks like a header#").WriteToTrace(); 98 | 99 | new Paragraph("Special markdown symbols, like *this* and _that_, will be escaped so that the HTML is a faithful reproduction of the input").WriteToTrace(); 100 | 101 | new Paragraph("1984. A great book!").WriteToTrace(); 102 | } 103 | 104 | } 105 | } -------------------------------------------------------------------------------- /UnitTests/ReflectionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using MarkdownLog; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace UnitTests.MarkdownLog 8 | { 9 | [TestClass] 10 | public class ReflectionExtensionsTests 11 | { 12 | [TestMethod] 13 | public void TestIsEnumerable() 14 | { 15 | Assert.IsTrue(new[] {"a", "b", "c"}.GetType().IsEnumerable()); 16 | Assert.IsTrue(new[] {1, 2, 3}.GetType().IsEnumerable()); 17 | Assert.IsTrue(new List {"a", "b", "c"}.GetType().IsEnumerable()); 18 | Assert.IsTrue(new List {1, 2, 3}.GetType().IsEnumerable()); 19 | Assert.IsTrue(new ArrayList {1, "a", true}.GetType().IsEnumerable()); 20 | Assert.IsTrue("abc".GetType().IsEnumerable()); 21 | 22 | Assert.IsFalse(123.GetType().IsEnumerable()); 23 | Assert.IsFalse(true.GetType().IsEnumerable()); 24 | Assert.IsFalse(new{Property1 = "value1"}.GetType().IsEnumerable()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UnitTests/StringExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using MarkdownLog; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace UnitTests.MarkdownLog 5 | { 6 | [TestClass] 7 | public class StringExtensionsTests 8 | { 9 | [TestMethod] 10 | public void TestMarkdownCharactersWithinTextAreEscaped() 11 | { 12 | Assert.AreEqual(@"this is some \*text\*", "this is some *text*".EscapeMarkdownCharacters(), "asterisks are escaped"); 13 | Assert.AreEqual(@"this is some \*\*text\*\*", "this is some **text**".EscapeMarkdownCharacters(), "double asterisk is escaped"); 14 | Assert.AreEqual(@"this is some \_text\_", "this is some _text_".EscapeMarkdownCharacters(), "underscore is escaped"); 15 | Assert.AreEqual(@"this is some \_\_text\_\_", "this is some __text__".EscapeMarkdownCharacters(), "double underscore is escaped"); 16 | Assert.AreEqual(@"this is some \`text\`", "this is some `text`".EscapeMarkdownCharacters(), "backtick is escaped"); 17 | Assert.AreEqual(@"this is a backslash \\", @"this is a backslash \".EscapeMarkdownCharacters(), "backslash is escaped"); 18 | } 19 | 20 | [TestMethod] 21 | public void TestHeaderCharacterAtBeginningAreEscaped() 22 | { 23 | Assert.AreEqual(@"\#this looks like a header", "#this looks like a header".EscapeMarkdownCharacters(), "hash is escaped if at start"); 24 | Assert.AreEqual(@"this is a hash #, and another #", "this is a hash #, and another #".EscapeMarkdownCharacters(), "hash is not escaped elsewhere"); 25 | 26 | Assert.AreEqual(@"\##looks like a second level header", "##looks like a second level header".EscapeMarkdownCharacters(), "two hashes at begining are escaped"); 27 | Assert.AreEqual(@"\###looks like a third level header", "###looks like a third level header".EscapeMarkdownCharacters(), "three hashes at begining are escaped"); 28 | Assert.AreEqual(@"\####looks like a fourth level header", "####looks like a fourth level header".EscapeMarkdownCharacters(), "four hashes at begining are escaped"); 29 | Assert.AreEqual(@"\#####looks like a fifth level header", "#####looks like a fifth level header".EscapeMarkdownCharacters(), "five hashes at begining are escaped"); 30 | Assert.AreEqual(@"\######looks like a sixth level header", "######looks like a sixth level header".EscapeMarkdownCharacters(), "six hashes at begining are escaped"); 31 | } 32 | 33 | [TestMethod] 34 | public void TestFullStopAfterNumbersIsEscaped() 35 | { 36 | Assert.AreEqual(@"1\. This looks like a list", "1. This looks like a list".EscapeMarkdownCharacters(), "A full-stop (period) following one number will be escaped"); 37 | Assert.AreEqual(@"12\. This looks like a list", "12. This looks like a list".EscapeMarkdownCharacters(), "A full-stop (period) following two numbers will be escaped"); 38 | Assert.AreEqual(@"123\. This looks like a list", "123. This looks like a list".EscapeMarkdownCharacters(), "A full-stop (period) following three numbers will be escaped"); 39 | Assert.AreEqual(@"1234\. This looks like a list", "1234. This looks like a list".EscapeMarkdownCharacters(), "A full-stop (period) following four numbers will be escaped"); 40 | } 41 | 42 | [TestMethod] 43 | public void TestSplitByLines() 44 | { 45 | var lines = "apple\nbanana\ncherry".SplitByLine(); 46 | Assert.AreEqual(3, lines.Count); 47 | Assert.AreEqual("apple", lines[0]); 48 | Assert.AreEqual("banana", lines[1]); 49 | Assert.AreEqual("cherry", lines[2]); 50 | } 51 | 52 | [TestMethod] 53 | public void TestSplitByLinesHandlesAllLineEndings() 54 | { 55 | Assert.AreEqual(3, "a\rb\rc".SplitByLine().Count); 56 | Assert.AreEqual(3, "a\nb\nc".SplitByLine().Count); 57 | Assert.AreEqual(3, "a\r\nb\r\nc".SplitByLine().Count); 58 | Assert.AreEqual(3, "a\n\rb\n\rc".SplitByLine().Count); 59 | } 60 | 61 | [TestMethod] 62 | public void TestSplitByLinesHandlesMixedLineEndings() 63 | { 64 | Assert.AreEqual(5, "a\rb\nc\r\nd\n\re".SplitByLine().Count); 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /UnitTests/TestExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text; 3 | using MarkdownLog; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace UnitTests.MarkdownLog 7 | { 8 | public static class TestExtensions 9 | { 10 | public static void WriteToTrace(this IMarkdownElement element) 11 | { 12 | var markdown = element.ToMarkdown(); 13 | 14 | markdown.WriteToTraceWithDelimiter("Markdown"); 15 | 16 | Trace.WriteLine(""); 17 | 18 | var markdownSharp = new MarkdownToHtmlConverter(); 19 | var html = markdownSharp.Transform(markdown); 20 | 21 | html.WriteToTraceWithDelimiter("HTML"); 22 | } 23 | 24 | public static void AssertOutputEquals(this IMarkdownElement element, string expectedMarkdown, string expectedHtml = null) 25 | { 26 | var markdown = element.ToMarkdown(); 27 | 28 | markdown.WriteToTraceWithDelimiter("Markdown"); 29 | 30 | Trace.WriteLine(""); 31 | 32 | var html = markdown.MarkdownToHtml(); 33 | 34 | html.WriteToTraceWithDelimiter("HTML"); 35 | 36 | Trace.WriteLine(""); 37 | 38 | if (expectedMarkdown != markdown) 39 | Assert.Fail("Unexpected Markdown:\r\n\r\n{0}", BuildOutputWithDelimiter(expectedMarkdown, "Expected Markdown")); 40 | else 41 | Trace.WriteLine("Markdown output meets expectations"); 42 | 43 | if (expectedHtml != null) 44 | { 45 | if (expectedHtml != html) 46 | Assert.Fail("Unexpected HTML:\r\n\r\n{0}", BuildOutputWithDelimiter(expectedHtml, "Expected HTML")); 47 | else 48 | Trace.WriteLine("HTML output meets expectations"); 49 | } 50 | } 51 | 52 | private static void WriteToTraceWithDelimiter(this string output, string type) 53 | { 54 | var builder = BuildOutputWithDelimiter(output, type); 55 | Trace.Write(builder.ToString()); 56 | } 57 | 58 | private static StringBuilder BuildOutputWithDelimiter(string output, string type) 59 | { 60 | var builder = new StringBuilder(); 61 | builder.AppendFormat("---BEGIN {0}---\r\n", type); 62 | builder.Append(output); 63 | builder.AppendFormat("---END {0}---\r\n", type); 64 | return builder; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | clone_depth: 1 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | configuration: Release 10 | 11 | build: 12 | project: MarkdownLog.sln 13 | 14 | test: auto 15 | 16 | after_test: 17 | - cmd: dotnet pack MarkdownLog\ -c Release 18 | --------------------------------------------------------------------------------