Compare commits

..

23 Commits

Author SHA1 Message Date
aa5fd4faf8 set main callback async (should help #6) 2017-01-07 09:11:30 +11:00
613e3fb1b2 support loading files from resource #5 2017-01-06 19:25:51 +11:00
057f770859 fix loading 404 from resource 2017-01-06 18:49:13 +11:00
76d56deff6 quick fix for #5 2017-01-06 17:21:32 +11:00
d2863e4a43 use timeout (1s) only when needed
The timeout are needed, at least on my computer, because ST crashes
otherwise
2017-01-05 16:05:27 +11:00
ed57d2813a update timeout (not crashing anymore) 2017-01-05 14:26:40 +11:00
b4038c3575 clean MarkdownLivePreview.py 2017-01-05 09:22:51 +11:00
fd633e0bc0 focus the md_view back 2017-01-05 09:18:09 +11:00
4a3caf30bb auto create preview when modified (and enabled) 2017-01-04 19:49:49 +11:00
28a7274b05 really basic but working base 2017-01-04 19:46:36 +11:00
6ad9e79926 preview markdown working 2017-01-04 12:24:05 +11:00
ac09c523e7 code formatting 2017-01-04 10:29:23 +11:00
3ad29ede37 hide/show preview on focus working 2017-01-04 10:26:58 +11:00
576956a8d1 restart from scratch 2017-01-03 16:22:25 +11:00
93a04733da better handling of 404. Still not perfect 2017-01-02 18:03:24 +11:00
6a0267fb3b minor fixes (see .tasks) 2017-01-02 16:45:35 +11:00
f52bf98470 remove unused function, code formating 2017-01-02 16:29:33 +11:00
5a3b1a7f81 make CSS monoline 2017-01-02 16:13:11 +11:00
182862ecce loading images from internet working 2017-01-02 16:07:15 +11:00
518f6f1ed4 image loader working but messy 2017-01-02 12:15:40 +11:00
b44151ed69 add 404 error 2017-01-01 20:58:05 +11:00
e4a5ea886a local image working with base64 #3
Need to be tested for a while, but looks good
2017-01-01 19:45:37 +11:00
e6ea13f0a2 fix escaping of the & (helping #2) 2016-12-10 08:08:05 +11:00
22 changed files with 549 additions and 254 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
Thumbs.db Thumbs.db
__pycache__/
cache.txt

View File

@ -3,7 +3,7 @@
"keys": ["alt+m"], "keys": ["alt+m"],
"command": "toggle_setting", "command": "toggle_setting",
"args": { "args": {
"setting": "markdown_preview_enabled" "setting": "markdown_live_preview_enabled"
}, },
"context": [ "context": [
{ {

View File

@ -3,7 +3,7 @@
"caption": "MarkdownLivePreview: Toggle", "caption": "MarkdownLivePreview: Toggle",
"command": "toggle_setting", "command": "toggle_setting",
"args": { "args": {
"setting": "markdown_preview_enabled" "setting": "markdown_live_preview_enabled"
} }
} }
] ]

View File

@ -0,0 +1,3 @@
{
"load_from_internet_when_starts": ["http://", "https://"]
}

BIN
404-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

1
404.txt Normal file

File diff suppressed because one or more lines are too long

144
MLPApi.py Normal file
View File

@ -0,0 +1,144 @@
# -*- encoding: utf-8 -*-
import sublime
import sublime_plugin
import os.path
from html.parser import HTMLParser
from .lib import markdown2
from .escape_amp import *
from .functions import *
from .setting_names import *
__folder__ = os.path.dirname(__file__)
STYLE_FILE = os.path.join(os.path.dirname(__folder__), 'User',
'MarkdownLivePreview.css')
def plugin_loaded():
global DEFAULT_STYLE_FILE
DEFAULT_STYLE_FILE = sublime.load_resource('Packages/MarkdownLivePreview/'
'default.css')
def get_preview_name(md_view):
name = md_view.name() \
or os.path.basename(md_view.file_name()) \
or 'Untitled'
return name + ' - Preview'
def find_preview(window):
for view in window.views():
vsettings = view.settings()
if vsettings.get(IS_PREVIEW):
yield view, vsettings
def create_preview(md_view):
window = md_view.window()
md_view_settings = md_view.settings()
md_view_settings.set(JUST_CREATED, True)
preview = window.new_file()
psettings = preview.settings()
psettings.set(IS_PREVIEW, True)
psettings.set(MD_VIEW_ID, md_view.id())
preview.set_name(get_preview_name(md_view))
preview.set_scratch(True)
md_view_settings.set(PREVIEW_ID, preview.id())
def move_and_focus_md_view():
window.run_command('new_pane')
sublime.set_timeout_async(lambda: window.focus_view(md_view), 250)
sublime.set_timeout_async(move_and_focus_md_view, 250)
return preview
def hide_preview(view):
window = view.window()
vsettings = view.settings()
if vsettings.get(IS_PREVIEW):
preview = view
psettings = vsettings
md_view_id = vsettings.get(MD_VIEW_ID)
md_view = get_view_from_id(window, md_view_id)
if md_view is None:
raise ValueError('Tried to get md_view from id {} but got None'.format(md_view_id))
mdvsettings = md_view.settings()
elif vsettings.get(PREVIEW_ENABLED):
md_view = view
preview_id = vsettings.get(PREVIEW_ID)
preview = get_view_from_id(window, preview_id)
mdvsettings = vsettings
if preview is None:
raise ValueError('Tried to get preview from id {} but got None'.format(preview_id))
psettings = preview.settings()
else:
raise ValueError('Call hide_preview with a view which is not the preview or the md_view')
psettings.set(IS_HIDDEN, True)
mdvsettings.erase(PREVIEW_ID)
sublime.set_timeout(lambda: preview.close(), 250)
return
window = md_view.window()
if window is None:
return
mdvsettings = md_view.settings()
preview_id = mdvsettings.get(PREVIEW_ID)
if not preview_id:
return
mdvsettings.erase(PREVIEW_ID)
preview = get_view_from_id(window, preview_id)
if preview is None:
raise ValueError('Tried to get view from id {} but got None'.format(preview_id))
psettings = preview.settings()
psettings.set(IS_HIDDEN, True)
sublime.set_timeout(preview.close(), 250)
def get_style():
content = ''.join([line.strip() for line in DEFAULT_STYLE_FILE.splitlines()])
return content + "pre code .space {color: var(--light-bg)}"
def show_html(md_view, preview):
html = '<style>{}</style>\n{}'.format(get_style(),
pre_with_br(markdown2.markdown(get_view_content(md_view),
extras=['fenced-code-blocks',
'no-code-highlighting'])))
# the option no-code-highlighting does not exists
# in the official version of markdown2 for now
# I personaly edited the file (markdown2.py:1743)
html = html.replace('&nbsp;', '&nbspespace;') # save where are the spaces
html = HTMLParser().unescape(html)
html = escape_amp(html)
# exception, again, because <pre> aren't supported by the phantoms
html = html.replace('&nbspespace;', '<i class="space">.</i>')
html = replace_img_src_base64(html)
preview.erase_phantoms('markdown_preview')
preview.add_phantom('markdown_preview',
sublime.Region(-1),
html,
sublime.LAYOUT_BLOCK,
lambda href: sublime.run_command('open_url',
{'url': href}))
# set viewport position
# sublime.set_clipboard(html)
return
# 0 < y < 1
y = md_view.text_to_layout(md_view.sel()[0].begin())[1] / md_view.layout_extent()[1]
vector = [0, y * preview.layout_extent()[1]]
# remove half of the viewport_extent.y to center it on the screen (verticaly)
vector[1] -= preview.viewport_extent()[1] / 2
vector[1] = mini(vector[1], 0)
vector[1] += preview.line_height()
preview.set_viewport_position(vector, animate=False)

51
MarkdownLivePreview.py Normal file
View File

@ -0,0 +1,51 @@
# -*- encoding: utf-8 -*-
import sys
import os.path
import sublime
import sublime_plugin
from .MLPApi import *
from .setting_names import *
from .functions import *
class MarkdownLivePreviewListener(sublime_plugin.EventListener):
def on_modified(self, view):
window = view.window()
vsettings = view.settings()
if vsettings.get(PREVIEW_ENABLED):
id = vsettings.get(PREVIEW_ID)
preview = get_view_from_id(window, id)
if id is None or preview is None:
preview = create_preview(view)
sublime.set_timeout_async(lambda: show_html(view, preview), 1000)
else:
show_html(view, preview)
return
def on_activated(self, view):
# if view is md_view and has no preview
# -> create preview
window = view.window()
vsettings = view.settings()
if vsettings.get(PREVIEW_ENABLED):
id = vsettings.get(PREVIEW_ID)
preview = get_view_from_id(window, id)
if id is None or preview is None:
preview = create_preview(view)
sublime.set_timeout(lambda: show_html(view, preview), 1000)
else:
show_html(view, preview)
return
# if view is preview
# -> do nothing
if vsettings.get(IS_PREVIEW):
return
# if view is not the md_view or the preview
# remove preview if any
for view, settings in find_preview(window):
settings.set(IS_HIDDEN, True)
view.close()

32
MarkdownLivePreview.tasks Normal file
View File

@ -0,0 +1,32 @@
Fast:
☐ sync scroll @needsUpdate(because of images)
☐ cache image in object when used, so that it's faster @needsTest
☐ call settings listener on_new too - might be too heavy
☐ add clear cache command
Medium:
☐ auto refresh preview if loading images
☐ use alt attribute for 404 error
☐ use MarkdownLivePreview syntax, so we can use syntax's settings
☐ listen for settings to change
Long:
☐ fix #4 @high
☐ support hanchor (TOC) @big
Unknown:
☐ check how many times is the show_html function called
___________________
Archive:
✔ regive focus to the right markdown view @done Mon 02 Jan 2017 at 18:34 @project(Fast)
✔ try/except for 404 @done Mon 02 Jan 2017 at 18:03 @project(Fast)
✔ fix bug when empty `src` @done Mon 02 Jan 2017 at 17:15 @project(Fast)
✔ preview.set_scratch(True) @done Mon 02 Jan 2017 at 16:58
✔ set the title of the preview @done Mon 02 Jan 2017 at 16:58
✔ preview.wordWrap => True @done Mon 02 Jan 2017 at 16:58
✔ clean the code (syntax) @done Mon 02 Jan 2017 at 16:58
✔ add 404 image @done Mon 02 Jan 2017 at 16:57
✔ load images from internet (`https:`) @done Mon 02 Jan 2017 at 16:57

43
default.css Normal file
View File

@ -0,0 +1,43 @@
:root, html, body {
height: 100%;
}
html {
--light-bg: color(var(--background) blend(#999 85%))
height: 100%;
}
body {
padding:10px;
padding-top: 0px;
font-family: "Open Sans", sans-serif;
background-color: var(--background);
font-size: 15px;
}
blockquote {
font-style: italic;
display: block;
margin-left: 30px;
border: 1px solid red;
}
code {
padding-left: 0.2rem;
padding-right: 0.2rem;
background-color: var(--light-bg);
margin: 0;
border-radius: 3px;
margin: 5px;
}
pre {
display: block;
margin-top: 20px;
line-height: 1.7;
background-color: var(--light-bg);
padding-left: 10px;
width: 100%;
border-radius: 3px;
}
pre code {
padding-left: 0;
}

20
devListener.py Normal file
View File

@ -0,0 +1,20 @@
# -*- encoding: utf-8 -*-
import sublime
import sublime_plugin
import os.path
class MLPDevListener(sublime_plugin.EventListener):
def on_post_save(self, view):
return
if not (os.path.dirname(__file__) in view.file_name() and
view.file_name().endswith('.py')):
return
sublime.run_command('reload_plugin', {
'main': os.path.join(sublime.packages_path(),
'MarkdownLivePreview', 'md_in_popup.py'),
'scripts': ['image_manager', 'functions', 'MLPApi',
'setting_names'],
'quiet': True
})

38
escape_amp.py Normal file
View File

@ -0,0 +1,38 @@
# -*- encoding: utf-8 -*-
import re
__all__ = [
'escape_amp'
]
RE_REPLACE_AMPERSAND = re.compile(r'&(\w*)(;)?')
def replace(matchobj):
if matchobj.group(2):
return matchobj.group(0)
else:
return matchobj.group(0).replace('&', '&amp;')
def escape_amp(text):
return RE_REPLACE_AMPERSAND.sub(replace, text)
def run_tests():
tests = [
['&amp;', '&amp;'],
['&amp', '&amp;amp'],
['&', '&amp;'],
['& &hello &bonjour;', '&amp; &amp;hello &bonjour;']
]
fails = 0
for i, (subject, result) in enumerate(tests):
if RE_REPLACE_AMPERSAND.sub(replace, subject) != result:
print('TEST FAIL ({i}): {subject!r} escaped did not match {result!r}'.format(**locals()))
fails += 1
if fails == 0:
print("SUCCESS: every tests ({}) passed successfully!".format(len(tests)))
else:
print("{} test{} failed".format(fails, 's' if fails > 1 else ''))
if __name__ == '__main__':
run_tests()

98
functions.py Normal file
View File

@ -0,0 +1,98 @@
# -*- encoding: utf-8 -*-
import base64
import os.path
import sublime
import re
from .image_manager import ImageManager
def plugin_loaded():
global error404, loading
loading = sublime.load_resource('Packages/MarkdownLivePreview/loading.txt')
error404 = sublime.load_resource('Packages/MarkdownLivePreview/404.txt')
def replace_img_src_base64(html):
"""Really messy, but it works (should be updated)"""
index = -1
tag_start = '<img src="'
shtml, html = html, list(html)
while True:
index = shtml.find(tag_start, index + 1)
if index == -1:
break
path, end = get_content_till(html, '"', start=index + len(tag_start))
if ''.join(path).startswith('data:image/'):
continue
if ''.join(path).startswith(tuple(get_settings().get('load_from_internet'
'_when_starts'))):
image = ImageManager.get(''.join(path))
image = image or loading
else:
# local image
image = to_base64(''.join(path))
html[index+len(tag_start):end] = image
shtml = ''.join(html)
return ''.join(html)
def is_markdown_view(view):
return 'markdown' in view.scope_name(0)
def to_base64(path=None, content=None):
if path is None and content is None:
return error404
elif content is None and path is not None:
try:
with open(path, 'rb') as fp:
content = fp.read()
except (FileNotFoundError, OSError):
return error404
return 'data:image/png;base64,' + ''.join([chr(el) for el in list(base64.standard_b64encode(content))])
def md(*t, **kwargs):
sublime.message_dialog(kwargs.get('sep', '\n').join([str(el) for el in t]))
def sm(*t, **kwargs):
sublime.status_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
def em(*t, **kwargs):
sublime.error_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
def mini(val, min):
if val < min:
return min
return val
def get_content_till(string, char_to_look_for, start=0):
i = start
while i < len(string):
if string[i] == char_to_look_for:
return string[start:i], i
i += 1
def get_view_content(view):
return view.substr(sublime.Region(0, view.size()))
def get_view_from_id(window, id):
for view in window.views():
if view.id() == id:
return view
def get_settings():
return sublime.load_settings('MarkdownLivePreview.sublime-settings')
def pre_with_br(html):
"""Because the phantoms of sublime text does not support <pre> blocks
this function replaces every \n with a <br> in a <pre>"""
while True:
obj = re.search(r'<pre>(.*?)</pre>', html, re.DOTALL)
if not obj:
break
html = list(html)
html[obj.start(0):obj.end(0)] = '<pre >' + ''.join(html[obj.start(1):obj.end(1)]) \
.replace('\n', '<br>') \
.replace(' ', '&nbsp;') + '</pre>'
html = ''.join(html)
return html

98
image_manager.py Normal file
View File

@ -0,0 +1,98 @@
# -*- encoding: utf-8 -*-
import os.path
from threading import Thread
import urllib.request, urllib.error
import sublime
from .functions import *
import tempfile
CACHE_FILE = os.path.join(tempfile.gettempdir(),
'MarkdownLivePreviewCache.txt')
TIMEOUT = 20 # seconds
SEPARATOR = '---%cache%--'
def get_base64_saver(loading, url):
def callback(content):
if isinstance(content, urllib.error.HTTPError):
if content.getcode() == 404:
loading[url] = 404
return
elif isinstance(content, urllib.error.URLError):
if (content.reason.errno == 11001 and
content.reason.strerror == 'getaddrinfo failed'):
loading[url] = 404
return
return sublime.error_message('An unexpected error has occured: ' +
str(content))
loading[url] = to_base64(content=content)
return callback
def get_cache_for(imageurl):
if not os.path.exists(CACHE_FILE):
return
with open(CACHE_FILE) as fp:
for line in fp.read().splitlines():
url, base64 = line.split(SEPARATOR, 1)
if url == imageurl:
return base64
def cache(imageurl, base64):
with open(CACHE_FILE, 'a') as fp:
fp.write(imageurl + SEPARATOR + base64 + '\n')
class ImageLoader(Thread):
def __init__(self, url, callback):
Thread.__init__(self)
self.url = url
self.callback = callback
def run(self):
try:
page = urllib.request.urlopen(self.url, None, TIMEOUT)
except Exception as e:
self.callback(e)
else:
self.callback(page.read())
class ImageManager(object):
"""
Usage:
>>> image = ImageManager.get('http://domain.com/image.png')
>>> image = ImageManager.get('http://domain.com/image.png')
# still loading (this is a comment, no an outputed text), it doesn't
# run an other request
>>> image = ImageManager.get('http://domain.com/image.png')
'data:image/png;base64,....'
"""
loading = {}
@staticmethod
def get(imageurl, user_callback=None):
cached = get_cache_for(imageurl)
if cached:
return cached
elif imageurl in ImageManager.loading.keys():
# return None (the file is still loading, already made a request)
# return string the base64 of the url (which is going to be cached)
temp_cached = ImageManager.loading[imageurl]
if temp_cached == 404:
return to_base64('404.png')
if temp_cached:
cache(imageurl, temp_cached)
del ImageManager.loading[imageurl]
return temp_cached
else:
# load from internet
ImageManager.loading[imageurl] = None
callback = get_base64_saver(ImageManager.loading, imageurl)
loader = ImageLoader(imageurl, callback)
loader.start()
sublime.set_timeout_async(lambda: loader.join(), TIMEOUT * 1000)

View File

@ -1,3 +1,4 @@
# CSW: ignore
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
#!/usr/bin/env python #!/usr/bin/env python

BIN
loading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

1
loading.txt Normal file
View File

@ -0,0 +1 @@
data:image/png;base64,R0lGODlhZABkAPU+AAAAAAwNDRweHyYpKzg8Pzo+QUBFSERJTEdMT05UV1NYXFVbX1hfY1lfZGFobWJpbmhvdGxzeHF5fnJ6gHV9g3Z+hHqDiXuEin+IjoCIjoKLkYKMkoSNk4eQl4iSmIqTmouUm42XnY+ZoJKco5OdpJOepZSeppahqJeiqZmjqpumrZ6psJ+qsqOutqSvt6SwuKezu6i0vKm1vay4wK66wq+7w6+8xLK+xrK/x7TAybXCy7bDy7jEzbjFzrzJ0gAAACH5BAUAAD4AIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAZABkAAAG/kCfcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiW8IAAAUilUpjQABkEsmMUchkwBIOTOQBQICGUabk0ctFhYdiSajAgOZRKeNRjkYqxaghyuwAgxFtZ1FJBe6NokHvya0nEUzuhYgijG/B86oRCDSOZAPv6VCw0SquiiWNwOwAzfjz0I8uasYPIMvDQ0kRhm/Ee/afKiQ1sIIDBAgkuUxQKDhA29ERMHK9GJSJR85pLUiwkOELgx6Goo0sG/IK1gVhCig9MjHimOreAmBMU+XngciRTrAMSQB/qxmR6KtEjGko7Shey7kbGgA6A0GBz4oOUjCno8YNXWp6NOCwVICD6UYPQqiBiANDHNOkILiqIYVg2Y0yPlAikddICASQtuwJJQY9OAimqFCZpRPei0pPnKjg4fHkB936JBYyg4VmDNrVlH5zYMFoEOLZgDBSoejqDfQEc1atBXUsOl8bi26bpUNsKWpnlPjg+PIj32brZJjs/HOi5PjiMFzCo4ZyAWpqCBhwgspMFa9NZRjg4TvEjZCEQFzWvQ9KiiA/+73SVtpGAT7mcFh/XcPVqH0uCsNhDs+J9gnAQXX+cADCSDMggRVVtGE2lZ6fCAgfkPcdYFhRAhlAVHxxfCnC4d42EdghtII1hYGLgjxki6GOSiNHtR990F+QpymizcZ0SNEjquI1+FHetDHQYFEuCANhBpaMMRAuqRYxEEJDSLPR1YlWVRN9Vjy3ioFCWHlEC6Uh44iOcB0gQck2kSEB90o4sEFx1yY5irQ9JdIDdIANcSXRBiDzGAfVcbnELiwmEgHx3Q5p5JGmOPjIdAF9eIRnyRnhA1AWvqEn4pq6umnoIYq6qiklmrqqaimquqqrLbq6quwxirrrLTWauuttwYBADs=

View File

@ -1,200 +0,0 @@
import sublime
import sublime_plugin
from . import markdown2
import os.path
import re
from html.parser import HTMLParser
# Main sublime tools function
def md(*t, **kwargs):
sublime.message_dialog(kwargs.get('sep', '\n').join([str(el) for el in t]))
def sm(*t, **kwargs):
sublime.status_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
def em(*t, **kwargs):
sublime.error_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
def mini(val, min):
if val < min:
return min
return val
STYLE_FILE = os.path.join(sublime.packages_path(), 'User', 'MarkdownLivePreview.css')
def get_style():
content = None
if os.path.exists(STYLE_FILE):
with open(STYLE_FILE) as fp:
content = fp.read()
return content
if not content:
content = """
html {
--light-bg: color(var(--background) blend(#999 85%))
}
body {
padding:10px;
padding-top: 0px;
font-family: "Open Sans", sans-serif;
background-color: var(--background);
font-size: 15px;
}
blockquote {
font-style: italic;
display: block;
margin-left: 30px;
border: 1px solid red;
}
code {
padding-left: 0.2rem;
padding-right: 0.2rem;
background-color: var(--light-bg);
margin: 0;
border-radius: 3px;
margin: 5px;
}
pre {
display: block;
margin-top: 20px;
line-height: 1.7;
background-color: var(--light-bg);
padding-left: 10px;
width: 100%;
border-radius: 3px;
}
pre code {
padding-left: 0;
}
"""
return content + "pre code .space {color: var(--light-bg)}"
def pre_with_br(html):
"""Because the phantoms of sublime text does not support <pre> blocks
this function replaces every \n with a <br> in a <pre>"""
while True:
obj = re.search(r'<pre>(.*?)</pre>', html, re.DOTALL)
if not obj:
break
html = list(html)
html[obj.start(0):obj.end(0)] = '<pre >' + ''.join(html[obj.start(1):obj.end(1)]) \
.replace('\n', '<br>') \
.replace(' ', '&nbsp;') + '</pre>'
html = ''.join(html)
return html
def close_preview(md_view_settings, preview):
preview.close()
md_view_settings.erase('markdown_preview_id')
md_view_settings.erase('markdown_preview_enabled')
def create_preview(window, md_view):
focus_group, focus_view = window.get_view_index(md_view)
preview = window.new_file()
window.run_command('new_pane') # move the preview to a new group
preview.set_name(os.path.basename(md_view.file_name()) + ' - Preview')
preview_settings = preview.settings()
preview_settings.set('gutter', False)
preview_settings.set('is_markdown_preview', True)
preview_settings.set('markdown_view_id', md_view.id())
md_view.settings().set('markdown_preview_id', preview.id())
window.focus_group(focus_group)
window.focus_view(md_view)
return preview
def show_html(md_view, preview):
html = ('<style>{}</style>'.format(get_style()) +
pre_with_br(markdown2.markdown(get_view_content(md_view),
extras=['fenced-code-blocks', 'no-code-highlighting'])))
# the option no-code-highlighting does not exists
# in the official version of markdown2 for now
# I personaly edited the file (markdown2.py:1743)
html = html.replace('&nbsp;', '&nbspespace;') # save where are the spaces
html = HTMLParser().unescape(html)
# exception, again, because <pre> aren't supported by the phantoms
html = html.replace('&nbspespace;', '<i class="space">.</i>')
preview.erase_phantoms('markdown_preview')
preview.add_phantom('markdown_preview',
sublime.Region(-1),
html,
sublime.LAYOUT_BLOCK,
lambda href: sublime.run_command('open_url', {'url': href}))
# 0 < y < 1
y = md_view.text_to_layout(md_view.sel()[0].begin())[1] / md_view.layout_extent()[1]
vector = [0, y * preview.layout_extent()[1]]
# remove half of the viewport_extent.y to center it on the screen (verticaly)
vector[1] -= preview.viewport_extent()[1] / 2
vector[1] = mini(vector[1], 0)
vector[1] += preview.line_height()
preview.set_viewport_position(vector, animate=False)
def get_view_content(view):
return view.substr(sublime.Region(0, view.size()))
def get_view_from_id(window, id):
for view in window.views():
if view.id() == id:
return view
class MarkdownInPopupCommand(sublime_plugin.EventListener):
def on_load(self, view):
settings = view.settings()
if not 'markdown' in settings.get('syntax').lower():
return
settings.add_on_change('markdown_preview_enabled', lambda: self.on_modified(view))
def on_modified(self, md_view):
window = md_view.window()
md_view_settings = md_view.settings()
if not 'markdown' in md_view_settings.get('syntax').lower():
return
markdown_preview_enabled = md_view_settings.get('markdown_preview_enabled') is True
preview_id = md_view_settings.get('markdown_preview_id', None)
if not markdown_preview_enabled:
if preview_id is not None:
preview = get_view_from_id(window, preview_id)
if preview:
close_preview(md_view_settings, preview)
return
if preview_id is None:
preview = create_preview(window, md_view)
else:
preview = get_view_from_id(window, preview_id)
if not preview:
md_view_settings.erase('markdown_preview_id')
md_view_settings.erase('markdown_preview_enabled')
return
show_html(md_view, preview)
def on_pre_close(self, view):
settings = view.settings()
if settings.get('markdown_preview_enabled') is True:
preview = get_view_from_id(view.window(), settings.get('markdown_preview_id'))
if preview:
sublime.set_timeout_async(lambda: preview.close(), 250)
elif settings.get('is_markdown_preview') is True:
md_view = get_view_from_id(view.window(), settings.get('markdown_view_id'))
if md_view:
def callback():
md_view_settings = md_view.settings()
md_view_settings.erase('markdown_preview_enabled')
md_view_settings.erase('markdown_preview_id')
sublime.set_timeout_async(callback, 250)

View File

@ -1,46 +1,13 @@
# Hello world! # DuckDuckGo - The Search engine you'll fall in love with this is a test.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. This is a test, and this is pretty cool!
### The Zen of Python, by Tim Peters ![image](http://afterishtar.pl/images/100x100.gif)
> Beautiful is better than ugly. Hope you'll enjoy using MarkdownLivePreview!
> Explicit is better than implicit.
> Simple is better than complex.
> Complex is better than complicated.
> Flat is better than nested.
> Sparse is better than dense.
> Readability counts.
> Special cases aren't special enough to break the rules.
> Although practicality beats purity.
> Errors should never pass silently.
> Unless explicitly silenced.
> In the face of ambiguity, refuse the temptation to guess.
> There should be one-- and preferably only one --obvious way to do it.
> Although that way may not be obvious at first unless you're Dutch.
> Now is better than never.
> Although never is often better than *right* now.
> If the implementation is hard to explain, it's a bad idea.
> If the implementation is easy to explain, it may be a good idea.
> Namespaces are one honking great idea -- let's do more of those!
> Code tells you how, comments tells you why ![image](https://forum.sublimetext.com/uploads/st-forum-wide.png)
print('hello world') ![image](http://local.dev/tests/php/img/image-php.php)
print('hi')
```python this is a tets
print('This is some pretty')
print('cool stuff')
if test:
print('hello world')
```
This is some `code`
- a
- list
1. and
2. other
3. list

8
setting_names.py Normal file
View File

@ -0,0 +1,8 @@
# -*- encoding: utf-8 -*-
PREVIEW_ENABLED = 'markdown_live_preview_enabled'
PREVIEW_ID = 'markdown_live_preview_id'
IS_PREVIEW = 'is_markdown_live_preview'
IS_HIDDEN = 'is_hidden_markdown_live_preview'
MD_VIEW_ID = 'markdown_live_preview_md_id'
JUST_CREATED = 'markdown_live_preview_just_created'

12
todo.md
View File

@ -1,12 +0,0 @@
# todo
- add message in status bar @notGoodIdea
- add **custom css** feature @done
- sync scroll @done
- regive focus to the right markdown view @done
- set the title of the preview @done
- disable previewing when the preview is closed @done
- check when setting is activated and create panel and stuff @done