Skip to content

Commit 58317f9

Browse files
authored
Allow caller to overwrite styling (#3)
* Allow caller to overwrite styling * Allow the caller to remove the wrapping around Headers
1 parent 408bfef commit 58317f9

File tree

13 files changed

+210
-74
lines changed

13 files changed

+210
-74
lines changed

ConsoleMarkdownRenderer.Example/Program.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class ExampleSettings : CommandSettings
3939
[DefaultValue(false)]
4040
public bool IncludeDebug { get; init; }
4141

42+
[CommandOption("-r|--remove-header-wrap")]
43+
[DefaultValue(false)]
44+
public bool RemoveHeaderWrap { get; init; }
45+
4246
[CommandOption("-w|--web")]
4347
[DefaultValue(false)]
4448
public bool UseWeb { get; init; }
@@ -63,10 +67,16 @@ public override int Execute([NotNull] CommandContext context, [NotNull] ExampleS
6367
uri = new Uri(Path.GetFullPath(path));
6468
}
6569

70+
DisplayOptions options = new()
71+
{
72+
IncludeDebug = settings.IncludeDebug,
73+
WrapHeader = !settings.RemoveHeaderWrap,
74+
};
75+
6676
Displayer.DisplayMarkdown(
6777
uri,
68-
!settings.IgnoreLinks,
69-
settings.IncludeDebug);
78+
options,
79+
allowFollowingLinks: !settings.IgnoreLinks);
7080
return 0;
7181
}
7282
}

ConsoleMarkdownRenderer.Example/usage.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ The little application will display the contents of markdown file, and accepts t
77
| `-p`/`--path` | The path to where the markdown content can be found, it can be relative (or absolute) file path, or any valid Uri. If not proved this default the contents of `data/example.md` unless the `--web` is provided, in will use the default content from the web |
88
| `-i`/`--ignore--links` | Used to suppress the list of links found within the document. |
99
| `-d`/`--include-debug` | Include debug information |
10+
| `-r`/`--remove-header-wrap` | Remove the `#` that wrap headers |
1011
| `-w`/`--web` | When specified (and `--path` is not) content from [the source repo](https://github.com/boxofyellow/ConsoleMarkdownRenderer) will be displayed |
1112

ConsoleMarkdownRenderer.Tests/RendererTests.cs

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public void RendererTests_TextValidation()
5858
}
5959

6060
var pipeline = Displayer.DefaultPipeline;
61-
var renderer = new ConsoleRenderer(includeDebug: true);
61+
var renderer = new ConsoleRenderer(new DisplayOptions() { IncludeDebug = true });
6262

6363
foreach (var markdown in markdowns)
6464
{
@@ -76,9 +76,13 @@ public void RendererTests_TextValidation()
7676
}
7777
}
7878

79-
[TestMethod]
80-
public void RendererTests_CodeInlineTest()
81-
=> AssertMarkdownYieldsFormat("codeInline", "in line code", new Style(foreground: Color.Yellow, background: Color.Blue));
79+
[DataTestMethod]
80+
[DataRow(false)]
81+
[DataRow(true)]
82+
public void RendererTests_CodeInlineTest(bool useCrazy)
83+
{
84+
AssertMarkdownYieldsFormat("codeInline", "in line code", new Style(foreground: Color.Yellow, background: Color.Blue), useCrazy);
85+
}
8286

8387
[DataTestMethod]
8488
[DataRow("bold" , Decoration.Bold)]
@@ -87,25 +91,40 @@ public void RendererTests_CodeInlineTest()
8791
[DataRow("subscript" , Decoration.SlowBlink)]
8892
[DataRow("superscript" , Decoration.RapidBlink)]
8993
[DataRow("inserted" , Decoration.Underline)]
90-
public void RendererTests_EmphasisInlineTest(string text, Decoration decoration)
91-
=> AssertMarkdownYieldsFormat("emphasisInline", text, new Style(decoration: decoration));
94+
public void RendererTests_EmphasisInlineTest(string text, Decoration decoration)
95+
{
96+
AssertMarkdownYieldsFormat("emphasisInline", text, new Style(decoration: decoration), useCrazy: false);
97+
AssertMarkdownYieldsFormat("emphasisInline", text, new Style(decoration: decoration), useCrazy: true);
98+
}
9299

93-
[TestMethod]
94-
public void RendererTests_MarkedTest()
95-
=> AssertMarkdownYieldsFormat("emphasisInline", "marked", new Style(foreground: Color.Black, background: Color.Yellow));
100+
[DataTestMethod]
101+
[DataRow(false)]
102+
[DataRow(true)]
103+
public void RendererTests_MarkedTest(bool useCrazy)
104+
{
105+
AssertMarkdownYieldsFormat("emphasisInline", "marked", new Style(foreground: Color.Black, background: Color.Yellow), useCrazy);
106+
}
96107

97-
[TestMethod]
98-
public void RendererTests_HeaderTest()
108+
[DataTestMethod]
109+
[DataRow(false)]
110+
[DataRow(true)]
111+
public void RendererTests_HeaderTest(bool useCrazy)
99112
=> AssertMarkdownYieldsFormat(
100113
"headingBlock",
101-
"# Level One # ## Level Two ## ### Level Three ###",
102-
new Style(decoration: Decoration.Bold | Decoration.Invert | Decoration.Underline));
114+
text: useCrazy
115+
? "Level One Level Two Level Three"
116+
: "# Level One # ## Level Two ## ### Level Three ###",
117+
new Style(decoration: Decoration.Bold | Decoration.Invert | Decoration.Underline),
118+
useCrazy);
103119

104120
[DataTestMethod]
105121
[DataRow("htmlBlock", "<table> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>3</td> <td>4</td> </tr> </table>")]
106122
[DataRow("htmlInline", "<span>html</span>")]
107-
public void RendererTests_HtmlTest(string name, string text)
108-
=> AssertMarkdownYieldsFormat(name, text, new Style(foreground: Color.Black, background: Color.Green));
123+
public void RendererTests_HtmlTest(string name, string text)
124+
{
125+
AssertMarkdownYieldsFormat(name, text, new Style(foreground: Color.Black, background: Color.Green), useCrazy: false);
126+
AssertMarkdownYieldsFormat(name, text, new Style(foreground: Color.Black, background: Color.Green), useCrazy: true);
127+
}
109128

110129
[TestMethod]
111130
public void RendererTests_LinkTest()
@@ -123,7 +142,7 @@ public void RendererTests_LinkTest()
123142
new ("9", "https://www.nine.com/nine.jpg", true),
124143
};
125144

126-
var renderer = new ConsoleRenderer(includeDebug: true);
145+
var renderer = new ConsoleRenderer(new DisplayOptions() { IncludeDebug = true});
127146

128147
ConsoleUnderTest.Write(Renderer(GetResourceContent("linkInline", "md"), renderer));
129148

@@ -140,17 +159,22 @@ public void RendererTests_LinkTest()
140159
[DataRow("quote 2." , Decoration.Italic)]
141160
[DataRow("should even" , Decoration.Italic | Decoration.Bold)]
142161
public void RendererTests_QuoteBlockTest(string text, Decoration decoration)
143-
=> AssertMarkdownYieldsFormat("quoteBlock", text, new Style(decoration: decoration));
162+
{
163+
AssertMarkdownYieldsFormat("quoteBlock", text, new Style(decoration: decoration), useCrazy: false);
164+
AssertMarkdownYieldsFormat("quoteBlock", text, new Style(decoration: decoration), useCrazy: true);
165+
}
144166

