Markdown Rendering Support

This commit is contained in:
Martin Mahner 2018-04-05 21:18:22 +02:00
parent 46604ae492
commit 76e8ddf3ea
11 changed files with 340 additions and 140 deletions

View file

@ -1,12 +1,9 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[dev-packages]
django-sslserver = "*"
sphinx-rtd-theme = "*"
sphinx = "*"

141
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "ea49aed405ba5dd5edf72d9212ffb88163c3816b91eb2940158e2dbae6fe7761"
"sha256": "7aaac7b35a2344e96fa3eaa2642d9e47aa3cb69b6cb26ab8176ba48ee69d9964"
},
"pipfile-spec": 6,
"requires": {},
@ -13,131 +13,7 @@
}
]
},
"default": {
"coverage": {
"hashes": [
"sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba",
"sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed",
"sha256:104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a",
"sha256:15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd",
"sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640",
"sha256:1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2",
"sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162",
"sha256:2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508",
"sha256:337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249",
"sha256:3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694",
"sha256:3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a",
"sha256:3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287",
"sha256:3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1",
"sha256:4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000",
"sha256:56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1",
"sha256:5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e",
"sha256:69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5",
"sha256:6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062",
"sha256:701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba",
"sha256:7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc",
"sha256:76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc",
"sha256:7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99",
"sha256:7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653",
"sha256:7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c",
"sha256:8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558",
"sha256:9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f",
"sha256:9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4",
"sha256:ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91",
"sha256:b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d",
"sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9",
"sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd",
"sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d",
"sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6",
"sha256:e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77",
"sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80",
"sha256:f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e"
],
"version": "==4.5.1"
},
"django": {
"hashes": [
"sha256:2d8b9eed8815f172a8e898678ae4289a5e9176bc08295676eff4228dd638ea61",
"sha256:d81a1652963c81488e709729a80b510394050e312f386037f26b54912a3a10d0"
],
"version": "==2.0.4"
},
"django-csp": {
"hashes": [
"sha256:04c0ccd4e1339e8f6af48c55c3347dc996fde2d22d79e8bf2f6b7a920412e408",
"sha256:096b634430d8ea81c3d9f216f87be890f3a975c17bb9a4631f6a1619ac09c91e"
],
"version": "==3.4"
},
"e1839a8": {
"editable": true,
"extras": [
"standalone"
],
"path": "."
},
"pluggy": {
"hashes": [
"sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff"
],
"version": "==0.6.0"
},
"py": {
"hashes": [
"sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881",
"sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"
],
"version": "==1.5.3"
},
"pygments": {
"hashes": [
"sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
"sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
],
"version": "==2.2.0"
},
"pygments-lexer-solidity": {
"hashes": [
"sha256:89b9e2f7d235c48b2605b93e48660c5d5cffa0b5f62e521465bb0dd1dc6b2a1f"
],
"version": "==0.1.0"
},
"pytz": {
"hashes": [
"sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd",
"sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5",
"sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0",
"sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d",
"sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9",
"sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef",
"sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f",
"sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe",
"sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda"
],
"version": "==2018.3"
},
"six": {
"hashes": [
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
],
"version": "==1.11.0"
},
"tox": {
"hashes": [
"sha256:96efa09710a3daeeb845561ebbe1497641d9cef2ee0aea30db6969058b2bda2f",
"sha256:9ee7de958a43806402a38c0d2aa07fa8553f4d2c20a15b140e9f771c2afeade0"
],
"version": "==3.0.0"
},
"virtualenv": {
"hashes": [
"sha256:1d7e241b431e7afce47e77f8843a276f652699d1fa4f93b9d8ce0076fd7b0b54",
"sha256:e8e05d4714a1c51a2f5921e62f547fcb0f713ebbe959e0a7f585cc8bef71d11f"
],
"version": "==15.2.0"
}
},
"default": {},
"develop": {
"alabaster": {
"hashes": [
@ -222,6 +98,12 @@
],
"version": "==3.4"
},
"django-markup": {
"hashes": [
"sha256:4ef5d268d9376253513f3a7897ac15da48795412b34c8c9c4b9ef0a472ff21b7"
],
"version": "==1.2"
},
"django-sslserver": {
"hashes": [
"sha256:6b2514427b4eed0713e478ae6dfdda622070a98f657d61a4502bf42b6311c66e"
@ -264,6 +146,13 @@
],
"version": "==2.10"
},
"markdown": {
"hashes": [
"sha256:9ba587db9daee7ec761cfc656272be6aabe2ed300fece21208e4aab2e457bc8f",
"sha256:a856869c7ff079ad84a3e19cd87a64998350c2b94e9e08e44270faef33400f81"
],
"version": "==2.6.11"
},
"markupsafe": {
"hashes": [
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"

View file

@ -0,0 +1,71 @@
/*
Generic Content pages and Text based lexer.
*/
article {
padding: 0 $boxPadding 40px $boxPadding;
margin-top: 30px;
font-size: 16px;
font-weight: $baseFontRegular;
line-height: 24px;
max-width: 600px;
color: $textColor;
h1, h2, h3, h4, h5, h6 {
font-weight: $baseFontRegular;
line-height: 1.3em;
padding-bottom: 5px;
border-bottom: 1px solid $borderColor;
}
p {
margin: 30px 0;
}
strong, b {
font-weight: $baseFontDemiBold;
}
table {
margin: 20px 0;
border: 1px solid $borderColor;
border-collapse: collapse;
td, th {
border: 1px solid $borderColor;
padding: 5px 10px;
}
}
hr {
border: 0;
height: 1px;
background-color: darken($borderColor, 10%);
}
pre {
font-family: $codeFont;
font-size: 13px;
font-weight: 300;
background-color: #f8f8f8;
padding: 10px;
}
blockquote {
font-style: italic;
}
@include colored-links;
}
article.content-page {
h2 { margin-top: 0; }
}

View file

@ -88,6 +88,6 @@ $mobileBoxPadding: 20px;
// Components.
@import 'components/header';
@import 'components/form';
@import 'components/text';
@import 'components/code';
@import 'components/article';
@import 'components/history';

217
dpaste/diff.py Normal file
View file

@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
"""
lodgeit.lib.diff
~~~~~~~~~~~~~~~~
Render a nice diff between two things.
:copyright: 2007 by Armin Ronacher.
:license: BSD
https://github.com/openstack-infra/lodgeit/blob/master/lodgeit/lib/diff.py
"""
import re
import time
from html import escape
import six
def prepare_udiff(udiff):
"""Prepare an udiff for a template."""
return DiffRenderer(udiff).prepare()
class DiffRenderer(object):
"""Give it a unified diff and it renders you a beautiful
html diff :-)
"""
_chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
def __init__(self, udiff):
""":param udiff: a text in udiff format"""
self.lines = [escape(line) for line in udiff.splitlines()]
def _extract_rev(self, line1, line2):
def _extract(line):
parts = line.split(None, 1)
return parts[0], (len(parts) == 2 and parts[1] or None)
try:
if line1.startswith('--- ') and line2.startswith('+++ '):
return _extract(line1[4:]), _extract(line2[4:])
except (ValueError, IndexError):
pass
return (None, None), (None, None)
def _highlight_line(self, line, next):
"""Highlight inline changes in both lines."""
start = 0
limit = min(len(line['line']), len(next['line']))
while start < limit and line['line'][start] == next['line'][start]:
start += 1
end = -1
limit -= start
while -end <= limit and line['line'][end] == next['line'][end]:
end -= 1
end += 1
if start or end:
def do(l):
last = end + len(l['line'])
if l['action'] == 'add':
tag = 'ins'
else:
tag = 'del'
l['line'] = u'%s<%s>%s</%s>%s' % (
l['line'][:start],
tag,
l['line'][start:last],
tag,
l['line'][last:]
)
do(line)
do(next)
def _parse_info(self):
"""Look for custom information preceding the diff."""
nlines = len(self.lines)
if not nlines:
return
firstline = self.lines[0]
info = []
# look for Hg export changeset
if firstline.startswith('# HG changeset patch'):
info.append(('Type', 'HG export changeset'))
i = 0
line = firstline
while line.startswith('#'):
if line.startswith('# User'):
info.append(('User', line[7:].strip()))
elif line.startswith('# Date'):
try:
t, tz = map(int, line[7:].split())
info.append(('Date', time.strftime(
'%b %d, %Y %H:%M:%S', time.gmtime(float(t) - tz))))
except Exception:
pass
elif line.startswith('# Branch'):
info.append(('Branch', line[9:].strip()))
i += 1
if i == nlines:
return info
line = self.lines[i]
commitmsg = ''
while not line.startswith('diff'):
commitmsg += line + '\n'
i += 1
if i == nlines:
return info
line = self.lines[i]
info.append(('Commit message', '\n' + commitmsg.strip()))
self.lines = self.lines[i:]
return info
def _parse_udiff(self):
"""Parse the diff an return data for the template."""
info = self._parse_info()
in_header = True
header = []
lineiter = iter(self.lines)
files = []
try:
line = six.next(lineiter)
while 1:
# continue until we found the old file
if not line.startswith('--- '):
if in_header:
header.append(line)
line = six.next(lineiter)
continue
if header and all(x.strip() for x in header):
files.append({'is_header': True, 'lines': header})
header = []
in_header = False
chunks = []
old, new = self._extract_rev(line, six.next(lineiter))
files.append({
'is_header': False,
'old_filename': old[0],
'old_revision': old[1],
'new_filename': new[0],
'new_revision': new[1],
'chunks': chunks
})
line = six.next(lineiter)
while line:
match = self._chunk_re.match(line)
if not match:
in_header = True
break
lines = []
chunks.append(lines)
old_line, old_end, new_line, new_end = \
[int(x or 1) for x in match.groups()]
old_line -= 1
new_line -= 1
old_end += old_line
new_end += new_line
line = six.next(lineiter)
while old_line < old_end or new_line < new_end:
if line:
command, line = line[0], line[1:]
else:
command = ' '
affects_old = affects_new = False
if command == '+':
affects_new = True
action = 'add'
elif command == '-':
affects_old = True
action = 'del'
else:
affects_old = affects_new = True
action = 'unmod'
old_line += affects_old
new_line += affects_new
lines.append({
'old_lineno': affects_old and old_line or u'',
'new_lineno': affects_new and new_line or u'',
'action': action,
'line': line
})
line = six.next(lineiter)
except StopIteration:
pass
# highlight inline changes
for file in files:
if file['is_header']:
continue
for chunk in file['chunks']:
lineiter = iter(chunk)
try:
while True:
line = six.next(lineiter)
if line['action'] != 'unmod':
nextline = six.next(lineiter)
if nextline['action'] == 'unmod' or \
nextline['action'] == line['action']:
continue
self._highlight_line(line, nextline)
except StopIteration:
pass
return files, info
def prepare(self):
return self._parse_udiff()

View file

@ -5,6 +5,7 @@ from logging import getLogger
from django.conf import settings
from django.template.defaultfilters import escape, linebreaksbr
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from pygments import highlight
from pygments.formatters.html import HtmlFormatter
@ -62,6 +63,22 @@ class PlainTextHighlighter(Highlighter):
def highlight(self, code_string, lexer_name=None):
return linebreaksbr(code_string)
class MarkdownHighlighter(PlainTextHighlighter):
"""Markdown"""
def highlight(self, code_string, lexer_name=None):
import misaka
extensions = ('tables', 'fenced-code', 'footnotes', 'autolink,',
'strikethrough', 'underline', 'quote', 'superscript',
'math')
return mark_safe(misaka.html(code_string, extensions=extensions))
# -----------------------------------------------------------------------------
class PlainCodeHighlighter(Highlighter):
"""Plain Code. No highlighting but Pygments like span tags around each line."""
@ -136,9 +153,9 @@ PLAIN_CODE = '_code' # lexer name of code with no hihglighting
TEXT_FORMATTER = [
(PLAIN_TEXT, 'Plain Text', PlainTextHighlighter),
# ('_markdown', 'Markdown'),
# ('_rst', 'reStructuredText'),
# ('_textile', 'Textile'),
('_markdown', 'Markdown', MarkdownHighlighter),
#('_rst', 'reStructuredText', MarkdownHighlighter),
#('_textile', 'Textile', MarkdownHighlighter),
]
CODE_FORMATTER = [

View file

@ -3,7 +3,7 @@
{% block title %}404 Snippet not found{% endblock %}
{% block page %}
<article>
<article class="content-page">
<h2>404 Snippet not found</h2>
<p>

View file

@ -3,7 +3,7 @@
{% block title %}500 Internal Server Error{% endblock %}
{% block page %}
<article>
<article class="content-page">
<h2>500 Internal Server Error</h2>
<p>There was an issue with your request.</p>
</article>

View file

@ -5,7 +5,7 @@
{% block title %}{% trans "About" %}{% endblock %}
{% block page %}
<article>
<article class="content-page">
<h2>{% trans "About dpaste" %}</h2>

View file

@ -1,3 +1,3 @@
<div class="snippet-text">
<div>{{ highlighted }}</div>
</div>
<article class="snippet-text">
{{ highlighted }}
</article>

View file

@ -24,7 +24,7 @@ long_description = u'\n\n'.join((
setup(
name='dpaste',
version='3.0a1',
version='3.0a2',
description='dpaste is a Django based pastebin. It\'s intended to run '
'separately but its also possible to be installed into an '
'existing Django project like a regular app.',
@ -49,9 +49,18 @@ setup(
},
include_package_data=True,
install_requires=[
# Essential packages
'django>=1.11',
'pygments>=1.6',
# Additional Code Lexer
'pygments-lexer-solidity>=0.1.0',
# Additional Text Lexer
'misaka>=2.1.0',
'docutils',
# Testsuite
'tox',
'coverage',
],