Skip to content

Commit

Permalink
Merge pull request #67 from zw963/master
Browse files Browse the repository at this point in the history
Use tartrazine render code block
  • Loading branch information
nobodywasishere authored Jan 14, 2025
2 parents 386cf98 + 3c85a25 commit 588b172
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 30 deletions.
127 changes: 127 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,133 @@ renderer = CustomRenderer.new(options)
html = renderer.render(document)
```

## Use tartrazine shards to render code block.

Added and require [tartrazine](https://github.com/ralsina/tartrazine) before markd will use it to render code block.

By default, it use formatter like following:

```crystal
formatter = Tartrazine::Html.new(
theme: Tartrazine.theme("catppuccin-macchiato"),
line_numbers: true,
standalone: true,
)
```

You can passing a formatter instead.

e.g.

```crystal
require "tartrazine" # require it before markd
require "markd"
formatter = Tartrazine::Html.new(
theme: Tartrazine.theme("emacs"),
# Disable print line number
line_numbers: false,
# Set standalone to false for better performace.
#
# You need generate css file use `bin/tartrazine -f html -t "emacs" --css`,
# then link it in you site.
standalone: false,
)
html = Markd.to_html(markdown,formatter: formatter)
```

If you don't care about the formatter config, you can just passing a string instead.

```crystal
require "tartrazine" # require it before markd
require "markd"
html = Markd.to_html(markdown, formatter: "emacs")
```


Currently Tartrazine supports 247 languages and [331 themes](https://github.com/ralsina/tartrazine/tree/main/styles), you can retrieve the supported languages use `Tartrazine::LEXERS_BY_NAME.values.uniq.sort`, for now the result is:

```crystal
[
"LiquidLexer", "VelocityLexer",
"abap", "abnf", "actionscript", "actionscript_3", "ada", "agda", "al", "alloy", "angular2",
"antlr", "apacheconf", "apl", "applescript", "arangodb_aql", "arduino", "armasm",
"autohotkey", "autoit", "awk",
"ballerina", "bash", "bash_session", "batchfile", "bbcode", "bibtex", "bicep", "blitzbasic",
"bnf", "bqn", "brainfuck",
"c", "c#", "c++", "cap_n_proto", "cassandra_cql", "ceylon", "cfengine3", "cfstatement",
"chaiscript", "chapel", "cheetah", "clojure", "cmake", "cobol", "coffeescript",
"common_lisp", "coq", "crystal", "css", "cue", "cython",
"d", "dart", "dax", "desktop_entry", "diff", "django_jinja", "dns", "docker", "dtd", "dylan",
"ebnf", "elixir", "elm", "emacslisp", "erlang",
"factor", "fennel", "fish", "forth", "fortran", "fortranfixed", "fsharp",
"gas", "gdscript", "gdscript3", "gherkin", "gleam", "glsl", "gnuplot", "go_template",
"graphql", "groff", "groovy",
"handlebars", "hare", "haskell", "hcl", "hexdump", "hlb", "hlsl", "holyc", "html", "hy",
"idris", "igor", "ini", "io", "iscdhcpd",
"j", "java", "javascript", "json", "jsonata", "julia", "jungle",
"kotlin",
"lighttpd_configuration_file", "llvm", "lua",
"makefile", "mako", "markdown", "mason", "materialize_sql_dialect", "mathematica", "matlab",
"mcfunction", "meson", "metal", "minizinc", "mlir", "modula-2", "moinwiki", "monkeyc",
"morrowindscript", "myghty", "mysql",
"nasm", "natural", "ndisasm", "newspeak", "nginx_configuration_file", "nim", "nix",
"objective-c", "objectpascal", "ocaml", "octave", "odin", "onesenterprise", "openedge_abl",
"openscad", "org_mode",
"pacmanconf", "perl", "php", "pig", "pkgconfig", "pl_pgsql", "plaintext", "plutus_core",
"pony", "postgresql_sql_dialect", "postscript", "povray", "powerquery", "powershell",
"prolog", "promela", "promql", "properties", "protocol_buffer", "prql", "psl", "puppet",
"python", "python_2",
"qbasic", "qml",
"r", "racket", "ragel", "react", "reasonml", "reg", "rego", "rexx", "rpm_spec", "rst",
"ruby", "rust",
"sas", "sass", "scala", "scheme", "scilab", "scss", "sed", "sieve", "smali", "smalltalk",
"smarty", "snobol", "solidity", "sourcepawn", "sparql", "sql", "squidconf", "standard_ml",
"stas", "stylus", "swift", "systemd", "systemverilog",
"tablegen", "tal", "tasm", "tcl", "tcsh", "termcap", "terminfo", "terraform", "tex",
"thrift", "toml", "tradingview", "transact-sql", "turing", "turtle", "twig", "typescript",
"typoscript", "typoscriptcssdata", "typoscripthtmldata",
"ucode",
"v", "v_shell", "vala", "vb_net", "verilog", "vhdl", "vhs", "viml", "vue", "wdte",
"webgpu_shading_language", "whiley",
"xml", "xorg",
"yaml", "yang", "z80_assembly",
"zed", "zig"
]
```

For details usage, check [tartrazine](https://github.com/ralsina/tartrazine) documents.

## Performance

Here is the result of [a sample markdown file](benchmarks/source.md) parse at MacBook Pro Retina 2015 (2.2 GHz):
Expand Down
39 changes: 33 additions & 6 deletions src/markd.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,38 @@ require "./markd/parser"
require "./markd/version"

module Markd
def self.to_html(source : String, options = Options.new)
return "" if source.empty?
{% if @top_level.has_constant?("Tartrazine") %}
def self.to_html(
source : String,
options = Options.new,
*,
formatter : Tartrazine::Formatter | String = "catppuccin-macchiato",
)
return "" if source.empty?

document = Parser.parse(source, options)
renderer = HTMLRenderer.new(options)
renderer.render(document)
end
if formatter.is_a?(String)
formatter = Tartrazine::Html.new(
theme: Tartrazine.theme(formatter),
line_numbers: true,
standalone: true,
)
end

document = Parser.parse(source, options)
renderer = HTMLRenderer.new(options)
renderer.render(document, formatter)
end
{% else %}
def self.to_html(
source : String,
options = Options.new,
formatter = nil,
)
return "" if source.empty?

document = Parser.parse(source, options)
renderer = HTMLRenderer.new(options)
renderer.render(document, formatter)
end
{% end %}
end
4 changes: 2 additions & 2 deletions src/markd/renderer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module Markd
false
end

def render(document : Node)
def render(document : Node, formatter : T?) forall T
Utils.timer("rendering", @options.time) do
walker = document.walker
while event = walker.next
Expand All @@ -67,7 +67,7 @@ module Markd
when Node::Type::ThematicBreak
thematic_break(node, entering)
when Node::Type::CodeBlock
code_block(node, entering)
code_block(node, entering, formatter)
when Node::Type::Code
code(node, entering)
when Node::Type::HTMLBlock
Expand Down
80 changes: 58 additions & 22 deletions src/markd/renderers/html_renderer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,12 @@ module Markd
output(node.text)
end

def code_block(node : Node, entering : Bool)
languages = node.fence_language ? node.fence_language.split : nil
code_tag_attrs = attrs(node)
pre_tag_attrs = if @options.prettyprint?
{"class" => "prettyprint"}
else
nil
end

lang = code_block_language(languages)
if lang
code_tag_attrs ||= {} of String => String
code_tag_attrs["class"] = "language-#{escape(lang)}"
end

newline
tag("pre", pre_tag_attrs) do
tag("code", code_tag_attrs) do
code_block_body(node, lang)
end
end
newline
def code_block(node : Node, entering : Bool, formatter : T?) forall T
{% if @top_level.has_constant?("Tartrazine") %}
render_code_block_use_tartrazine(node, formatter)
{% else %}
render_code_block_use_code_tag(node)
{% end %}
end

def code_block_language(languages)
Expand Down Expand Up @@ -289,5 +273,57 @@ module Markd
nil
end
end

private def render_code_block_use_tartrazine(node : Node, formatter : Tartrazine::Formatter?)
languages = node.fence_language ? node.fence_language.split : nil
lang = code_block_language(languages)

newline

if lang
lexer = Tartrazine.lexer(lang)

literal(formatter.format(node.text.chomp, lexer))
else
code_tag_attrs = attrs(node)
pre_tag_attrs = if @options.prettyprint?
{"class" => "prettyprint"}
else
nil
end

tag("pre", pre_tag_attrs) do
tag("code", code_tag_attrs) do
code_block_body(node, lang)
end
end
end

newline
end

private def render_code_block_use_code_tag(node : Node)
languages = node.fence_language ? node.fence_language.split : nil
code_tag_attrs = attrs(node)
pre_tag_attrs = if @options.prettyprint?
{"class" => "prettyprint"}
else
nil
end

lang = code_block_language(languages)
if lang
code_tag_attrs ||= {} of String => String
code_tag_attrs["class"] = "language-#{escape(lang)}"
end

newline
tag("pre", pre_tag_attrs) do
tag("code", code_tag_attrs) do
code_block_body(node, lang)
end
end
newline
end
end
end

0 comments on commit 588b172

Please sign in to comment.