145-
private void AssertMarkdownYieldsFormat(string name, string text, Style style)
167+
private void AssertMarkdownYieldsFormat(string name, string text, Style style, bool useCrazy)
146168
{
169+
Style format = useCrazy ? c_crazyFormat : style;
170+
DisplayOptions options = useCrazy ? m_crazyOptions : new DisplayOptions();
147171
var markdown = GetResourceContent(name, "md");
148172

149-
var renderHook = new TestRenderHook(text, style);
173+
var renderHook = new TestRenderHook(text, format);
150174
ConsoleUnderTest.Pipeline.Attach(renderHook);
151175

152176
Logger.LogMessage($"Rendering {name}");
153-
ConsoleUnderTest.Write(Renderer(markdown));
177+
ConsoleUnderTest.Write(Renderer(markdown, options: options));
154178

155179
renderHook.AssertFormattedTextFound();
156180
}
@@ -164,10 +188,13 @@ private string GetResourceContent(string name, string extension)
164188
return reader.ReadToEnd();
165189
}
166190

167-
private static IRenderable Renderer(string text, ConsoleRenderer? renderer = default, MarkdownPipeline? pipeline = default)
191+
private static IRenderable Renderer(string text, ConsoleRenderer? renderer = default, MarkdownPipeline? pipeline = default, DisplayOptions? options = default)
168192
{
169193
var document = Markdown.Parse(text, pipeline ?? Displayer.DefaultPipeline);
170-
renderer ??= new ConsoleRenderer(includeDebug: true);
194+
options ??= new();
195+
options = options.Clone();
196+
options.IncludeDebug = true;
197+
renderer ??= new ConsoleRenderer(options);
171198
renderer.Clear();
172199
renderer.Render(document);
173200
Assert.IsNotNull(renderer.Root);
@@ -222,6 +249,27 @@ private static Dictionary<string, int> Counts(string text)
222249
private int m_count;
223250
}
224251

252+
private const string c_crazyFormat = "red on purple";
253+
private readonly static DisplayOptions m_crazyOptions = new DisplayOptions
254+
{
255+
Bold = c_crazyFormat,
256+
CodeBlock = c_crazyFormat,
257+
CodeInLine = c_crazyFormat,
258+
Header = c_crazyFormat,
259+
HtmlBlock = c_crazyFormat,
260+
HtmlInline = c_crazyFormat,
261+
Inserted = c_crazyFormat,
262+
Italic = c_crazyFormat,
263+
Marked = c_crazyFormat,
264+
QuotedBlock = c_crazyFormat,
265+
Strikethrough = c_crazyFormat,
266+
Subscript = c_crazyFormat,
267+
Superscript = c_crazyFormat,
268+
UnknownDelimiterChar = c_crazyFormat,
269+
UnknownDelimiterContent = c_crazyFormat,
270+
WrapHeader = false,
271+
};
272+
225273
private const string c_resources = "resources";
226274
}
227275
}

