Skip to content
This repository has been archived by the owner on May 17, 2018. It is now read-only.

Commit

Permalink
Merge pull request #304 from facelessuser/master
Browse files Browse the repository at this point in the history
Bug Fixes, Improvements, and Features
  • Loading branch information
facelessuser committed Dec 5, 2015
2 parents 86d15b3 + 8721e69 commit adcf252
Show file tree
Hide file tree
Showing 228 changed files with 1,777 additions and 80,647 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
Changes in Markdown Preview
===========================
## 1.3.0

* Now supports any markdown parser through a generalized method. Now you can map a binary to parser name via `markdown_binary_map`. Then use the parser name in `enabled_parsers` to use it.
* Multimarkdown specific settings have been removed. Multimarkdown should now be configured via `markdown_binary_map` and `enabled_parsers`.
* Upgraded to Python Markdown 2.6.4.
* Removed internal PyYaml and Pygments. Markdown Preview now uses Package Control dependencies to obtain PyYaml and Pygments.
* Update kbd CSS for Github.

## 1.0.3

Expand Down
137 changes: 92 additions & 45 deletions MarkdownPreview.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
import time
import codecs
import cgi
import yaml

pygments_local = {
'github': 'pygments_css/github.css',
'github2014': 'pygments_css/github2014.css'
}


def is_ST3():
Expand All @@ -20,20 +26,14 @@ def is_ST3():

if is_ST3():
from . import desktop
from . import yaml
from .markdown_settings import Settings
from .markdown_wrapper import StMarkdown as Markdown
from .lib.markdown_preview_lib.pygments.formatters import get_formatter_by_name
from .helper import INSTALLED_DIRECTORY
from urllib.request import urlopen, url2pathname, pathname2url
from urllib.parse import urlparse, urlunparse
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from .markdown.extensions import codehilite
try:
PYGMENTS_AVAILABLE = codehilite.pygments
except:
PYGMENTS_AVAILABLE = False

def Request(url, data, headers):
''' Adapter for urllib2 used in ST2 '''
Expand All @@ -44,22 +44,22 @@ def Request(url, data, headers):

else:
import desktop
import yaml
from markdown_settings import Settings
from markdown_wrapper import StMarkdown as Markdown
from lib.markdown_preview_lib.pygments.formatters import get_formatter_by_name
from helper import INSTALLED_DIRECTORY
from urllib2 import Request, urlopen, HTTPError, URLError
from urllib import quote, url2pathname, pathname2url
from urlparse import urlparse, urlunparse
import markdown.extensions.codehilite as codehilite
try:
PYGMENTS_AVAILABLE = codehilite.pygments
except:
PYGMENTS_AVAILABLE = False

unicode_str = unicode

from pygments.formatters import get_formatter_by_name
try:
PYGMENTS_AVAILABLE = codehilite.pygments
except:
PYGMENTS_AVAILABLE = False

_CANNOT_CONVERT = u'cannot convert markdown'

