Skip to content

Commit 7a9b0d1

Browse files
committed
migrate from Sunlight to Prism for syntax highlighting (#982)
This avoids depending on a custom Github link, so we can use a library manager in the next commit.
1 parent 15ebfd1 commit 7a9b0d1

File tree

4 files changed

+66
-131
lines changed

4 files changed

+66
-131
lines changed

docs/release-notes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
# Release notes
44
## Upcoming release
55
* For the web UI:
6+
* Improved JSON parser code display:
7+
* You can now hover/click braces to highlight matching pairs.
8+
* You can now hover and click 'Copy' on the top-right to copy the full code to the clipboard.
9+
* Updated to newer syntax highlighting library.
10+
* Fixed some JSON files breaking page layout.
611
* Removed support for very old SMAPI logs.
712

813
## 4.2.1

src/SMAPI.Web/Views/JsonValidator/Index.cshtml

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,41 @@
2727
{
2828
<meta name="robots" content="noindex" />
2929
}
30+
31+
<!-- page CSS/JS -->
3032
<link rel="stylesheet" href="@Url.ContentWithCacheBust("~/Content/css/file-upload.css")" />
3133
<link rel="stylesheet" href="@Url.ContentWithCacheBust("~/Content/css/json-validator.css")" />
32-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/themes/sunlight.default.min.css" />
33-
34-
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
35-
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/sunlight.min.js" crossorigin="anonymous"></script>
36-
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/plugins/sunlight-plugin.linenumbers.min.js" crossorigin="anonymous"></script>
37-
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/lang/sunlight.javascript.min.js" crossorigin="anonymous"></script>
3834
<script src="@Url.ContentWithCacheBust("~/Content/js/file-upload.js")"></script>
3935
<script src="@Url.ContentWithCacheBust("~/Content/js/json-validator.js")"></script>
36+
37+
<!-- jQuery -->
38+
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
39+
40+
<!-- Prism -->
41+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prism-themes@1.9.0/themes/prism-vs.min.css" crossorigin="anonymous" />
42+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/prism.min.js" crossorigin="anonymous"></script>
43+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/components/prism-json.js" crossorigin="anonymous"></script>
44+
45+
<!-- Prism line numbers -->
46+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/line-numbers/prism-line-numbers.min.css" crossorigin="anonymous" />
47+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/line-numbers/prism-line-numbers.min.js" crossorigin="anonymous"></script>
48+
49+
<!-- Prism line highlight -->
50+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/line-highlight/prism-line-highlight.min.css" crossorigin="anonymous" />
51+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/line-highlight/prism-line-highlight.min.js" crossorigin="anonymous"></script>
52+
53+
<!-- Prism toolbar -->
54+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/toolbar/prism-toolbar.min.css" crossorigin="anonymous" />
55+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/toolbar/prism-toolbar.min.js" crossorigin="anonymous"></script>
56+
57+
<!-- Prism copy to clipboard -->
58+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js" crossorigin="anonymous"></script>
59+
60+
<!-- Prism match braces -->
61+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/match-braces/prism-match-braces.min.css" crossorigin="anonymous" />
62+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/plugins/match-braces/prism-match-braces.min.js" crossorigin="anonymous"></script>
63+
64+
<!-- init -->
4065
<script>
4166
$(function() {
4267
smapi.jsonValidator(@this.ForJson(this.Url.PlainAction("Index", "JsonValidator", new { schemaName = "$schemaName", id = "$id" })), @this.ForJson(Model.PasteId));
@@ -153,7 +178,7 @@ else if (Model is { IsEditView: false, PasteId: not null })
153178
@foreach (JsonValidatorErrorModel error in Model.Errors)
154179
{
155180
<tr data-schema-error="@error.SchemaErrorType">
156-
<td><a href="#L@(error.Line)">@error.Line</a></td>
181+
<td><a href="#code.@(error.Line)">@error.Line</a></td>
157182
<td>@error.Path</td>
158183
<td>@error.Message</td>
159184
</tr>
@@ -171,7 +196,7 @@ else if (Model is { IsEditView: false, PasteId: not null })
171196
}
172197
</select>) or <a href="@(this.Url.PlainAction("Index", "JsonValidator", new { id = this.Model.PasteId, schemaName = this.Model.SchemaName, operation = "edit" }))">edit this file</a>.
173198
</div>
174-
<pre id="raw-content" class="sunlight-highlight-javascript">@Model.Content</pre>
199+
<pre id="code" class="language-json line-numbers linkable-line-numbers match-braces"><code>@Model.Content</code></pre>
175200

176201
@if (isValidSchema)
177202
{

src/SMAPI.Web/wwwroot/Content/css/json-validator.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@
2121
}
2222

2323

24+
/*********
25+
** Code block
26+
*********/
27+
#code {
28+
max-width: calc(
29+
100vw
30+
- 10px /* #output padding */
31+
- 1em /* #content padding */
32+
- 1px /* #content border */
33+
- 10em /* #content-column position */
34+
- 3em /* scrollbar */
35+
);
36+
overflow-x: scroll;
37+
}
38+
39+
@media (min-width: 1020px) and (max-width: 1199px) {
40+
#code {
41+
max-width: calc(100vw - 10px - 1em - 1px - 7em - 3em); /* #content-column reduced to 7em */
42+
}
43+
}
44+
45+
@media (max-width: 1019px) {
46+
#code {
47+
max-width: calc(100vw - 10px - 1em - 1px - 0em - 2em); /* #content-column moved to 0em */
48+
}
49+
}
50+
51+
2452
/*********
2553
** Result banner
2654
*********/

src/SMAPI.Web/wwwroot/Content/js/json-validator.js

Lines changed: 0 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -2,141 +2,18 @@
22

33
var smapi = smapi || {};
44

5-
/**
6-
* Manages the logic for line range selections.
7-
* @param {int} maxLines The maximum number of lines in the content.
8-
*/
9-
smapi.LineNumberRange = function (maxLines) {
10-
var self = this;
11-
12-
/**
13-
* @var {int} minLine The first line in the selection, or null if no lines selected.
14-
*/
15-
self.minLine = null;
16-
17-
/**
18-
* @var {int} maxLine The last line in the selection, or null if no lines selected.
19-
*/
20-
self.maxLine = null;
21-
22-
/**
23-
* Parse line numbers from a URL hash.
24-
* @param {string} hash the URL hash to parse.
25-
*/
26-
self.parseFromUrlHash = function (hash) {
27-
self.minLine = null;
28-
self.maxLine = null;
29-
30-
// parse hash
31-
var hashParts = hash.match(/^#L(\d+)(?:-L(\d+))?$/);
32-
if (!hashParts || hashParts.length <= 1)
33-
return;
34-
35-
// extract min/max lines
36-
self.minLine = parseInt(hashParts[1]);
37-
self.maxLine = parseInt(hashParts[2]) || self.minLine;
38-
};
39-
40-
/**
41-
* Generate a URL hash for the current line range.
42-
* @returns {string} The generated URL hash.
43-
*/
44-
self.buildHash = function () {
45-
if (!self.minLine)
46-
return "";
47-
else if (self.minLine === self.maxLine)
48-
return "#L" + self.minLine;
49-
else
50-
return "#L" + self.minLine + "-L" + self.maxLine;
51-
}
52-
53-
/**
54-
* Get a list of all selected lines.
55-
* @returns {Array<int>} The selected line numbers.
56-
*/
57-
self.getLinesSelected = function () {
58-
// format
59-
if (!self.minLine)
60-
return [];
61-
62-
var lines = [];
63-
for (var i = self.minLine; i <= self.maxLine; i++)
64-
lines.push(i);
65-
return lines;
66-
};
67-
68-
return self;
69-
};
70-
715
/**
726
* UI logic for the JSON validator page.
737
* @param {string} urlFormat The URL format for a file, with $schemaName and $id placeholders.
748
* @param {string} fileId The file ID for the content being viewed, if any.
759
*/
7610
smapi.jsonValidator = function (urlFormat, fileId) {
77-
/**
78-
* The original content element.
79-
*/
80-
var originalContent = $("#raw-content").clone();
81-
82-
/**
83-
* The currently highlighted lines.
84-
*/
85-
var selection = new smapi.LineNumberRange();
86-
87-
/**
88-
* Rebuild the syntax-highlighted element.
89-
*/
90-
var formatCode = function () {
91-
// reset if needed
92-
$(".sunlight-container").replaceWith(originalContent.clone());
93-
94-
// apply default highlighting
95-
Sunlight.highlightAll({
96-
lineHighlight: selection.getLinesSelected()
97-
});
98-
99-
// fix line links
100-
$(".sunlight-line-number-margin a").each(function () {
101-
var link = $(this);
102-
var lineNumber = parseInt(link.text());
103-
link
104-
.attr("id", "L" + lineNumber)
105-
.attr("href", "#L" + lineNumber)
106-
.removeAttr("name")
107-
.data("line-number", lineNumber);
108-
});
109-
};
110-
111-
/**
112-
* Scroll the page so the selected range is visible.
113-
*/
114-
var scrollToRange = function () {
115-
if (!selection.minLine)
116-
return;
117-
118-
var targetLine = Math.max(1, selection.minLine - 5);
119-
$("#L" + targetLine).get(0).scrollIntoView();
120-
};
121-
12211
/**
12312
* Initialize the JSON validator page.
12413
*/
12514
var init = function () {
12615
var input = $("#input");
12716

128-
// set initial code formatting
129-
selection.parseFromUrlHash(location.hash);
130-
formatCode();
131-
scrollToRange();
132-
133-
// update code formatting on hash change
134-
$(window).on("hashchange", function () {
135-
selection.parseFromUrlHash(location.hash);
136-
formatCode();
137-
scrollToRange();
138-
});
139-
14017
// change format
14118
$("#output #format").on("change", function () {
14219
var schemaName = $(this).val();

0 commit comments

Comments
 (0)