DisplayOptions.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using Spectre.Console;
2+
3+
namespace ConsoleMarkdownRenderer
4+
{
5+
/// <summary>
6+
/// Class for controlling the styling and other other display options for the Markdown elements
7+
/// </summary>
8+
public class DisplayOptions
9+
{
10+
public Style Bold { get; set; } = new(decoration: Decoration.Bold);
11+
public Style CodeBlock { get; set; } = new(foreground: Color.Yellow, background: Color.Blue);
12+
public Style CodeInLine { get; set; } = new(foreground: Color.Yellow, background: Color.Blue);
13+
public Style Header { get; set; } = new(decoration: Decoration.Bold | Decoration.Underline | Decoration.Invert);
14+
public Style HtmlBlock { get; set; } = new(foreground: Color.Black, background: Color.Green);
15+
public Style HtmlInline { get; set; } = new(foreground: Color.Black, background: Color.Green);
16+
/// <see cref="Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Inserted"/>
17+
public Style Inserted { get; set; } = new(decoration: Decoration.Underline);
18+
public Style Italic { get; set; } = new(decoration: Decoration.Italic);
19+
/// <see cref="Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Marked"/>
20+
public Style Marked { get; set; } = new(foreground: Color.Black, background: Color.Yellow);
21+
22+
public Style QuotedBlock { get; set; } = new Style(decoration: Decoration.Italic);
23+
24+
/// <see cref="Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Strikethrough"/>
25+
public Style Strikethrough { get; set; } = new(decoration: Decoration.Strikethrough);
26+
27+
// Hey, I'm sure there might be something better for subscript... but sometimes you have to make due with what you got
28+
// And the blink does not seem to render well
29+
/// <see cref="Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Subscript"/>
30+
public Style Subscript { get; set; } = new(decoration: Decoration.SlowBlink);
31+
32+
// This another one. Don't have an exact match for superscript
33+
/// <see cref="Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Superscript"/>
34+
public Style Superscript { get; set; } = new(decoration: Decoration.RapidBlink);
35+
36+
37+
// Yes, these more a style, but it should help identify where things need updating
38+
public Style UnknownDelimiterChar { get; set; } = new(decoration: Decoration.Dim);
39+
public Style UnknownDelimiterContent { get; set; } = new(decoration: Decoration.Invert);
40+
41+
// When set to true wrap Headers with '#'s
42+
public bool WrapHeader { get; set; } = true;
43+
44+
// When set to true the content structure is displayed and detail of unsupported markdown is displayed
45+
public bool IncludeDebug = false;
46+
47+
public DisplayOptions Clone() => new()
48+
{
49+
Bold = this.Bold,
50+
CodeBlock = this.CodeBlock,
51+
CodeInLine = this.CodeInLine,
52+
Header = this.Header,
53+
HtmlBlock = this.HtmlBlock,
54+
HtmlInline = this.HtmlInline,
55+
IncludeDebug = this.IncludeDebug,
56+
Inserted = this.Inserted,
57+
Italic = this.Italic,
58+
Marked = this.Marked,
59+
QuotedBlock = this.QuotedBlock,
60+
Strikethrough = this.Strikethrough,
61+
Subscript = this.Subscript,
62+
Superscript = this.Superscript,
63+
UnknownDelimiterChar = this.UnknownDelimiterChar,
64+
UnknownDelimiterContent = this.UnknownDelimiterContent,
65+
WrapHeader = this.WrapHeader,
66+
};
67+
}
68+
}

