Skip to content

Commit d813ba0

Browse files
Copilotarika0093
andauthored
Replace manual and Roslyn-based syntax highlighting with Shiki (#262)
* Initial plan * Add Shiki highlighting service and update CodeBlock component Co-authored-by: arika0093 <4524647+arika0093@users.noreply.github.com> * Add Shiki CSS styling and update to latest version Co-authored-by: arika0093 <4524647+arika0093@users.noreply.github.com> * Replace PreviewPane with ShikiPreviewPane in Playground Co-authored-by: arika0093 <4524647+arika0093@users.noreply.github.com> * Remove unused CSharpSyntaxHighlighter registration Co-authored-by: arika0093 <4524647+arika0093@users.noreply.github.com> * Address code review feedback: add using directives and clarify documentation Co-authored-by: arika0093 <4524647+arika0093@users.noreply.github.com> * chore: remove unused file --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arika0093 <4524647+arika0093@users.noreply.github.com> Co-authored-by: Arika Ishinami <delete0093@gmail.com>
1 parent 7965f8b commit d813ba0

File tree

8 files changed

+200
-441
lines changed

8 files changed

+200
-441
lines changed
Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
@inject CSharpSyntaxHighlighter SyntaxHighlighter
1+
@using Linqraft.Playground.Services
2+
@inject ShikiHighlightService ShikiHighlighter
23

34
<div class="code-block max-w-full">
45
@if(!string.IsNullOrEmpty(Title))
@@ -7,8 +8,8 @@
78
<span class="px-2 text-xs @(Highlight ? "text-green-400" : "text-gray-400")">● @Title</span>
89
</div>
910
}
10-
<div class="code-block-vscode max-h-120 overflow-y-auto" translate="no">
11-
<pre><code>@((MarkupString)HighlightedCode)</code></pre>
11+
<div class="code-block-shiki max-h-120 overflow-y-auto" translate="no">
12+
@((MarkupString)highlightedCode)
1213
</div>
1314
</div>
1415

@@ -18,5 +19,17 @@
1819
[Parameter] public string Size { get; set; } = "sm";
1920
[Parameter] public bool Highlight { get; set; } = false;
2021

21-
private string HighlightedCode => SyntaxHighlighter.Highlight(Code);
22+
private string highlightedCode = "";
23+
24+
protected override async Task OnParametersSetAsync()
25+
{
26+
if (!string.IsNullOrWhiteSpace(Code))
27+
{
28+
highlightedCode = await ShikiHighlighter.HighlightAsync(Code);
29+
}
30+
else
31+
{
32+
highlightedCode = "";
33+
}
34+
}
2235
}

playground/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
builder.Services.AddSingleton<TemplateService>();
2121
builder.Services.AddSingleton<CodeGenerationService>();
2222
builder.Services.AddSingleton<SemanticHighlightingService>();
23-
builder.Services.AddSingleton<CSharpSyntaxHighlighter>();
2423
builder.Services.AddScoped<UrlStateService>();
2524

25+
// Register Shiki syntax highlighting service
26+
builder.Services.AddScoped<ShikiHighlightService>();
27+
2628
await builder.Build().RunAsync();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Microsoft.JSInterop;
2+
3+
namespace Linqraft.Playground.Services;
4+
5+
/// <summary>
6+
/// Service for syntax highlighting using Shiki library via JavaScript interop.
7+
/// Replaces the custom CSharpSyntaxHighlighter for static code display.
8+
/// Note: SemanticHighlightingService is still used by EditorPane for enhanced Monaco editor highlighting.
9+
/// </summary>
10+
public class ShikiHighlightService
11+
{
12+
private readonly IJSRuntime _jsRuntime;
13+
private bool _initialized = false;
14+
15+
public ShikiHighlightService(IJSRuntime jsRuntime)
16+
{
17+
_jsRuntime = jsRuntime;
18+
}
19+
20+
/// <summary>
21+
/// Initialize Shiki (lazy initialization on first use).
22+
/// </summary>
23+
private async Task EnsureInitializedAsync()
24+
{
25+
if (_initialized)
26+
return;
27+
28+
try
29+
{
30+
await _jsRuntime.InvokeVoidAsync("shikiInterop.initialize");
31+
_initialized = true;
32+
}
33+
catch (Exception ex)
34+
{
35+
Console.WriteLine($"Failed to initialize Shiki: {ex.Message}");
36+
// Continue anyway - highlightCode will handle fallback
37+
}
38+
}
39+
40+
/// <summary>
41+
/// Highlight C# code and return HTML markup.
42+
/// </summary>
43+
/// <param name="code">The C# code to highlight</param>
44+
/// <param name="language">Language identifier (default: csharp)</param>
45+
/// <param name="theme">Theme name (default: dark-plus)</param>
46+
/// <returns>HTML string with syntax highlighting</returns>
47+
public async Task<string> HighlightAsync(string code, string language = "csharp", string theme = "dark-plus")
48+
{
49+
if (string.IsNullOrWhiteSpace(code))
50+
return string.Empty;
51+
52+
await EnsureInitializedAsync();
53+
54+
try
55+
{
56+
var html = await _jsRuntime.InvokeAsync<string>(
57+
"shikiInterop.highlightCode",
58+
code,
59+
language,
60+
theme
61+
);
62+
return html;
63+
}
64+
catch (Exception ex)
65+
{
66+
Console.WriteLine($"Failed to highlight code: {ex.Message}");
67+
// Fallback to plain pre/code block
68+
return $"<pre><code>{System.Net.WebUtility.HtmlEncode(code)}</code></pre>";
69+
}
70+
}
71+
}

playground/wwwroot/css/prism-vsc-dark-plus.css

Lines changed: 0 additions & 273 deletions
This file was deleted.

0 commit comments

Comments
 (0)