Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2668501
better syntax highlighting for elixir code
tmbb May 19, 2018
666e306
update minimum elixir version so that it supports nimble_parsec
tmbb May 19, 2018
b471576
fixed deps
tmbb May 19, 2018
bf002b1
use makeup on the generated HTML
tmbb May 20, 2018
3a1d0f3
drop support for elixir 1.3
tmbb May 20, 2018
5fd6086
fixed 3 of Ebert's issues; refuse to fix the TODO
tmbb May 20, 2018
e09036a
fix highlighting of non-elixir languages
tmbb May 20, 2018
46354a8
remove ExDocMakeup from the list of MD implementations
tmbb May 21, 2018
4e90a52
make pretty_codeblocks unnecessary
tmbb May 21, 2018
ac9d8bc
remove unused function
tmbb May 21, 2018
f91a1fd
move makeup-specific code to the highlighter module
tmbb May 21, 2018
068ffc6
rename assets
tmbb May 21, 2018
ca54b80
Fix tests
tmbb May 22, 2018
123dfc0
remove debug log
tmbb May 22, 2018
08d475a
move makeup assets to the js and css bundles
tmbb May 23, 2018
d42b7e8
make gulp lint happy
tmbb May 23, 2018
d0ca3c8
remove explicit dependency on makeup (it will be pulled by makeup_eli…
tmbb May 23, 2018
f30fc76
add faster decoder (about 2x as fast)
tmbb May 26, 2018
c34f5ba
make our dear friend ebert happy again
tmbb May 26, 2018
c029cba
fix spelling in comment
tmbb May 27, 2018
5538985
use nested css classes
tmbb Jun 4, 2018
e7e92ec
improve redability of iex errors in night mode
tmbb Jun 4, 2018
9523419
fixed bug (typo) in the HTML decoder and added tests
tmbb Jun 5, 2018
14da15c
Merge branch 'master' into ex_doc_makeup
josevalim Jul 10, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import hljs from 'highlight.js/build/highlight.pack'
import {initialize as initEvents} from './events'
import {initialize as initSidebar} from './sidebar'
import {initialize as initNightMode} from './night'
import {initialize as initMakeup} from './makeup'

window.$ = $

Expand All @@ -20,5 +21,6 @@ $(() => {
initNightMode()
initSidebar()
initEvents()
initMakeup()
hljs.initHighlighting()
})
2 changes: 2 additions & 0 deletions assets/js/epub.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
// ------------

import hljs from 'highlight.js/build/highlight.pack'
import {initialize as initMakeup} from './makeup'

initMakeup()
hljs.initHighlightingOnLoad()
25 changes: 25 additions & 0 deletions assets/js/makeup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var HIGHLIGHT_CLASS = 'hll'
function onMouseEnter (evt) {
var groupId = evt.target.getAttribute('data-group-id')
var siblings = document.querySelectorAll('[data-group-id=\'' + groupId + '\']')
for (var i = 0; i < siblings.length; ++i) {
siblings[i].classList.add(HIGHLIGHT_CLASS)
}
}

function onMouseLeave (evt) {
var groupId = evt.target.getAttribute('data-group-id')
var siblings = document.querySelectorAll('[data-group-id=\'' + groupId + '\']')
for (var i = 0; i < siblings.length; ++i) {
siblings[i].classList.remove(HIGHLIGHT_CLASS)
}
}

export function initialize () {
var delims = document.querySelectorAll('[data-group-id]')
for (var i = 0; i < delims.length; i++) {
var elem = delims[i]
elem.addEventListener('mouseenter', onMouseEnter)
elem.addEventListener('mouseleave', onMouseLeave)
}
}
1 change: 1 addition & 0 deletions assets/less/app.less
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@

@import './screen-reader';
@import './print';
@import './makeup';
1 change: 1 addition & 0 deletions assets/less/epub.less
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@import 'npm://highlight.js/src/styles/tomorrow';

@import './variables';
@import './makeup';

body {
display: block;
Expand Down
188 changes: 188 additions & 0 deletions assets/less/makeup.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
code.makeup {
display: block;
overflow-x: auto;
padding-top: 0.3em !important;
padding-bottom: 0.3em !important;
padding-right: 0.5em !important;
padding-left: 0.5em !important;
color: white;
white-space: pre;
}

.makeup {
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

.hll {background-color: #ffffcc}
.bp {color: #3465a4; } /* :name_builtin_pseudo */
.c {color: #8E908C; } /* :comment */
.c1 {color: #8E908C; } /* :comment_single */
.ch {color: #8E908C; } /* :comment_hashbang */
.cm {color: #8E908C; } /* :comment_multiline */
.cp {color: #8E908C; } /* :comment_preproc */
.cpf {color: #8E908C; } /* :comment_preproc_file */
.cs {color: #8E908C; font-style: italic; } /* :comment_special */
.dl {color: #4e9a06; } /* :string_delimiter */
.err {color: #a40000; border: #ef2929; } /* :error */
.fm {color: #000000; } /* :name_function_magic */
.g {color: #000000; } /* :generic */
.gd {color: #a40000; } /* :generic_deleted */
.ge {color: #000000; font-style: italic; } /* :generic_emph */
.gh {color: #000080; font-weight: bold; } /* :generic_heading */
.gi {color: #00A000; } /* :generic_inserted */
.go {color: #000000; font-style: italic; } /* :generic_output */
.gp {color: #8f5902; } /* :generic_prompt */
.gr {color: #ef2929; } /* :generic_error */
.gs {color: #000000; font-weight: bold; } /* :generic_strong */
.gt {color: #a40000; font-weight: bold; } /* :generic_traceback */
.gu {color: #800080; font-weight: bold; } /* :generic_subheading */
.il {color: #0000cf; font-weight: bold; } /* :number_integer_long */
.k {color: #204a87; font-weight: bold; } /* :keyword */
.kc {color: #204a87; font-weight: bold; } /* :keyword_constant */
.kd {color: #204a87; font-weight: bold; } /* :keyword_declaration */
.kn {color: #204a87; font-weight: bold; } /* :keyword_namespace */
.kp {color: #204a87; font-weight: bold; } /* :keyword_pseudo */
.kr {color: #204a87; font-weight: bold; } /* :keyword_reserved */
.kt {color: #204a87; font-weight: bold; } /* :keyword_type */
.l {color: #000000; } /* :literal */
.ld {color: #8f5902; } /* :literal_date */
.m {color: #0000cf; font-weight: bold; } /* :number */
.mb {color: #0000cf; font-weight: bold; } /* :number_bin */
.mf {color: #0000cf; font-weight: bold; } /* :number_float */
.mh {color: #0000cf; font-weight: bold; } /* :number_hex */
.mi {color: #0000cf; font-weight: bold; } /* :number_integer */
.mo {color: #0000cf; font-weight: bold; } /* :number_oct */
.n {color: #000000; } /* :name */
.na {color: #c4a000; } /* :name_attribute */
.nb {color: #204a87; } /* :name_builtin */
.nc {color: #5c35cc; } /* :name_class */
.nd {color: #5c35cc; font-weight: bold; } /* :name_decorator */
.ne {color: #cc0000; font-weight: bold; } /* :name_exception */
.nf {color: #ce5c00; } /* :name_function */
.ni {color: #ce5c00; } /* :name_entity */
.nl {color: #f57900; } /* :name_label */
.nn {color: #000000; } /* :name_namespace */
.no {color: #0000cf; } /* :name_constant */
.nt {color: #204a87; font-weight: bold; } /* :name_tag */
.nv {color: #000000; } /* :name_variable */
.nx {color: #000000; } /* :name_other */
.o {color: #ce5c00; font-weight: bold; } /* :operator */
.ow {color: #204a87; font-weight: bold; } /* :operator_word */
.p {color: #000000; } /* :punctuation */
.py {color: #000000; } /* :name_property */
.s {color: #4e9a06; } /* :string */
.s1 {color: #c4a000 } /* :string_single */
.s2 {color: #4e9a06; } /* :string_double */
.sa {color: #4e9a06; } /* :string_affix */
.sb {color: #4e9a06; } /* :string_backtick */
.sc {color: #4e9a06; } /* :string_char */
.sd {color: #8f5902; font-style: italic; } /* :string_doc */
.se {color: #204a87; } /* :string_escape */
.sh {color: #4e9a06; } /* :string_heredoc */
.si {color: #5c35cc; } /* :string_interpol */
.sr {color: #cc0000; } /* :string_regex */
.ss {color: #f57900; } /* :string_symbol */
.sx {color: #4e9a06; } /* :string_other */
.sx {color: #f56900; } /* :string_sigil */
.vc {color: #000000; } /* :name_variable_class */
.vg {color: #000000; } /* :name_variable_global */
.vi {color: #000000; } /* :name_variable_instance */
.vm {color: #000000; } /* :name_variable_magic */
.x {color: #000000; } /* :other */
}

.night-mode code.makeup {
display: block;
overflow-x: auto;
padding-top: 0.3em !important;
padding-bottom: 0.3em !important;
padding-right: 0.5em !important;
padding-left: 0.5em !important;
white-space: pre;
}

.night-mode .makeup {
color: #e7e9db;
background-color: #2f1e2e;
}

.night-mode .makeup {
.hll {background-color: #4f424c}
.bp {color: #e7e9db; } /* :name_builtin_pseudo */
.c {color: #776e71; } /* :comment */
.c1 {color: #776e71; } /* :comment_single */
.ch {color: #776e71; } /* :comment_hashbang */
.cm {color: #776e71; } /* :comment_multiline */
.cp {color: #776e71; } /* :comment_preproc */
.cpf {color: #776e71; } /* :comment_preproc_file */
.cs {color: #776e71; } /* :comment_special */
.dl {color: #48b685; } /* :string_delimiter */
.err {color: #ef6155; } /* :error */
.fm {color: #06b6ef; } /* :name_function_magic */
.gd {color: #ef6155; } /* :generic_deleted */
.ge {font-style: italic; } /* :generic_emph */
.gh {color: #e7e9db; font-weight: bold; } /* :generic_heading */
.gi {color: #48b685; } /* :generic_inserted */
.gp {color: #776e71; font-weight: bold; } /* :generic_prompt */
.gs {font-weight: bold; } /* :generic_strong */
.gu {color: #5bc4bf; font-weight: bold; } /* :generic_subheading */
.gt {color: #ef6155; font-weight: bold; }
.il {color: #f99b15; } /* :number_integer_long */
.k {color: #815ba4; font-weight: normal } /* :keyword */
.kc {color: #815ba4; font-weight: normal } /* :keyword_constant */
.kd {color: #815ba4; font-weight: normal } /* :keyword_declaration */
.kn {color: #5bc4bf; font-weight: normal } /* :keyword_namespace */
.kp {color: #815ba4; font-weight: normal } /* :keyword_pseudo */
.kr {color: #815ba4; font-weight: normal } /* :keyword_reserved */
.kt {color: #fec418; font-weight: normal } /* :keyword_type */
.l {color: #f99b15; } /* :literal */
.ld {color: #776e71; } /* :literal_date */
.m {color: #f99b15; } /* :number */
.mb {color: #f99b15; font-weight: normal } /* :number_bin */
.mf {color: #f99b15; font-weight: normal } /* :number_float */
.mh {color: #f99b15; font-weight: normal } /* :number_hex */
.mi {color: #f99b15; font-weight: normal } /* :number_integer */
.mo {color: #f99b15; font-weight: normal } /* :number_oct */
.n {color: #e7e9db; } /* :name */
.na {color: #06b6ef; } /* :name_attribute */
.nb {color: #e7e9db; } /* :name_builtin */
.nc {color: #fec418; } /* :name_class */
.nd {color: #5bc4bf; } /* :name_decorator */
.ne {color: #ef6155; } /* :name_exception */
.nf {color: #06b6ef; } /* :name_function */
.ni {color: #ef6155; } /* :name_entity */
.nl {color: #e7e9db; } /* :name_label */
.nn {color: #fec418; } /* :name_namespace */
.no {color: #ef6155; } /* :name_constant */
.nt {color: #5bc4bf; } /* :name_tag */
.nv {color: #ef6155; } /* :name_variable */
.nx {color: #06b6ef; } /* :name_other */
.o {color: #5bc4bf; font-weight: normal } /* :operator */
.ow {color: #5bc4bf; font-weight: normal } /* :operator_word */
.p {color: #e7e9db; } /* :punctuation */
.py {color: #e7e9db; } /* :name_property */
.s {color: #48b685; } /* :string */
.s1 {color: #48b685; } /* :string_single */
.s2 {color: #48b685; } /* :string_double */
.sa {color: #48b685; } /* :string_affix */
.sb {color: #48b685; } /* :string_backtick */
.sc {color: #e7e9db; } /* :string_char */
.sd {color: #776e71; } /* :string_doc */
.se {color: #f99b15; } /* :string_escape */
.sh {color: #48b685; } /* :string_heredoc */
.si {color: #f99b15; } /* :string_interpol */
.sr {color: #48b685; } /* :string_regex */
.ss {color: #48b685; } /* :string_symbol */
.sx {color: #48b685; } /* :string_other */
.sx {color: #48b685; } /* :string_sigil */
.vc {color: #ef6155; } /* :name_variable_class */
.vg {color: #ef6155; } /* :name_variable_global */
.vi {color: #ef6155; } /* :name_variable_instance */
.vm {color: #ef6155; } /* :name_variable_magic */
}
2 changes: 2 additions & 0 deletions formatters/epub/dist/app-12c41c88e5.js

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions formatters/epub/dist/app-6d5e229d3d.js

This file was deleted.

1 change: 0 additions & 1 deletion formatters/epub/dist/epub-4de2ec3725.css

This file was deleted.

1 change: 1 addition & 0 deletions formatters/epub/dist/epub-ae70736789.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions formatters/html/dist/app-01f6330ade.css

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions formatters/html/dist/app-0684e614ed.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions formatters/html/dist/app-0684e614ed.js.map

Large diffs are not rendered by default.

8 changes: 0 additions & 8 deletions formatters/html/dist/app-b2d0635bae.js

This file was deleted.

1 change: 0 additions & 1 deletion formatters/html/dist/app-b2d0635bae.js.map

This file was deleted.

1 change: 0 additions & 1 deletion formatters/html/dist/app-f81cbf3cc2.css

This file was deleted.

37 changes: 37 additions & 0 deletions lib/ex_doc/highlighter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule ExDoc.Highlighter do
@moduledoc false
alias ExDoc.Highlighter.HtmlDecoder

# Assets are included in the bundles and don't need to be declared here.
def assets(_), do: []

def before_closing_head_tag(_), do: ""

def before_closing_body_tag(_), do: ""

# If new lexers are available, add them here:
defp pick_language_and_lexer(""), do: {"elixir", Makeup.Lexers.ElixirLexer}
defp pick_language_and_lexer("elixir"), do: {"elixir", Makeup.Lexers.ElixirLexer}
defp pick_language_and_lexer(other), do: {other, nil}

# Public API for the module.
# Highlights all code block in an already generated HTML document.
def highlight_code_blocks(html) do
Regex.replace(~r/<pre><code(?:\s+class="(\w*)")?>([^<]*)<\/code><\/pre>/, html, &highlight_code_block/3)
end

defp highlight_code_block(full_block, lang, code) do
case pick_language_and_lexer(lang) do
{_language, nil} -> full_block
{language, lexer} -> render_code(language, lexer, code)
end
end

defp render_code(lang, lexer, code) do
highlighted =
code
|> HtmlDecoder.unescape_html_entities()
|> Makeup.highlight_inner_html(lexer: lexer)
~s(<pre><code class="nohighlight makeup #{lang}">#{highlighted}</code></pre>)
end
end
24 changes: 24 additions & 0 deletions lib/ex_doc/highlighter/html_decoder.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule ExDoc.Highlighter.HtmlDecoder do
@moduledoc false
entities = [{"&amp;", ?&}, {"&lt;", ?<}, {"&gt;", ?>}, {"&quot;", ?"}, {"&#39;", ?'}]

for {encoded, decoded} <- entities do
defp to_iodata(unquote(encoded) <> rest) do
[unquote(decoded) | to_iodata(rest)]
end
end

defp to_iodata(<< c, rest :: binary >>) do
[c | to_iodata(rest)]
end

defp to_iodata(<<>>) do
[]
end

def unescape_html_entities(html) do
html
|> to_iodata()
|> IO.iodata_to_binary
end
end
Loading