Displayer.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public static class Displayer
3434
/// - For everything else it is thrown at the OS to see if it can sort it out.
3535
/// </summary>
3636
/// <param name="uri">The uri to pull the content from</param>
37+
/// <param name="options">options to control how to display the content</param>
3738
/// <param name="allowFollowingLinks">when set to true, the list of links will be provided, when false the list is omitted</param>
38-
/// <param name="includeDebug">when set to true the content structure is displayed and detail of unsupported markdown is displayed</param>
39-
public static void DisplayMarkdown(Uri uri, bool allowFollowingLinks = true, bool includeDebug = false)
39+
public static void DisplayMarkdown(Uri uri, DisplayOptions? options = default, bool allowFollowingLinks = true)
4040
{
4141
using var tempFiles = new TempFileManager();
4242
string path = string.Empty;
@@ -60,7 +60,7 @@ public static void DisplayMarkdown(Uri uri, bool allowFollowingLinks = true, boo
6060
if (!string.IsNullOrEmpty(path))
6161
{
6262
var text = File.ReadAllText(path);
63-
DisplayMarkdown(text, uri, allowFollowingLinks, tempFiles, includeDebug);
63+
DisplayMarkdown(text, uri, options, allowFollowingLinks, tempFiles);
6464
}
6565
}
6666

@@ -77,12 +77,12 @@ public static void DisplayMarkdown(Uri uri, bool allowFollowingLinks = true, boo
7777
/// </summary>
7878
/// <param name="text">the content to display</param>
7979
/// <param name="baseUri">uri for that content, this base is used to calculate relative links</param>
80+
/// <param name="options">options to control how to display the content</param>
8081
/// <param name="allowFollowingLinks">when set to true, the list of links will be provided, when false the list is omitted</param>
81-
/// <param name="includeDebug">when set to true the content structure is displayed and detail of unsupported markdown is displayed</param>
82-
public static void DisplayMarkdown(string text, Uri baseUri, bool allowFollowingLinks = true, bool includeDebug = false)
82+
public static void DisplayMarkdown(string text, Uri baseUri, DisplayOptions? options = default, bool allowFollowingLinks = true)
8383
{
8484
using var tempFiles = new TempFileManager();
85-
DisplayMarkdown(text, baseUri, allowFollowingLinks, tempFiles, includeDebug);
85+
DisplayMarkdown(text, baseUri, options, allowFollowingLinks, tempFiles);
8686
}
8787

8888
/// <summary>
@@ -130,7 +130,7 @@ internal static string Download(Uri uri, TempFileManager tempFiles, bool expectI
130130
string tempFile = tempFiles.GetTempFile();
131131
using var fileStream = File.Create(tempFile);
132132
// We we make this method async we should flip this too.
133-
response.Content.CopyTo(fileStream, context: null, CancellationToken.None);
133+
response.Content.CopyTo(fileStream, context: default, CancellationToken.None);
134134
return tempFile;
135135
}
136136
catch (Exception ex)
@@ -145,17 +145,19 @@ internal static string Download(Uri uri, TempFileManager tempFiles, bool expectI
145145
/// </summary>
146146
/// <param name="text">the content to display</param>
147147
/// <param name="baseUri">uri for that content, this base is used to calculate relative links</param>
148+
/// <param name="options">options to control how to display the content</param>
148149
/// <param name="allowFollowingLinks">when true the user will be allow to follow links in the document</param>
149150
/// <param name="tempFiles">a manager for temp files, the caller is expected to clean these up</param>
150-
/// <param name="includeDebug"see the public version></param>
151-
private static void DisplayMarkdown(string text, Uri baseUri, bool allowFollowingLinks, TempFileManager tempFiles, bool includeDebug)
151+
private static void DisplayMarkdown(string text, Uri baseUri, DisplayOptions? options, bool allowFollowingLinks, TempFileManager tempFiles)
152152
{
153153
// Two additional options that get included in the list links
154154
const int done = -1; // To indicate that the user is done and want to give control back to the caller
155155
const int back = -2; // To indicate that the user was to view the previously displayed content
156156

157+
options ??= new DisplayOptions();
158+
157159
var pipeline = DefaultPipeline;
158-
var renderer = new ConsoleRenderer(includeDebug);
160+
var renderer = new ConsoleRenderer(options);
159161

160162
// As the user browses the links, this stack allows us display the previous content at their request
161163
var stack = new Stack<(string Text, Uri RelativePath)>();
@@ -384,24 +386,24 @@ private static void Open(Uri uri)
384386
/// <returns>an http client, the call should NOT dispose this</returns>
385387
private static HttpClient GetClient()
386388
{
387-
lock (m_lockObject)
389+
lock (s_lockObject)
388390
{
389-
if (m_client is null)
391+
if (s_client is null)
390392
{
391393
var handler = new SocketsHttpHandler
392394
{
393395
// Really I don't expect the DNS much for these, but as a library we don't really know how long we will hang around
394396
// so **_some_** limit makes sense
395397
PooledConnectionLifetime = TimeSpan.FromMinutes(15)
396398
};
397-
m_client = new HttpClient(handler);
399+
s_client = new HttpClient(handler);
398400
}
399401
}
400-
return m_client;
402+
return s_client;
401403
}
402404

403-
private static HttpClient? m_client;
404-
private static object m_lockObject = new();
405+
private static HttpClient? s_client;
406+
private static readonly object s_lockObject = new();
405407

406408
/// <summary>
407409
/// Try to determine if this an iTerm2 console

ObjectRenderers/ConsoleCodeBlockRenderer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ protected override void Write(ConsoleRenderer renderer, CodeBlock obj)
1010
renderer
1111
.NewFrame()
1212
.StartInline()
13-
.AddInLine("[yellow on blue]")
13+
.PushStyle(renderer.Options.CodeBlock)
1414
.AddInLine(Environment.NewLine);
1515

1616
for (int i = 0; i < obj.Lines.Lines.Length; i++)
@@ -25,7 +25,7 @@ protected override void Write(ConsoleRenderer renderer, CodeBlock obj)
2525
}
2626

2727
renderer
28-
.AddInLine("[/]")
28+
.PopStyle()
2929
.EndInline()
3030
.CompleteFrame();
3131
}

0 commit comments

Comments
 (0)