mirror of
https://github.com/danbulant/mdsvexrs
synced 2026-05-19 04:08:47 +00:00
initial commit
This commit is contained in:
commit
abd71d6263
10 changed files with 1352 additions and 0 deletions
9
.envrc
Normal file
9
.envrc
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# the shebang is ignored, but nice for editors
|
||||||
|
|
||||||
|
if type -P lorri &>/dev/null; then
|
||||||
|
eval "$(lorri direnv)"
|
||||||
|
else
|
||||||
|
echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]'
|
||||||
|
use nix
|
||||||
|
fi
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
node_modules
|
||||||
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "markdown"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
markdown = { version = "1.0.0-alpha.21", features = ["serde"]}
|
||||||
|
itertools = "0.13"
|
||||||
|
serde_json = "1.0.133"
|
||||||
|
serde_yaml = "0.9.34"
|
||||||
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
|
regex = "1.11.1"
|
||||||
|
clap = { version = "4.5.21", features = ["derive"] }
|
||||||
|
syntext = "5.0"
|
||||||
5
README.md
Normal file
5
README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# MDSvexRS
|
||||||
|
|
||||||
|
A faster markdown preprocessor for svelte. Compiles `.md` files into `.svelte` with non-reactive blocks wrapped in direct HTML for faster compilation and rendering.
|
||||||
|
|
||||||
|
Note that this, like the original MDSvex, trusts it's input and doesn't escape HTML or script files.
|
||||||
508
highlighter/hanekawa.json
Normal file
508
highlighter/hanekawa.json
Normal file
|
|
@ -0,0 +1,508 @@
|
||||||
|
{
|
||||||
|
"type": "dark",
|
||||||
|
"name": "Hanekawa",
|
||||||
|
"colors": {
|
||||||
|
"foreground": "#c3c3c3",
|
||||||
|
"editor.foreground": "#F8F8F2",
|
||||||
|
"dropdown.foreground": "#c3c3c3",
|
||||||
|
"panelTitle.activeForeground": "#c3c3c3",
|
||||||
|
"panelTitle.inactiveForeground": "#c3c3c3",
|
||||||
|
"gitDecoration.ignoredResourceForeground": "#6f6f6f",
|
||||||
|
"list.invalidItemForeground": "#6f6f6f",
|
||||||
|
"list.errorForeground": "#FF5555",
|
||||||
|
"editorError.foreground": "#FF5555",
|
||||||
|
"input.background": "#1b1820AA",
|
||||||
|
"peekViewResult.background": "#1b1820",
|
||||||
|
"peekViewResult.fileForeground": "#988ccb",
|
||||||
|
"peekViewTitleDescription.foreground": "#988ccb",
|
||||||
|
"list.focusBackground": "#5a3b5d",
|
||||||
|
"quickInput.list.focusBackground": "#5a3b5dAA",
|
||||||
|
"quickInput.foreground": "#c3c3c3",
|
||||||
|
"peekViewTitleLabel.foreground": "#c3c3c3",
|
||||||
|
"selection.background": "#5a3b5d",
|
||||||
|
"editorGutter.modifiedBackground": "#152A3D",
|
||||||
|
"editorGutter.addedBackground": "#142F14",
|
||||||
|
"editorGutter.deletedBackground": "#1D1D1D",
|
||||||
|
"editorUnnecessaryCode.border": "#72737A",
|
||||||
|
"editorGroup.dropBackground": "#262626AA",
|
||||||
|
"panelSection.dropBackground": "#262626AA",
|
||||||
|
"statusBarItem.prominentBackground": "#5a3b5dAA",
|
||||||
|
"statusBarItem.prominentForeground": "#ebd6fc",
|
||||||
|
"keybindingLabel.background": "#2e2939",
|
||||||
|
"keybindingLabel.foreground": "#c7c7c7",
|
||||||
|
"keybindingLabel.bottomBorder": "#211c23",
|
||||||
|
"settings.sashBorder": "#211c23",
|
||||||
|
"tab.hoverBackground": "#262129AA",
|
||||||
|
"list.activeSelectionBackground": "#5a3b5d",
|
||||||
|
"peekViewResult.selectionBackground": "#5a3b5d",
|
||||||
|
"list.hoverBackground": "#5a3b5d7a",
|
||||||
|
"editor.selectionHighlightBackground": "#262626",
|
||||||
|
"editor.linkedEditingBackground": "#262626",
|
||||||
|
"editor.selectionBackground": "#5a3b5dAA",
|
||||||
|
"minimap.selectionHighlight": "#5a3b5d",
|
||||||
|
"list.activeSelectionForeground": "#ebd6fc",
|
||||||
|
"list.focusForeground": "#ebd6fc",
|
||||||
|
"pickerGroup.foreground": "#988ccb",
|
||||||
|
"descriptionForeground": "#988ccb",
|
||||||
|
"gitlens.trailingLineForegroundColor": "#988ccb99",
|
||||||
|
"input.placeholderForeground": "#988ccb",
|
||||||
|
"list.deemphasizedForeground": "#988ccb",
|
||||||
|
"inputValidation.errorBackground": "#36151F",
|
||||||
|
"minimap.errorHighlight": "#FF5555AA",
|
||||||
|
"inputValidation.errorForeground": "#FF5555",
|
||||||
|
"inputValidation.warningBackground": "#191a18",
|
||||||
|
"list.inactiveSelectionBackground": "#36233899",
|
||||||
|
"list.inactiveSelectionForeground": "#c3c3c3",
|
||||||
|
"editor.background": "#100d11",
|
||||||
|
"errorForeground": "#FF5555",
|
||||||
|
"tab.activeBackground": "#262129",
|
||||||
|
"panelSectionHeader.background": "#0f0d10",
|
||||||
|
"focusBorder": "#211c23",
|
||||||
|
"pickerGroup.border": "#211c23",
|
||||||
|
"tab.border": "#211c23",
|
||||||
|
"tree.indentGuidesStroke": "#211c23",
|
||||||
|
"sideBar.border": "#211c23",
|
||||||
|
"editorIndentGuide.background": "#211c23",
|
||||||
|
"editorIndentGuide.activeBackground": "#211c23",
|
||||||
|
"inputOption.activeBorder": "#211c23",
|
||||||
|
"textBlockQuote.border": "#211c23",
|
||||||
|
"dropdown.border": "#211c23",
|
||||||
|
"input.border": "#211c23",
|
||||||
|
"activityBar.border": "#211c23",
|
||||||
|
"editorGroup.border": "#211c23",
|
||||||
|
"editorOverviewRuler.border": "#211c23",
|
||||||
|
"editorError.border": "#36151F",
|
||||||
|
"editorWarning.border": "#211c23",
|
||||||
|
"editorInfo.border": "#211c23",
|
||||||
|
"editorHint.border": "#211c23",
|
||||||
|
"editorHoverWidget.border": "#211c23",
|
||||||
|
"debugExceptionWidget.border": "#211c23",
|
||||||
|
"merge.border": "#211c23",
|
||||||
|
"panel.border": "#211c23",
|
||||||
|
"statusBar.border": "#211c23",
|
||||||
|
"titleBar.border": "#211c23",
|
||||||
|
"notificationCenter.border": "#211c23",
|
||||||
|
"notifications.border": "#211c23",
|
||||||
|
"editorCursor.background": "#100d11",
|
||||||
|
"terminalCursor.background": "#100d11",
|
||||||
|
"terminalCursor.foreground": "#7660A4",
|
||||||
|
"editorSuggestWidget.border": "#100d11",
|
||||||
|
"panel.background": "#100d11",
|
||||||
|
"sideBar.background": "#100d11",
|
||||||
|
"tab.inactiveBackground": "#100d11",
|
||||||
|
"editorGroupHeader.tabsBackground": "#100d11",
|
||||||
|
"dropdown.background": "#100d11",
|
||||||
|
"editorLineNumber.foreground": "#777495",
|
||||||
|
"gitlens.gutterForegroundColor": "#777495",
|
||||||
|
"checkbox.border": "#7e7997",
|
||||||
|
"checkbox.foreground": "#7660A4",
|
||||||
|
"toolbar.hoverBackground": "#262129",
|
||||||
|
"toolbar.hoverOutline": "#262129",
|
||||||
|
"toolbar.activeBackground": "#262129",
|
||||||
|
"textLink.foreground": "#7660A4AA",
|
||||||
|
"textLink.activeForeground": "#7660A4",
|
||||||
|
"settings.modifiedItemIndicator": "#7660A4",
|
||||||
|
"textBlockQuote.background": "#281f28",
|
||||||
|
"textCodeBlock.background": "#281f28",
|
||||||
|
"textSeparator.foreground": "#211c23",
|
||||||
|
"editorCursor.foreground": "#7660A4",
|
||||||
|
"editorWidget.border": "#7660A4",
|
||||||
|
"list.highlightForeground": "#8968dc",
|
||||||
|
"editorLineNumber.activeForeground": "#7660A4",
|
||||||
|
"debugIcon.breakpointForeground": "#7660A4",
|
||||||
|
"tab.activeBorder": "#7660A4",
|
||||||
|
"peekView.border": "#7660A4",
|
||||||
|
"statusBar.background": "#7660A4",
|
||||||
|
"statusBarItem.remoteBackground": "#5a3b5d",
|
||||||
|
"statusBarItem.remoteForeground": "#ebd6fc",
|
||||||
|
"statusBar.noFolderBackground": "#7660A4",
|
||||||
|
"panelTitle.activeBorder": "#7660A4",
|
||||||
|
"statusBar.debuggingBackground": "#7660A4",
|
||||||
|
"editorBracketMatch.border": "#7660A4",
|
||||||
|
"gitDecoration.modifiedResourceForeground": "#7660A4",
|
||||||
|
"activityBar.foreground": "#7660A4",
|
||||||
|
"activityBarBadge.background": "#7660A4",
|
||||||
|
"activityBarBadge.foreground": "#141115",
|
||||||
|
"badge.background": "#7660A4",
|
||||||
|
"badge.foreground": "#141115",
|
||||||
|
"progressBar.background": "#7660A4",
|
||||||
|
"scrollbarSlider.hoverBackground": "#7660A49A",
|
||||||
|
"notificationToast.border": "#7660A49A",
|
||||||
|
"scrollbarSlider.background": "#7660A45A",
|
||||||
|
"scrollbarSlider.activeBackground": "#7660A49A",
|
||||||
|
"activityBar.inactiveForeground": "#7660A4BB",
|
||||||
|
"button.background": "#2e2939",
|
||||||
|
"button.foreground": "#c7c7c7",
|
||||||
|
"button.secondaryForeground": "#c7c7c7BB",
|
||||||
|
"button.secondaryBackground": "#2e2939CC",
|
||||||
|
"editor.lineHighlightBackground": "#14111577",
|
||||||
|
"gitlens.trailingLineBackgroundColor": "#1e1d1f77",
|
||||||
|
"editor.lineHighlightBorder": "#14111500",
|
||||||
|
"sideBarSectionHeader.background": "#141115",
|
||||||
|
"activityBar.background": "#141115",
|
||||||
|
"editorGutter.background": "#00000000",
|
||||||
|
"symbolIcon.arrayForeground": "#86dbfd",
|
||||||
|
"symbolIcon.constantForeground": "#86dbfd",
|
||||||
|
"symbolIcon.enumeratorMemberForeground": "#86dbfd",
|
||||||
|
"symbolIcon.booleanForeground": "#d55fde",
|
||||||
|
"symbolIcon.classForeground": "#e39656",
|
||||||
|
"symbolIcon.enumeratorForeground": "#7ceec8",
|
||||||
|
"symbolIcon.fieldForeground": "#7ceec8",
|
||||||
|
"symbolIcon.constructorForeground": "#e39656",
|
||||||
|
"symbolIcon.functionForeground": "#9a97f4",
|
||||||
|
"symbolIcon.colorForeground": "#7660A4",
|
||||||
|
"symbolIcon.eventForeground": "#7660A4",
|
||||||
|
"symbolIcon.fileForeground": "#7e7997",
|
||||||
|
"symbolIcon.folderForeground": "#7e7997",
|
||||||
|
"symbolIcon.interfaceForeground": "#F6E3CC",
|
||||||
|
"editor.findMatchBackground": "#5a3b5d",
|
||||||
|
"editor.findMatchBorder": "#7660A4",
|
||||||
|
"editor.wordHighlightStrongBackground": "#262626AA",
|
||||||
|
"editor.snippetTabstopHighlightBackground": "#262626AA",
|
||||||
|
"editor.findMatchHighlightBackground": "#305D49AA",
|
||||||
|
"peekViewEditor.matchHighlightBackground": "#362338",
|
||||||
|
"editor.wordHighlightBackground": "#262626AA",
|
||||||
|
"editorOverviewRuler.findMatchForeground": "#362338",
|
||||||
|
"searchEditor.findMatchBackground": "#305D49",
|
||||||
|
"peekViewResult.matchHighlightBackground": "#362338",
|
||||||
|
"editorWidget.background": "#0f0d10",
|
||||||
|
"debugToolBar.background": "#0f0d10",
|
||||||
|
"debugView.stateLabelBackground": "#262129",
|
||||||
|
"debugTokenExpression.string": "#F6E3CC",
|
||||||
|
"debugTokenExpression.boolean": "#d55fde",
|
||||||
|
"debugTokenExpression.value": "#988ccb",
|
||||||
|
"debugTokenExpression.number": "#86dbfd",
|
||||||
|
"debugTokenExpression.error": "#FF5555",
|
||||||
|
"debugTokenExpression.name": "#e39656",
|
||||||
|
"breadcrumb.foreground": "#777495",
|
||||||
|
"breadcrumb.focusForeground": "#988ccb",
|
||||||
|
"breadcrumb.activeSelectionForeground": "#9a97f4",
|
||||||
|
"testing.iconFailed": "#FF5555",
|
||||||
|
"testing.iconErrored": "#FF5555",
|
||||||
|
"testing.runAction": "#7660A4",
|
||||||
|
"welcomePage.tileBackground": "#281f28AA",
|
||||||
|
"welcomePage.buttonBackground": "#281f28AA",
|
||||||
|
"welcomePage.tileHoverBackground": "#5a3b5dAA",
|
||||||
|
"welcomePage.buttonHoverBackground": "#5a3b5dAA",
|
||||||
|
"testing.message.hint.decorationForeground": "#988ccb",
|
||||||
|
"settings.focusedRowBackground": "#262129",
|
||||||
|
"notebook.focusedRowBorder": "#262129",
|
||||||
|
"notebook.rowHoverBackground": "#1e1d1f",
|
||||||
|
"debugView.valueChangedHighlight": "#5a3b5d99",
|
||||||
|
"editorSuggestWidget.background": "#0f0d10",
|
||||||
|
"notifications.background": "#0f0d1080",
|
||||||
|
"peekViewTitle.background": "#0f0d10",
|
||||||
|
"widget.shadow": "#1111115a",
|
||||||
|
"scrollbar.shadow": "#1111113a",
|
||||||
|
"titleBar.activeBackground": "#0f0d10",
|
||||||
|
"titleBar.inactiveBackground": "#0f0d10",
|
||||||
|
"gitlens.gutterBackgroundColor": "#281f28",
|
||||||
|
"peekViewEditor.background": "#281f28",
|
||||||
|
"peekViewEditorGutter.background": "#281f28",
|
||||||
|
"terminal.ansiRed": "#E356A7",
|
||||||
|
"terminal.ansiGreen": "#42E66C",
|
||||||
|
"terminal.ansiYellow": "#EFA554",
|
||||||
|
"terminal.ansiBlue": "#9B6BDF",
|
||||||
|
"terminal.ansiMagenta": "#E64747",
|
||||||
|
"terminal.ansiCyan": "#75D7EC",
|
||||||
|
"editor.stackFrameHighlightBackground": "#5a3b5d",
|
||||||
|
"debugIcon.breakpointCurrentStackframeForeground": "#5a3b5d",
|
||||||
|
"debugIcon.breakpointDisabledForeground": "#6f6f6f",
|
||||||
|
"debugIcon.breakpointUnverifiedForeground": "#72737A",
|
||||||
|
"debugIcon.startForeground": "#7660A4",
|
||||||
|
"debugIcon.continueForeground": "#7660A4",
|
||||||
|
"debugIcon.stepOverForeground": "#9B6BDF",
|
||||||
|
"debugIcon.stepIntoForeground": "#EFA554",
|
||||||
|
"debugIcon.stopForeground": "#FF5555",
|
||||||
|
"debugIcon.stepOutForeground": "#75D7EC",
|
||||||
|
"debugIcon.restartForeground": "#42E66C",
|
||||||
|
"debugConsole.infoForeground": "#988ccb",
|
||||||
|
"debugConsole.errorForeground": "#FF5555",
|
||||||
|
"debugConsole.sourceForeground": "#9a97f4",
|
||||||
|
"debugIcon.breakpointStackframeForeground": "#362338",
|
||||||
|
"editor.focusedStackFrameHighlightBackground": "#362338",
|
||||||
|
"diffEditor.removedTextBackground": "#1D1D1D50",
|
||||||
|
"diffEditor.insertedTextBackground": "#142F1450",
|
||||||
|
"gitlens.gutterUncommittedForegroundColor": "#152A3D",
|
||||||
|
"gitlens.lineHighlightBackgroundColor": "#142F1450",
|
||||||
|
"charts.red": "#E356A7",
|
||||||
|
"charts.blue": "#9B6BDF",
|
||||||
|
"charts.yellow": "#EFA554",
|
||||||
|
"charts.orange": "#9a97f4",
|
||||||
|
"charts.green": "#42E66C",
|
||||||
|
"charts.purple": "#7ceec8",
|
||||||
|
"editorBracketHighlight.foreground1": "#F8F8F2",
|
||||||
|
"editorBracketHighlight.foreground2": "#7ceec8",
|
||||||
|
"editorBracketHighlight.foreground3": "#e39656",
|
||||||
|
"editorBracketHighlight.foreground4": "#9a97f4",
|
||||||
|
"editorBracketHighlight.foreground5": "#7ceec8",
|
||||||
|
"editorBracketHighlight.foreground6": "#86dbfd",
|
||||||
|
"editorBracketHighlight.unexpectedBracket.foreground": "#FF5555"
|
||||||
|
},
|
||||||
|
"semanticHighlighting": true,
|
||||||
|
"semanticTokenColors": {
|
||||||
|
"variable.readonly.local": "#F8F8F2",
|
||||||
|
"function.declaration": "#e39656"
|
||||||
|
},
|
||||||
|
"tokenColors": [
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"emphasis"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"strong"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"invalid"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#FF5555",
|
||||||
|
"fontStyle": "underline strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"invalid.deprecated"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#988ccb",
|
||||||
|
"fontStyle": "underline italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Underlined markup",
|
||||||
|
"scope": [
|
||||||
|
"markup.underline"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "underline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bold markup",
|
||||||
|
"scope": [
|
||||||
|
"markup.bold"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Markup italic",
|
||||||
|
"scope": [
|
||||||
|
"markup.italic"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"support.type.property-name.json",
|
||||||
|
"meta.object-literal.key",
|
||||||
|
"variable.object.property",
|
||||||
|
"variable.other.readwrite.alias",
|
||||||
|
"variable.other.property",
|
||||||
|
"meta.brace"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#F8F8F2",
|
||||||
|
"fontStyle": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"source",
|
||||||
|
"punctuation.definition.parameters",
|
||||||
|
"punctuation.definition.block",
|
||||||
|
"text.html.markdown",
|
||||||
|
"variable.other.constant",
|
||||||
|
"meta.definition.variable",
|
||||||
|
"variable.other.constant.js",
|
||||||
|
"variable.other.constant.ts"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#F8F8F2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"variable.parameter",
|
||||||
|
"meta.parameters",
|
||||||
|
"meta.function-call.arguments",
|
||||||
|
"markup.inline.raw.string.markdown",
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#F6E3CC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"constant.language.json",
|
||||||
|
"keyword",
|
||||||
|
"storage.type",
|
||||||
|
"storage.modifier",
|
||||||
|
"keyword.operator.optional",
|
||||||
|
"keyword.operator.new",
|
||||||
|
"keyword.operator.instanceof",
|
||||||
|
"keyword.operator.expression.typeof",
|
||||||
|
"constant.language.boolean",
|
||||||
|
"variable.language.this",
|
||||||
|
"constant.language.undefined",
|
||||||
|
"constant.language.java",
|
||||||
|
"constant.language.python",
|
||||||
|
"support.type.primitive.ts",
|
||||||
|
"markup.heading.setext",
|
||||||
|
"punctuation.definition.raw.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#d55fde"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"variable.other.constant",
|
||||||
|
"meta.definition.method",
|
||||||
|
"meta.definition.function",
|
||||||
|
"meta.embedded.line",
|
||||||
|
"markup.inline.raw.string",
|
||||||
|
"variable.css",
|
||||||
|
"meta.function",
|
||||||
|
"support.class",
|
||||||
|
"meta.method.identifier",
|
||||||
|
"variable.other.readwrite.alias"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#e39656"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"keyword.operator",
|
||||||
|
"entity.name.tag",
|
||||||
|
"meta.template.expression",
|
||||||
|
"punctuation.definition.list.begin",
|
||||||
|
"support.class.component.svelte",
|
||||||
|
"punctuation.definition.link.restructuredtext",
|
||||||
|
"punctuation.definition.typeparameters"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#ff8fec"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"entity.name.type.class",
|
||||||
|
"meta.type",
|
||||||
|
"variable.other.object",
|
||||||
|
"entity.name.type",
|
||||||
|
"markup.heading",
|
||||||
|
"string.other.link.title",
|
||||||
|
"storage.modifier.import",
|
||||||
|
"meta.import",
|
||||||
|
"storage.type.java",
|
||||||
|
"support.type.property-name",
|
||||||
|
"meta.property-name"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#7ceec8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"support.type.primitive",
|
||||||
|
"entity.name.type.interface",
|
||||||
|
"variable.parameter.function-call"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#7ceec8",
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"meta.function-call",
|
||||||
|
"meta.decorator",
|
||||||
|
"meta.link",
|
||||||
|
"meta.method-call",
|
||||||
|
"meta.function-call.ts",
|
||||||
|
"storage.type.annotation",
|
||||||
|
"meta.declaration.annotation",
|
||||||
|
"support.function",
|
||||||
|
"meta.assertion.look-ahead.regexp",
|
||||||
|
"entity.name.function"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#9a97f4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"entity.other.attribute-name"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#9a97f4",
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"constant.numeric",
|
||||||
|
"support.type.object.module",
|
||||||
|
"variable.other.enummember",
|
||||||
|
"meta.enum.declaration",
|
||||||
|
"constant.other.color",
|
||||||
|
"variable.language.special",
|
||||||
|
"support.constant",
|
||||||
|
"meta.type.parameters",
|
||||||
|
"fenced_code.block.language.markdown",
|
||||||
|
"entity.name.section.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#86dbfd"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#86dbfd",
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"comment",
|
||||||
|
"markup.quote"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#79769f",
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"markup.fenced_code.block"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#988ccb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
95
highlighter/index.js
Normal file
95
highlighter/index.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { createHighlighter } from 'shiki'
|
||||||
|
import { readFileSync } from 'fs'
|
||||||
|
import readline from "readline/promises"
|
||||||
|
import {
|
||||||
|
transformerNotationDiff,
|
||||||
|
transformerNotationHighlight,
|
||||||
|
transformerNotationWordHighlight,
|
||||||
|
transformerNotationErrorLevel,
|
||||||
|
transformerMetaHighlight,
|
||||||
|
transformerMetaWordHighlight,
|
||||||
|
transformerNotationFocus
|
||||||
|
} from "@shikijs/transformers"
|
||||||
|
|
||||||
|
const theme = JSON.parse(readFileSync('hanekawa.json', 'utf-8'))
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
})
|
||||||
|
|
||||||
|
const highlighter = createHighlighter({
|
||||||
|
langs: ["javascript", "rust", "c#", "c", "asm", "sh", "ts"],
|
||||||
|
themes: [theme]
|
||||||
|
})
|
||||||
|
|
||||||
|
const tokenMap = {
|
||||||
|
fn: "meta.declaration.annotation"
|
||||||
|
}
|
||||||
|
|
||||||
|
let loadedLangs;
|
||||||
|
let sum = 0
|
||||||
|
|
||||||
|
function time(start) {
|
||||||
|
let time = performance.now() - start
|
||||||
|
sum += time
|
||||||
|
// console.error(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.on('line', async (line) => {
|
||||||
|
let start = performance.now();
|
||||||
|
const data = JSON.parse(line)
|
||||||
|
let { lang, inline, code, meta } = data
|
||||||
|
if(lang[0] == ".") {
|
||||||
|
let scope = lang.slice(1)
|
||||||
|
let color
|
||||||
|
if(tokenMap[scope]) scope = tokenMap[scope]
|
||||||
|
for(let tokens of theme.tokenColors) {
|
||||||
|
if(tokens.scope?.includes(scope)) {
|
||||||
|
color = tokens.settings.foreground
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let html
|
||||||
|
// shiki does this for us, but we need to do it manually here
|
||||||
|
code = simplehtmlentities(code)
|
||||||
|
if(color) {
|
||||||
|
html = `<code data-pretty-code-figure style="color: ${color}">${code}</code>`
|
||||||
|
} else {
|
||||||
|
html = code
|
||||||
|
}
|
||||||
|
time(start)
|
||||||
|
console.log(JSON.stringify({ html, elapsed: performance.now() - start, sum }))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let shiki = await highlighter
|
||||||
|
if(!loadedLangs) loadedLangs = shiki.getLoadedLanguages()
|
||||||
|
if(!loadedLangs.includes(lang)) {
|
||||||
|
await shiki.loadLanguage(lang)
|
||||||
|
loadedLangs = shiki.getLoadedLanguages()
|
||||||
|
}
|
||||||
|
let html = shiki.codeToHtml(code, {
|
||||||
|
lang,
|
||||||
|
structure: inline ? 'inline' : 'classic',
|
||||||
|
theme: "Hanekawa",
|
||||||
|
meta: {
|
||||||
|
"data-pretty-code": "",
|
||||||
|
__raw: meta
|
||||||
|
},
|
||||||
|
transformers: [
|
||||||
|
transformerNotationDiff(),
|
||||||
|
transformerNotationHighlight(),
|
||||||
|
transformerNotationWordHighlight(),
|
||||||
|
transformerNotationErrorLevel(),
|
||||||
|
transformerNotationFocus()
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if(inline) html = `<code data-pretty-code-figure>${html}</code>`
|
||||||
|
time(start)
|
||||||
|
let out = JSON.stringify({ html, elapsed: performance.now() - start, sum });
|
||||||
|
console.log(out)
|
||||||
|
})
|
||||||
|
|
||||||
|
function simplehtmlentities(str) {
|
||||||
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
||||||
|
}
|
||||||
15
highlighter/package.json
Normal file
15
highlighter/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "highlighter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"public": false,
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/transformers": "^1.22.2",
|
||||||
|
"shiki": "^1.24.0"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "Daniel Bulant"
|
||||||
|
}
|
||||||
9
package.json
Normal file
9
package.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "markdown",
|
||||||
|
"scripts": {
|
||||||
|
"build": "cargo build --release"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"highlighter": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
shell.nix
Normal file
38
shell.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {}, pkgsun ? import <nixos-unstable> {} }:
|
||||||
|
let
|
||||||
|
fenix = import (fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz") { };
|
||||||
|
rust-toolchain =
|
||||||
|
fenix.default.toolchain;
|
||||||
|
in
|
||||||
|
pkgs.mkShell rec {
|
||||||
|
buildInputs = with pkgs;[
|
||||||
|
openssl
|
||||||
|
pkg-config
|
||||||
|
cmake
|
||||||
|
zlib
|
||||||
|
rust-toolchain
|
||||||
|
|
||||||
|
libclang
|
||||||
|
llvmPackages.clang
|
||||||
|
openssl
|
||||||
|
clang
|
||||||
|
llvmPackages.libclang.lib stdenv.cc.libc
|
||||||
|
];
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
pkg-config
|
||||||
|
fontconfig
|
||||||
|
rustPlatform.bindgenHook
|
||||||
|
];
|
||||||
|
|
||||||
|
preConfigure = with pkgs;''
|
||||||
|
export BINDGEN_EXTRA_CLANG_ARGS="-isystem ${clang}/resource-root/include $NIX_CFLAGS_COMPILE"
|
||||||
|
'';
|
||||||
|
|
||||||
|
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}";
|
||||||
|
OPENSSL_DIR="${pkgs.openssl.dev}";
|
||||||
|
OPENSSL_LIB_DIR="${pkgs.openssl.out}/lib";
|
||||||
|
RUST_SRC_PATH = "${pkgsun.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||||
|
RUST_PATH="${rust-toolchain}";
|
||||||
|
RUST_LOG="debug";
|
||||||
|
LIBCLANG_PATH = "${pkgs.llvmPackages_14.libclang.lib}/lib";
|
||||||
|
}
|
||||||
657
src/main.rs
Normal file
657
src/main.rs
Normal file
|
|
@ -0,0 +1,657 @@
|
||||||
|
use std::{io::{stdin, BufRead, BufReader, Read, Write}, process::{Child, ChildStdout, Command, Stdio}, sync::LazyLock, time::{Duration, Instant}};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use markdown::{mdast::{Blockquote, Break, Code, Definition, Delete, Emphasis, FootnoteDefinition, FootnoteReference, Heading, Html, Image, ImageReference, InlineCode, InlineMath, Link, LinkReference, List, ListItem, Math, MdxFlowExpression, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Node, Paragraph, Root, Strong, Table, TableCell, TableRow, Text, ThematicBreak, Toml, Yaml}, unist::Position, Constructs};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ToHtmlResult {
|
||||||
|
html: String,
|
||||||
|
svelte: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtmlResult {
|
||||||
|
fn new(html: String, svelte: bool) -> Self {
|
||||||
|
Self { html, svelte }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self::new("".to_string(), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ToHtml {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult;
|
||||||
|
|
||||||
|
fn visit(&self, _ctx: &mut Context) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToHtml for Vec<T> where T: ToHtml {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
merge(&self.iter().map(|t| t.to_html(ctx)).collect::<Vec<_>>())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit(&self, ctx: &mut Context) {
|
||||||
|
for t in self {
|
||||||
|
t.visit(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Root {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
self.children.to_html(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit(&self, ctx: &mut Context) {
|
||||||
|
self.children.visit(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Blockquote {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<blockquote>{}</blockquote>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for FootnoteDefinition {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit(&self, _ctx: &mut Context) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MdxJsxFlowElement {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for List {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
// todo!()
|
||||||
|
let litype = match self.ordered {
|
||||||
|
true => "ol",
|
||||||
|
false => "ul"
|
||||||
|
};
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<{}>{}</{}>", litype, children.html, litype), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MdxjsEsm {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Toml {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Yaml {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
ToHtmlResult::empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit(&self, ctx: &mut Context) {
|
||||||
|
let value = serde_yaml::from_str(&self.value).unwrap();
|
||||||
|
ctx.yaml = Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Break {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
ToHtmlResult::new("<br>".to_string(), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LANG_HINT_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| regex::Regex::new(r"\{:(?<lang>[\w.]+)\}$").unwrap());
|
||||||
|
|
||||||
|
impl ToHtml for InlineCode {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let value = &self.value;
|
||||||
|
// if value ends with {lang} then it's a language hint
|
||||||
|
let output = if let Some(caps) = LANG_HINT_REGEX.captures(value) {
|
||||||
|
let lang = &caps["lang"];
|
||||||
|
let code = &value[..value.len() - lang.len() - 3];
|
||||||
|
ctx.highlight(HighlightRequest {
|
||||||
|
lang: lang.to_string(),
|
||||||
|
inline: true,
|
||||||
|
code: code.to_string(),
|
||||||
|
meta: None
|
||||||
|
})
|
||||||
|
} else if let Some(lang) = &ctx.default_lang {
|
||||||
|
ctx.highlight(HighlightRequest {
|
||||||
|
lang: lang.clone(),
|
||||||
|
inline: true,
|
||||||
|
code: value.clone(),
|
||||||
|
meta: None
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
format!("<code>{}</code>", html_encode(&self.value))
|
||||||
|
};
|
||||||
|
ToHtmlResult::new(output, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for InlineMath {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Delete {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Emphasis {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<em>{}</em>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MdxTextExpression {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for FootnoteReference {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Html {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let value = self.value.clone();
|
||||||
|
if value.starts_with("<script") {
|
||||||
|
ctx.script = Some(value);
|
||||||
|
|
||||||
|
return ToHtmlResult::empty();
|
||||||
|
}
|
||||||
|
ToHtmlResult::new(value, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Image {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let alt = &self.alt;
|
||||||
|
let title = self.title.as_ref().map(|t| format!(" title=\"{}\"", t)).unwrap_or_default();
|
||||||
|
let url = &self.url;
|
||||||
|
ToHtmlResult::new(format!("<img src=\"{}\" alt=\"{}\"{}>", url, alt, title), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for ImageReference {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MdxJsxTextElement {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Link {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
let title = self.title.as_ref().map(|t| format!(" title=\"{}\"", t)).unwrap_or_default();
|
||||||
|
ToHtmlResult::new(format!("<a href=\"{}\"{}>{}</a>", self.url, title, children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for LinkReference {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Strong {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<strong>{}</strong>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Text {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
ToHtmlResult::new(html_encode(&self.value), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Code {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let value = &self.value;
|
||||||
|
let lang = self.lang.as_ref().or(ctx.default_lang.as_ref());
|
||||||
|
let highlighted = if let Some(lang) = lang {
|
||||||
|
ctx.highlight(HighlightRequest {
|
||||||
|
code: value.clone(),
|
||||||
|
inline: false,
|
||||||
|
lang: lang.clone(),
|
||||||
|
meta: self.meta.clone()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
format!("<pre><code>{}</code></pre>", html_encode(&self.value))
|
||||||
|
};
|
||||||
|
ToHtmlResult::new(highlighted, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Math {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MdxFlowExpression {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slug(str: &str) -> String {
|
||||||
|
str.to_lowercase().replace(" ", "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Heading {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
let text = self.children.iter().filter_map(|c| {
|
||||||
|
match c {
|
||||||
|
Node::Text(t) => Some(t.value.clone()),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}).join("");
|
||||||
|
let mut slug = slug(&text);
|
||||||
|
if ctx.titles.iter().any(|t| t.id == slug) {
|
||||||
|
let mut i = 1;
|
||||||
|
while ctx.titles.iter().any(|t| t.id == format!("{}-{}", slug, i)) {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
slug = format!("{}-{}", slug, i);
|
||||||
|
}
|
||||||
|
ctx.titles.push(Title {
|
||||||
|
level: self.depth,
|
||||||
|
text: text.clone(),
|
||||||
|
id: slug.clone(),
|
||||||
|
pos: self.position.clone()
|
||||||
|
});
|
||||||
|
ToHtmlResult::new(format!("\n<h{} id=\"{}\">{}</h{}>\n", self.depth, slug, children.html, self.depth), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Table {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<table>{}</table>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for ThematicBreak {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
ToHtmlResult::new("\n<hr>\n".to_string(), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for TableRow {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<tr>{}</tr>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for TableCell {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<td>{}</td>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for ListItem {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<li>{}</li>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Definition {
|
||||||
|
fn to_html(&self, _ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Paragraph {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
let children = self.children.to_html(ctx);
|
||||||
|
ToHtmlResult::new(format!("<p>{}</p>", children.html), children.svelte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! node_impl {
|
||||||
|
($($node:ident($name:ident)),+) => {
|
||||||
|
impl ToHtml for Node {
|
||||||
|
fn to_html(&self, ctx: &mut Context) -> ToHtmlResult {
|
||||||
|
match self {
|
||||||
|
$(markdown::mdast::Node::$node($name) => $name.to_html(ctx)),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn visit(&self, ctx: &mut Context) {
|
||||||
|
match self {
|
||||||
|
$(markdown::mdast::Node::$node($name) => $name.visit(ctx)),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_impl!(
|
||||||
|
Root(root),
|
||||||
|
Blockquote(blockquote),
|
||||||
|
FootnoteDefinition(footnote_definition),
|
||||||
|
MdxJsxFlowElement(mdx_jsx_flow_element),
|
||||||
|
List(list),
|
||||||
|
MdxjsEsm(mdxjs_esm),
|
||||||
|
Toml(toml),
|
||||||
|
Yaml(yaml),
|
||||||
|
Break(rbreak),
|
||||||
|
InlineCode(inline_code),
|
||||||
|
InlineMath(inline_math),
|
||||||
|
Delete(delete),
|
||||||
|
Emphasis(emphasis),
|
||||||
|
MdxTextExpression(mdx_text_expression),
|
||||||
|
FootnoteReference(footnote_reference),
|
||||||
|
Html(html),
|
||||||
|
Image(image),
|
||||||
|
ImageReference(image_reference),
|
||||||
|
MdxJsxTextElement(mdx_jsx_text_element),
|
||||||
|
Link(link),
|
||||||
|
LinkReference(link_reference),
|
||||||
|
Strong(strong),
|
||||||
|
Text(text),
|
||||||
|
Code(code),
|
||||||
|
Math(math),
|
||||||
|
MdxFlowExpression(mdx_flow_expression),
|
||||||
|
Heading(heading),
|
||||||
|
Table(table),
|
||||||
|
ThematicBreak(thematic_break),
|
||||||
|
TableRow(table_row),
|
||||||
|
TableCell(table_cell),
|
||||||
|
ListItem(list_item),
|
||||||
|
Definition(definition),
|
||||||
|
Paragraph(paragraph)
|
||||||
|
);
|
||||||
|
|
||||||
|
fn svelte_html_encode(string: String) -> String {
|
||||||
|
String::from("{@html `") + &string.replace("\\", "\\\\").replace("`", "\\`") + "`}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge(results: &[ToHtmlResult]) -> ToHtmlResult {
|
||||||
|
let chunked = results.iter().chunk_by(|r| r.svelte);
|
||||||
|
let mut html = chunked.into_iter().map(|(svelte, results)| {
|
||||||
|
let html = results.map(|r| r.html.clone()).join("");
|
||||||
|
ToHtmlResult::new(html, svelte)
|
||||||
|
});
|
||||||
|
let Some(first) = html.next() else {
|
||||||
|
return ToHtmlResult::new("".to_string(), false);
|
||||||
|
};
|
||||||
|
if let Some(second) = html.next() {
|
||||||
|
ToHtmlResult::new(
|
||||||
|
[first, second].into_iter().chain(html).map(|r| {
|
||||||
|
if r.html.is_empty() {
|
||||||
|
return "".to_string();
|
||||||
|
}
|
||||||
|
if r.svelte {
|
||||||
|
r.html
|
||||||
|
} else {
|
||||||
|
svelte_html_encode(r.html)
|
||||||
|
}
|
||||||
|
}).join(""),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from markdown-rs
|
||||||
|
pub fn html_encode(value: &str) -> String {
|
||||||
|
let encode_html = true; // originally a param
|
||||||
|
// It’ll grow a bit bigger for each dangerous character.
|
||||||
|
let mut result = String::with_capacity(value.len());
|
||||||
|
let bytes = value.as_bytes();
|
||||||
|
let mut index = 0;
|
||||||
|
let mut start = 0;
|
||||||
|
|
||||||
|
while index < bytes.len() {
|
||||||
|
let byte = bytes[index];
|
||||||
|
if matches!(byte, b'\0') || (encode_html && matches!(byte, b'&' | b'"' | b'<' | b'>')) {
|
||||||
|
result.push_str(&value[start..index]);
|
||||||
|
result.push_str(match byte {
|
||||||
|
b'\0' => "<EFBFBD>",
|
||||||
|
b'&' => "&",
|
||||||
|
b'"' => """,
|
||||||
|
b'<' => "<",
|
||||||
|
// `b'>'`
|
||||||
|
_ => ">",
|
||||||
|
});
|
||||||
|
|
||||||
|
start = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str(&value[start..]);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(res: ToHtmlResult) -> String {
|
||||||
|
if res.svelte {
|
||||||
|
res.html
|
||||||
|
} else {
|
||||||
|
svelte_html_encode(res.html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Title {
|
||||||
|
level: u8,
|
||||||
|
text: String,
|
||||||
|
id: String,
|
||||||
|
pos: Option<Position>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
child: Child,
|
||||||
|
bufread: BufReader<ChildStdout>,
|
||||||
|
yaml: Option<serde_json::Map<String, Value>>,
|
||||||
|
default_lang: Option<String>,
|
||||||
|
script: Option<String>,
|
||||||
|
args: Args,
|
||||||
|
titles: Vec<Title>,
|
||||||
|
|
||||||
|
highlight_times: Duration,
|
||||||
|
js_times: Duration,
|
||||||
|
js_sum: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct HighlightRequest {
|
||||||
|
code: String,
|
||||||
|
inline: bool,
|
||||||
|
lang: String,
|
||||||
|
meta: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct HighlightResponse {
|
||||||
|
html: String,
|
||||||
|
|
||||||
|
elapsed: f64,
|
||||||
|
sum: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
fn new(args: Args) -> Self {
|
||||||
|
let mut child = Command::new("node")
|
||||||
|
.arg(".")
|
||||||
|
.current_dir("highlighter")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn().unwrap();
|
||||||
|
let stdout = child.stdout.take().unwrap();
|
||||||
|
let bufread = BufReader::new(stdout);
|
||||||
|
|
||||||
|
Context { child, bufread, yaml: None, titles: Vec::new(), default_lang: None, script: None, args, highlight_times: Duration::ZERO, js_times: Duration::ZERO, js_sum: 0. }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight(&mut self, code: HighlightRequest) -> String {
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let stdin = self.child.stdin.as_mut().unwrap();
|
||||||
|
let data = serde_json::to_string(&code).unwrap() + "\n";
|
||||||
|
stdin.write_all(data.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
let _line = self.bufread.read_line(&mut buf).unwrap();
|
||||||
|
let res: HighlightResponse = serde_json::from_str(&buf).unwrap();
|
||||||
|
|
||||||
|
self.highlight_times += start.elapsed();
|
||||||
|
self.js_times += Duration::from_nanos((res.elapsed * 1_000_000.) as u64);
|
||||||
|
self.js_sum = res.sum;
|
||||||
|
res.html
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_layout(&self) -> &str {
|
||||||
|
&self.args.layout
|
||||||
|
// Path::new(&self.args.layout).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
struct Args {
|
||||||
|
#[arg(short, long)]
|
||||||
|
layout: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
path: String,
|
||||||
|
#[arg(long)]
|
||||||
|
timings: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts markdown to svelte code, MDSvex alternative. Expects trusted code!
|
||||||
|
fn main() {
|
||||||
|
let options = markdown::ParseOptions {
|
||||||
|
constructs: Constructs {
|
||||||
|
attention: true,
|
||||||
|
autolink: true,
|
||||||
|
block_quote: true,
|
||||||
|
character_escape: true,
|
||||||
|
character_reference: true,
|
||||||
|
code_indented: true,
|
||||||
|
code_fenced: true,
|
||||||
|
code_text: true,
|
||||||
|
definition: true,
|
||||||
|
frontmatter: true,
|
||||||
|
gfm_autolink_literal: true,
|
||||||
|
gfm_footnote_definition: true,
|
||||||
|
gfm_label_start_footnote: true,
|
||||||
|
gfm_strikethrough: true,
|
||||||
|
gfm_table: true,
|
||||||
|
gfm_task_list_item: true,
|
||||||
|
hard_break_escape: true,
|
||||||
|
hard_break_trailing: true,
|
||||||
|
heading_atx: true,
|
||||||
|
heading_setext: true,
|
||||||
|
html_flow: true,
|
||||||
|
html_text: true,
|
||||||
|
label_start_image: true,
|
||||||
|
label_start_link: true,
|
||||||
|
label_end: true,
|
||||||
|
list_item: true,
|
||||||
|
math_flow: true,
|
||||||
|
math_text: true,
|
||||||
|
mdx_esm: false,
|
||||||
|
mdx_expression_flow: false,
|
||||||
|
mdx_expression_text: false,
|
||||||
|
mdx_jsx_flow: false,
|
||||||
|
mdx_jsx_text: false,
|
||||||
|
thematic_break: true,
|
||||||
|
},
|
||||||
|
math_text_single_dollar: true,
|
||||||
|
gfm_strikethrough_single_tilde: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let args = Args::parse();
|
||||||
|
let mut ctx = Context::new(args);
|
||||||
|
let mut input = String::new();
|
||||||
|
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
stdin().read_to_string(&mut input).unwrap();
|
||||||
|
let stdin_read = start.elapsed();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let ast = markdown::to_mdast(&input, &options).unwrap();
|
||||||
|
let ast_parse = start.elapsed();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
ast.visit(&mut ctx);
|
||||||
|
let ast_visit = start.elapsed();
|
||||||
|
|
||||||
|
if let Some(yaml) = &ctx.yaml {
|
||||||
|
if let Some(val) = yaml.get("defaultLang") {
|
||||||
|
ctx.default_lang = Some(val.as_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let res = ast.to_html(&mut ctx);
|
||||||
|
let html = finish(res);
|
||||||
|
let html_convert = start.elapsed();
|
||||||
|
|
||||||
|
if ctx.args.timings {
|
||||||
|
dbg!(stdin_read, ast_parse, ast_visit, html_convert, ctx.highlight_times, ctx.js_times, ctx.js_sum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(yaml) = &mut ctx.yaml {
|
||||||
|
yaml.insert("titles".to_string(), serde_json::to_value(&ctx.titles).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = ctx.script.clone().unwrap_or_else(|| String::from("<script></script>"));
|
||||||
|
|
||||||
|
let script = {
|
||||||
|
let end = value.find('>').expect("Unclosed script tag (found <script but not >). May be a bug with Markdown parser.") + 1;
|
||||||
|
let mut script = value[..end].to_string();
|
||||||
|
let layout = ctx.resolve_layout();
|
||||||
|
script += format!("import MDXLayout from \"{}\";", layout).as_str();
|
||||||
|
script += &value[end..];
|
||||||
|
script
|
||||||
|
};
|
||||||
|
|
||||||
|
let frontmatter = (|| {
|
||||||
|
serde_json::to_string(&ctx.yaml?).ok()
|
||||||
|
})().unwrap_or("{}".to_string());
|
||||||
|
println!("<script context=\"module\">export const metadata = {}</script>", frontmatter);
|
||||||
|
|
||||||
|
println!("{script}");
|
||||||
|
println!("<MDXLayout {{...metadata}}>");
|
||||||
|
println!("{}", html);
|
||||||
|
println!("</MDXLayout>");
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue