Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Markdown handling #881

Merged
merged 11 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 13 additions & 17 deletions examples/reference/panes/Markdown.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"\n",
"For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n",
"\n",
"* **``dedent``** (bool): Whether to dedent common whitespace across all lines.\n",
"* **``extensions``** (list): A list of [Python-Markdown extensions](https://python-markdown.github.io/extensions/) to use.\n",
"* **``object``** (str or object): A string containing Markdown, or an object with a ``_repr_markdown_`` method\n",
"* **``style``** (dict): Dictionary specifying CSS styles\n",
"\n",
Expand Down Expand Up @@ -49,25 +51,13 @@
"##### H5\n",
"###### H6\n",
"\n",
"<br>\n",
"\n",
"### Emphasis\n",
"\n",
"Emphasis, aka italics, with *asterisks* or _underscores_.\n",
"Emphasis, aka italics, with **asterisks** or _underscores_.\n",
"\n",
"Strong emphasis, aka bold, with **asterisks** or __underscores__.\n",
"\n",
"Combined emphasis with **asterisks and _underscores_**.\n",
"\n",
"Strikethrough uses two tildes. ~~Scratch this.~~\n",
"\n",
"<br>\n",
"\n",
"### Task list\n",
"\n",
"- [x] Write the press release\n",
"- [ ] Update the website\n",
"- [ ] Contact the media\n",
"Combined emphasis with ** asterisks and _underscores_ **.\n",
"\n",
"<br>\n",
"\n",
Expand All @@ -90,10 +80,16 @@
"}\n",
"```\n",
"\n",
"### Nested list\n",
"\n",
"1. First list item\n",
" - First nested list item\n",
" - Second nested list item\n",
"\n",
"[This is a link to panel web portal](https://panel.pyviz.org/)\n",
"\"\"\")\n",
"\n",
"\n"
"------------\n",
"\"\"\", width=500)"
]
},
{
Expand Down Expand Up @@ -203,5 +199,5 @@
}
},
"nbformat": 4,
"nbformat_minor": 2
"nbformat_minor": 4
}
81 changes: 81 additions & 0 deletions panel/_styles/markdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
.codehilite .hll { background-color: #ffffcc }
.codehilite { background: #f8f8f8; }
.codehilite .c { color: #408080; font-style: italic } /* Comment */
.codehilite .err { border: 1px solid #FF0000 } /* Error */
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
.codehilite .o { color: #666666 } /* Operator */
.codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
.codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
.codehilite .ge { font-style: italic } /* Generic.Emph */
.codehilite .gr { color: #FF0000 } /* Generic.Error */
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
.codehilite .go { color: #888888 } /* Generic.Output */
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.codehilite .gs { font-weight: bold } /* Generic.Strong */
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.codehilite .gt { color: #0044DD } /* Generic.Traceback */
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.codehilite .kt { color: #B00040 } /* Keyword.Type */
.codehilite .m { color: #666666 } /* Literal.Number */
.codehilite .s { color: #BA2121 } /* Literal.String */
.codehilite .na { color: #7D9029 } /* Name.Attribute */
.codehilite .nb { color: #008000 } /* Name.Builtin */
.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.codehilite .no { color: #880000 } /* Name.Constant */
.codehilite .nd { color: #AA22FF } /* Name.Decorator */
.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.codehilite .nf { color: #0000FF } /* Name.Function */
.codehilite .nl { color: #A0A000 } /* Name.Label */
.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
.codehilite .nv { color: #19177C } /* Name.Variable */
.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
.codehilite .mb { color: #666666 } /* Literal.Number.Bin */
.codehilite .mf { color: #666666 } /* Literal.Number.Float */
.codehilite .mh { color: #666666 } /* Literal.Number.Hex */
.codehilite .mi { color: #666666 } /* Literal.Number.Integer */
.codehilite .mo { color: #666666 } /* Literal.Number.Oct */
.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.codehilite .sx { color: #008000 } /* Literal.String.Other */
.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
.codehilite .fm { color: #0000FF } /* Name.Function.Magic */
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
.codehilite .vm { color: #19177C } /* Name.Variable.Magic */
.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */

.markdown h1 { margin-block-start: 0.34em }
.markdown h2 { margin-block-start: 0.42em }
.markdown h3 { margin-block-start: 0.5em }
.markdown h4 { margin-block-start: 0.67em }
.markdown h5 { margin-block-start: 0.84em }
.markdown h6 { margin-block-start: 1.17em }
.markdown ul { padding-inline-start: 2em }
.markdown ol { padding-inline-start: 2em }
.markdown strong { font-weight: 600 }
.markdown a { color: -webkit-link }
.markdown a { color: -moz-hyperlinkText }
20 changes: 17 additions & 3 deletions panel/pane/markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"""
from __future__ import absolute_import, division, unicode_literals

import textwrap

try:
from html import escape
except:
Expand Down Expand Up @@ -263,9 +265,18 @@ class Markdown(DivPaneBase):
standard string, therefore it has to be invoked explicitly.
"""

dedent = param.Boolean(default=True, doc="""
Whether to dedent common whitespace across all lines.""")

extensions = param.List(default=[
"extra", "smarty", "codehilite"], doc="""
Markdown extension to apply when transforming markup.""")

# Priority depends on the data type
priority = None

_rerender_params = ['object', 'dedent', 'extensions']

@classmethod
def applies(cls, obj):
if hasattr(obj, '_repr_markdown_'):
Expand All @@ -282,8 +293,11 @@ def _get_properties(self):
data = ''
elif not isinstance(data, string_types):
data = data._repr_markdown_()
if self.dedent:
data = textwrap.dedent(data)
properties = super(Markdown, self)._get_properties()
properties['style'] = properties.get('style', {})
extensions = ['markdown.extensions.extra', 'markdown.extensions.smarty']
html = markdown.markdown(data, extensions=extensions, output_format='html5')
return dict(properties, text=html)
css_classes = properties.pop('css_classes', []) + ['markdown']
html = markdown.markdown(data, extensions=self.extensions,
output_format='html5')
return dict(properties, text=html, css_classes=css_classes)
10 changes: 8 additions & 2 deletions panel/tests/pane/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@ def test_pane_repr(document, comm):

@pytest.mark.parametrize('pane', all_panes)
def test_pane_layout_properties(pane, document, comm):
p = pane()
try:
p = pane()
except ImportError:
pytest.skip("Dependent library could not be imported.")
model = p.get_root(document, comm)
check_layoutable_properties(p, model)


@pytest.mark.parametrize('pane', all_panes)
def test_pane_clone(pane):
p = pane()
try:
p = pane()
except ImportError:
pytest.skip("Dependent library could not be imported.")
clone = p.clone()

assert ([(k, v) for k, v in sorted(p.param.get_param_values()) if k != 'name'] ==
Expand Down
27 changes: 27 additions & 0 deletions panel/tests/pane/test_markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ def test_markdown_pane(document, comm):
pane._cleanup(model)
assert pane._models == {}

def test_markdown_pane_dedent(document, comm):
pane = Pane(" ABC")

# Create pane
model = pane.get_root(document, comm=comm)
assert pane._models[model.ref['id']][0] is model
assert model.text.endswith("<p>ABC</p>")

pane.dedent = False
assert model.text.endswith('<div class="codehilite"><pre><span></span><span class="err">ABC</span>\n</pre></div>')


def test_markdown_pane_extensions(document, comm):
pane = Pane("""
```python
None
```
""")

# Create pane
model = pane.get_root(document, comm=comm)
assert pane._models[model.ref['id']][0] is model
assert model.text.endswith('<div class="codehilite"><pre><span></span><span class="kc">None</span>\n</pre></div>')

pane.extensions = ["extra", "smarty"]
assert model.text.endswith('<pre><code class="python">None\n</code></pre>')


def test_html_pane(document, comm):
pane = HTML("<h1>Test</h1>")
Expand Down
10 changes: 9 additions & 1 deletion panel/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,15 @@ def param_reprs(parameterized, skip=None):
cls = type(parameterized).__name__
param_reprs = []
for p, v in sorted(parameterized.get_param_values()):
if v is parameterized.param[p].default: continue
default = parameterized.param[p].default
equal = v is default
if not equal:
try:
equal = bool(v==default)
except:
equal = False

if equal: continue
elif v is None: continue
elif isinstance(v, string_types) and v == '': continue
elif isinstance(v, list) and v == []: continue
Expand Down