PATH_EXCLUDE = tuple(
Expand Down Expand Up @@ -119,7 +119,7 @@ def load_resource(name):
if is_ST3():
return sublime.load_resource('Packages/Markdown Preview/{0}'.format(name))
else:
filename = os.path.join(sublime.packages_path(), INSTALLED_DIRECTORY, name)
filename = os.path.join(sublime.packages_path(), INSTALLED_DIRECTORY, os.path.normpath(name))
return load_utf8(filename)
except:
print("Error while load_resource('%s')" % name)
Expand Down Expand Up @@ -276,11 +276,14 @@ def repl_relative(m, base_path, relative_path):
def repl_absolute(m, base_path):
""" Replace path with absolute path """
link = m.group(0)
scheme, netloc, path, params, query, fragment, is_url, is_absolute = parse_url(m.group('path')[1:-1])

path = url2pathname(path)
try:
scheme, netloc, path, params, query, fragment, is_url, is_absolute = parse_url(m.group('path')[1:-1])
except Exception:
return link

if (not is_absolute and not is_url):
path = url2pathname(path)
temp = os.path.normpath(os.path.join(base_path, path))
if os.path.exists(temp):
path = pathname2url(temp.replace("\\", "/"))
Expand Down Expand Up @@ -925,18 +928,23 @@ def parser_specific_convert(self, markdown_text):
return markdown_html


class MultiMarkdownCompiler(Compiler):
class ExternalMarkdownCompiler(Compiler):
default_css = "markdown.css"

def __init__(self, parser):
"""Initialize."""

self.parser = parser
super(ExternalMarkdownCompiler, self).__init__()

def parser_specific_convert(self, markdown_text):
import subprocess
binary = self.settings.get("multimarkdown_binary", "")
if os.path.exists(binary):
cmd = [binary]
critic_mode = self.settings.get("strip_critic_marks", "accept")
if critic_mode in ("accept", "reject"):
cmd.append('-a' if critic_mode == "accept" else '-r')
sublime.status_message('converting markdown with multimarkdown...')
settings = sublime.load_settings("MarkdownPreview.sublime-settings")
binary = settings.get('markdown_binary_map', {})[self.parser]

if len(binary) and os.path.exists(binary[0]):
cmd = binary
sublime.status_message('converting markdown with %s...' % self.parser)
if sublime.platform() == "windows":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
Expand All @@ -958,22 +966,35 @@ def parser_specific_convert(self, markdown_text):
print(markdown_html)
markdown_html = _CANNOT_CONVERT
else:
sublime.error_message("Cannot find multimarkdown binary!")
sublime.error_message("Cannot find % binary!" % self.binary)
markdown_html = _CANNOT_CONVERT
return markdown_html


class MarkdownCompiler(Compiler):
default_css = "markdown.css"

def get_highlight(self):
''' return the Pygments css if enabled '''
def set_highlight(self, pygments_style, css_class):
''' Set the Pygments css. '''

highlight = ''
if self.pygments_style and not self.noclasses:
highlight += '<style>%s</style>' % get_formatter_by_name('html', style=self.pygments_style).get_style_defs('.codehilite pre')
if pygments_style and not self.noclasses:
style = None
if pygments_style not in pygments_local:
try:
style = get_formatter_by_name('html', style=pygments_style).get_style_defs('.codehilite pre')
except Exception:
pygments_style = 'github'
if style is None:
style = load_resource(pygments_local[pygments_style]) % {
'css_class': ''.join(['.' + x for x in css_class.split(' ') if x])
}

return highlight
self.pygments_style = '<style>%s</style>' % style
return pygments_style

def get_highlight(self):
''' return the Pygments css if enabled. '''
return self.pygments_style if self.pygments_style else ''

def preprocessor_critic(self, text):
''' Stip out multi-markdown critic marks. Accept changes by default '''
Expand All @@ -985,24 +1006,29 @@ def parser_specific_preprocess(self, text):
return text

def process_extensions(self, extensions):
re_pygments = re.compile(r"pygments_style\s*=\s*([a-zA-Z][a-zA-Z_\d]*)")
re_pygments = re.compile(r"(?:\s*,)?pygments_style\s*=\s*([a-zA-Z][a-zA-Z_\d]*)")
re_pygments_replace = re.compile(r"pygments_style\s*=\s*([a-zA-Z][a-zA-Z_\d]*)")
re_use_pygments = re.compile(r"use_pygments\s*=\s*(True|False)")
re_insert_pygment = re.compile(r"(?P<bracket_start>codehilite\([^)]+?)(?P<bracket_end>\s*\)$)|(?P<start>codehilite)")
re_no_classes = re.compile(r"noclasses\s*=\s*(True|False)")
re_no_classes = re.compile(r"(?:\s*,)?noclasses\s*=\s*(True|False)")
re_css_class = re.compile(r"css_class\s*=\s*([\w\-]+)")
# First search if pygments has manually been set,
# and if so, read what the desired color scheme to use is
self.pygments_style = None
self.noclasses = False
use_pygments = True
pygments_css = None

count = 0
for e in extensions:
if e.startswith("codehilite"):
m = re_use_pygments.search(e)
use_pygments = True if m is None else m.group(1) == 'True'
m = re_css_class.search(e)
css_class = m.group(1) if m else 'codehilite'
pygments_style = re_pygments.search(e)
if pygments_style is None:
self.pygments_style = "github"
pygments_css = "github"
m = re_insert_pygment.match(e)
if m is not None:
if m.group('bracket_start'):
Expand All @@ -1012,9 +1038,20 @@ def process_extensions(self, extensions):
start = m.group('start') + "(pygments_style="
end = ')'

extensions[count] = start + self.pygments_style + end
extensions[count] = start + pygments_css + end
else:
self.pygments_style = pygments_style.group(1)
pygments_css = pygments_style.group(1)

# Set the style, but erase the setting if the CSS is pygments_local.
# Don't allow 'no_css' with non internal themes.
# Replace the setting with the correct name if the style was invalid.
original = pygments_css
pygments_css = self.set_highlight(pygments_css, css_class)
if pygments_css in pygments_local:
extensions[count] = re_no_classes.sub('', re_pygments.sub('', e))
elif original != pygments_css:
extensions[count] = re_pygments_replace.sub('pygments_style=%s' % pygments_css, e)

noclasses = re_no_classes.search(e)
if noclasses is not None and noclasses.group(1) == "True":
self.noclasses = True
Expand All @@ -1023,17 +1060,17 @@ def process_extensions(self, extensions):
# Second, if nothing manual was set, see if "enable_highlight" is enabled with pygment support
# If no style has been set, setup the default
if (
self.pygments_style is None and
pygments_css is None and
self.settings.get("enable_highlight") is True
):
pygments_css = self.set_highlight('github', 'codehilite')
guess_lang = str(bool(self.settings.get("guess_language", True)))
use_pygments = bool(self.settings.get("enable_pygments", True))
extensions.append(
"codehilite(guess_lang=%s,pygments_style=github,use_pygments=%s)" % (
"codehilite(guess_lang=%s,use_pygments=%s)" % (
guess_lang, str(use_pygments)
)
)
self.pygments_style = "github"

if not use_pygments:
self.pygments_style = None
Expand Down Expand Up @@ -1067,15 +1104,20 @@ def parser_specific_convert(self, markdown_text):

class MarkdownPreviewSelectCommand(sublime_plugin.TextCommand):
def run(self, edit, target='browser'):

settings = sublime.load_settings("MarkdownPreview.sublime-settings")
md_map = settings.get('markdown_binary_map', {})
parsers = [
"markdown",
"github",
"multimarkdown"
"github"
]

# Add external markdown binaries.
for k in md_map.keys():
parsers.append(k)

self.target = target

settings = sublime.load_settings("MarkdownPreview.sublime-settings")
enabled_parsers = set()
for p in settings.get("enabled_parsers", ["markdown", "github"]):
if p in parsers:
Expand Down Expand Up @@ -1119,9 +1161,12 @@ def run(self, edit, parser='markdown', target='browser'):

if parser == "github":
compiler = GithubCompiler()
elif parser == "multimarkdown":
compiler = MultiMarkdownCompiler()
elif parser == 'markdown':
compiler = MarkdownCompiler()
elif parser in settings.get("enabled_parsers", ("markdown", "github")):
compiler = ExternalMarkdownCompiler(parser)
else:
# Fallback to Python Markdown
compiler = MarkdownCompiler()

html, body = compiler.run(self.view, preview=(target in ['disk', 'browser']))
Expand Down Expand Up @@ -1259,8 +1304,10 @@ def run(self):

if parser == "github":
compiler = GithubCompiler()
elif parser == "multimarkdown":
compiler = MultiMarkdownCompiler()
elif parser == 'markdown':
compiler = MarkdownCompiler()
elif parser in settings.get("enabled_parsers", ("markdown", "github")):
compiler = ExternalMarkdownCompiler(parser)
else:
compiler = MarkdownCompiler()

Expand Down
24 changes: 19 additions & 5 deletions MarkdownPreview.sublime-settings
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@
*/
"enabled_parsers": ["markdown", "github"],

/*
Custom external markdown parsers.
"markdown_binary_map" contains key values pairs. The key
is name of the parser and what is used in "enabled_parsers"
to turn on the access to the parser. The value is an array
containing the path to the binary and all the parameters that
are desired.
Multimarkdown is provided as an example below. It's path may differ
on your system.
*/

"markdown_binary_map": {
"multimarkdown": ["/usr/local/bin/multimarkdown"]
},

/*
Default mode for the github Markdown parser : markdown (documents) or gfm (comments)
see http://developer.github.com/v3/markdown/#render-an-arbitrary-markdown-document
Expand Down Expand Up @@ -195,11 +212,6 @@
*/
// "path_tempfile": "/tmp/my_notes",

/*
Absolute path to multimarkdown executable
*/
"multimarkdown_binary": "",

/*
Sets HTML output to a simple form:
- No head
Expand Down Expand Up @@ -237,6 +249,8 @@
accept: Accepts the proposed inserts and deletions (comments etc. are discarded)
reject: Rejects the proposed inserts and deletions (comments etc. are discarded)
none: does nothing
Critic marks only affects "github" and "markdown" (Python Markdown).
*/
"strip_critic_marks": "none",

Expand Down
Loading

0 comments on commit adcf252

Please sign in to comment.