forked from revolunet/sublimetext-markdown-preview
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMarkdownPreview.py
267 lines (230 loc) · 11.9 KB
/
MarkdownPreview.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# -*- encoding: UTF-8 -*-
import sublime
import sublime_plugin
import desktop
import tempfile
import markdown2
import os
import sys
import re
import json
import urllib2
try:
settings = sublime.load_settings('MarkdownPreview.sublime-settings')
except:
settings = {}
def getTempMarkdownPreviewPath(view):
''' return a permanent full path of the temp markdown preview file '''
tmp_filename = '%s.html' % view.id()
if settings.get('path_tempfile'):
tmp_fullpath = os.path.join(settings.get('path_tempfile'), tmp_filename)
else:
tmp_fullpath = os.path.join(tempfile.gettempdir(), tmp_filename)
return tmp_fullpath
class MarkdownPreviewListener(sublime_plugin.EventListener):
''' auto update the output html if markdown file has already been converted once '''
def on_post_save(self, view):
filetypes = tuple(settings.get('markdown_filetypes', (".md", ".markdown", ".mdown")))
if filetypes and view.file_name().endswith(filetypes):
temp_file = getTempMarkdownPreviewPath(view)
if os.path.isfile(temp_file):
# reexec markdown conversion
view.run_command('markdown_preview', {'target': 'disk'})
sublime.status_message('Markdown preview file updated')
class MarkdownCheatsheetCommand(sublime_plugin.TextCommand):
''' open our markdown cheat sheet in ST2 '''
def run(self, edit):
cheatsheet = os.path.join(sublime.packages_path(), 'Markdown Preview', 'sample.md')
self.view.window().open_file(cheatsheet)
sublime.status_message('Markdown cheat sheet opened')
class MarkdownPreviewCommand(sublime_plugin.TextCommand):
''' preview file contents with python-markdown and your web browser '''
def getCSS(self):
''' return the correct CSS file based on parser and settings '''
config_parser = settings.get('parser')
config_css = settings.get('css')
styles = ''
if config_css and config_css != 'default':
styles += u"<link href='%s' rel='stylesheet' type='text/css'>" % config_css
else:
css_filename = 'markdown.css'
if config_parser and config_parser == 'github':
css_filename = 'github.css'
# path via package manager
css_path = os.path.join(sublime.packages_path(), 'Markdown Preview', css_filename)
if not os.path.isfile(css_path):
# path via git repo
css_path = os.path.join(sublime.packages_path(), 'sublimetext-markdown-preview', css_filename)
if not os.path.isfile(css_path):
sublime.error_message('markdown.css file not found!')
raise Exception("markdown.css file not found!")
styles += u"<style>%s</style>" % open(css_path, 'r').read().decode('utf-8')
if settings.get('allow_css_overrides'):
filename = self.view.file_name()
filetypes = settings.get('markdown_filetypes')
if filename and filetypes:
for filetype in filetypes:
if filename.endswith(filetype):
css_filename = filename.rpartition(filetype)[0] + '.css'
if (os.path.isfile(css_filename)):
styles += u"<style>%s</style>" % open(css_filename, 'r').read().decode('utf-8')
return styles
def getMathJax(self):
''' return the MathJax script if enabled '''
if settings.get('enable_mathjax') is True:
mathjax_path = os.path.join(sublime.packages_path(), 'Markdown Preview', "mathjax.html")
if not os.path.isfile(mathjax_path):
sublime.error_message('mathjax.html file not found!')
raise Exception("mathjax.html file not found!")
return open(mathjax_path, 'r').read().decode('utf-8')
return ''
def getHighlight(self):
''' return the Highlight.js and css if enabled '''
highlight = ''
if settings.get('enable_highlight') is True and settings.get('parser') == 'default':
highlight_path = os.path.join(sublime.packages_path(), 'Markdown Preview', "highlight.js")
highlight_css_path = os.path.join(sublime.packages_path(), 'Markdown Preview', "highlight.css")
if not os.path.isfile(highlight_path):
sublime.error_message('highlight.js file not found!')
raise Exception("highligh.js file not found!")
if not os.path.isfile(highlight_css_path):
sublime.error_message('highlight.css file not found!')
raise Exception("highlight.css file not found!")
highlight += u"<style>%s</style>" % open(highlight_css_path, 'r').read().decode('utf-8')
highlight += u"<script>%s</script>" % open(highlight_path, 'r').read().decode('utf-8')
highlight += "<script>hljs.initHighlightingOnLoad();</script>"
return highlight
def get_contents(self, region):
''' Get contents or selection from view and optionally strip the YAML front matter '''
contents = self.view.substr(region)
# use selection if any
selection = self.view.substr(self.view.sel()[0])
if selection.strip() != '':
contents = selection
if settings.get('strip_yaml_front_matter') and contents.startswith('---'):
title = ''
title_match = re.search('(?:title:)(.+)', contents, flags=re.IGNORECASE)
if title_match:
stripped_title = title_match.group(1).strip()
title = '%s\n%s\n\n' % (stripped_title, '=' * len(stripped_title))
contents_without_front_matter = re.sub(r'(?s)^---.*---\n', '', contents)
contents = '%s%s' % (title, contents_without_front_matter)
return contents
def postprocessor(self, html):
''' fix relative paths in images, scripts, and links for the internal parser '''
def tag_fix(match):
tag, src = match.groups()
filename = self.view.file_name()
if filename:
if not src.startswith(('file://', 'https://', 'http://', '/', '#')):
abs_path = u'file://%s/%s' % (os.path.dirname(filename), src)
tag = tag.replace(src, abs_path)
return tag
RE_SOURCES = re.compile("""(?P<tag><(?:img|script|a)[^>]+(?:src|href)=["'](?P<src>[^"']+)[^>]*>)""")
html = RE_SOURCES.sub(tag_fix, html)
return html
def convert_markdown(self, markdown):
''' convert input markdown to HTML, with github or builtin parser '''
config_parser = settings.get('parser')
github_oauth_token = settings.get('github_oauth_token')
markdown_html = u'cannot convert markdown'
if config_parser and config_parser == 'github':
# use the github API
sublime.status_message('converting markdown with github API...')
try:
github_mode = settings.get('github_mode', 'gfm')
data = {
"text": markdown,
"mode": github_mode
}
headers = {
'Content-Type': 'application/json'
}
if github_oauth_token:
headers['Authorization'] = "token %s" % github_oauth_token
data = json.dumps(data).encode('utf-8')
url = "https://api.github.com/markdown"
sublime.status_message(url)
request = urllib2.Request(url, data, headers)
markdown_html = urllib2.urlopen(request).read().decode('utf-8')
except urllib2.HTTPError, e:
if e.code == 401:
sublime.error_message('github API auth failed. Please check your OAuth token.')
else:
sublime.error_message('github API responded in an unfashion way :/')
except urllib2.URLError:
sublime.error_message('cannot use github API to convert markdown. SSL is not included in your Python installation')
except:
sublime.error_message('cannot use github API to convert markdown. Please check your settings.')
else:
sublime.status_message('converted markdown with github API successfully')
else:
# convert the markdown
enabled_extras = set(settings.get('enabled_extensions', ['footnotes', 'toc', 'fenced-code-blocks', 'cuddled-lists']))
if settings.get("enable_mathjax") is True or settings.get("enable_highlight") is True:
enabled_extras.add('code-friendly')
markdown_html = markdown2.markdown(markdown, extras=list(enabled_extras))
toc_html = markdown_html.toc_html
if toc_html:
toc_markers = ['[toc]', '[TOC]', '<!--TOC-->']
for marker in toc_markers:
markdown_html = markdown_html.replace(marker, toc_html)
# postprocess the html from internal parser
markdown_html = self.postprocessor(markdown_html)
return markdown_html
def run(self, edit, target='browser'):
region = sublime.Region(0, self.view.size())
contents = self.get_contents(region)
markdown_html = self.convert_markdown(contents)
full_html = u'<!DOCTYPE html>'
full_html += '<html><head><meta charset="utf-8">'
full_html += self.getCSS()
full_html += self.getHighlight()
full_html += self.getMathJax()
full_html += '</head><body>'
full_html += markdown_html
full_html += '</body>'
full_html += '</html>'
if target in ['disk', 'browser']:
# check if LiveReload ST2 extension installed and add its script to the resulting HTML
livereload_installed = ('LiveReload' in os.listdir(sublime.packages_path()))
# build the html
if livereload_installed:
full_html += '<script>document.write(\'<script src="http://\' + (location.host || \'localhost\').split(\':\')[0] + \':35729/livereload.js?snipver=1"></\' + \'script>\')</script>'
# update output html file
tmp_fullpath = getTempMarkdownPreviewPath(self.view)
tmp_html = open(tmp_fullpath, 'w')
tmp_html.write(full_html.encode('utf-8'))
tmp_html.close()
# now opens in browser if needed
if target == 'browser':
config_browser = settings.get('browser')
if config_browser and config_browser != 'default':
cmd = '"%s" %s' % (config_browser, tmp_fullpath)
if sys.platform == 'darwin':
cmd = "open -a %s" % cmd
elif sys.platform == 'linux2':
cmd += ' &'
result = os.system(cmd)
if result != 0:
sublime.error_message('cannot execute "%s" Please check your Markdown Preview settings' % config_browser)
else:
sublime.status_message('Markdown preview launched in %s' % config_browser)
else:
desktop.open(tmp_fullpath)
sublime.status_message('Markdown preview launched in default html viewer')
elif target == 'sublime':
# create a new buffer and paste the output HTML
new_view = self.view.window().new_file()
new_view.set_scratch(True)
new_view.run_command('append', {
'characters': markdown_html,
})
#new_edit = new_view.begin_edit()
#new_view.insert(new_edit, 0, markdown_html)
#new_view.end_edit(new_edit)
sublime.status_message('Markdown preview launched in sublime')
elif target == 'clipboard':
# clipboard copy the full HTML
sublime.set_clipboard(full_html)
sublime.status_message('Markdown export copied to clipboard')