diff --git a/Default.sublime-keymap b/.sublime/Default.sublime-keymap similarity index 100% rename from Default.sublime-keymap rename to .sublime/Default.sublime-keymap diff --git a/Main.sublime-menu b/.sublime/Main.sublime-menu similarity index 100% rename from Main.sublime-menu rename to .sublime/Main.sublime-menu diff --git a/MarkdownLivePreview.sublime-commands b/.sublime/MarkdownLivePreview.sublime-commands similarity index 100% rename from MarkdownLivePreview.sublime-commands rename to .sublime/MarkdownLivePreview.sublime-commands diff --git a/.sublime/MarkdownLivePreview.sublime-settings b/.sublime/MarkdownLivePreview.sublime-settings new file mode 100644 index 0000000..d499f35 --- /dev/null +++ b/.sublime/MarkdownLivePreview.sublime-settings @@ -0,0 +1,3 @@ +{ + "load_from_internet_when_starts": ["http://", "https://"] +} diff --git a/MarkdownLivePreview.tasks b/MarkdownLivePreview.tasks index a70969c..ad3187e 100644 --- a/MarkdownLivePreview.tasks +++ b/MarkdownLivePreview.tasks @@ -1,10 +1,13 @@ todo: - ☐ add **custom css** feature - ☐ sync scroll @needsUpdate - ☐ load images from internet (`https:`) + ☐ sync scroll @needsUpdate(because of images) + ✔ load images from internet (`https:`) @done (17-01-02 15:51) ☐ regive focus to the right markdown view ☐ set the title of the preview - ☐ disable previewing when the preview is closed ☐ preview.set_scratch(True) ☐ add 404 image + ☐ fix #4 + ☐ bug when empty `src` + ☐ auto refresh preview if loading images + ☐ call settings listener on_new too + ☐ preview.wordWrap => True diff --git a/functions.py b/functions.py new file mode 100644 index 0000000..85aafe3 --- /dev/null +++ b/functions.py @@ -0,0 +1,50 @@ +# -*- encoding: utf-8 -*- +import base64 +import os.path +import sublime + +file404 = os.path.join(os.path.dirname(__file__), '404.png') + +def to_base64(path=None, content=None): + if path is None and content is None: + return to_base64(file404) + elif content is None and path is not None: + try: + with open(path, 'rb') as fp: + content = fp.read() + except FileNotFoundError: + return to_base64(file404) + + 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): + i += 1 + if string[i] == char_to_look_for: + return string[start:i], i + +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') diff --git a/image_manager.py b/image_manager.py index 72e38e0..e205a9c 100644 --- a/image_manager.py +++ b/image_manager.py @@ -5,24 +5,15 @@ from threading import Thread import urllib.request import base64 import sublime +from .functions import * CACHE_FILE = os.path.join(os.path.dirname(__file__), 'cache.txt') -TIMEOUT = 20 +TIMEOUT = 20 # seconds SEPARATOR = '---%cache%--' class InternalError(Exception): pass -def to_base64(path=None, content=None): - if content is None and path is not None: - try: - with open(path, 'rb') as fp: - content = fp.read() - except FileNotFoundError: - return to_base64(os.path.join(os.path.dirname(__file__), '404.png')) - - return 'data:image/png;base64,' + ''.join([chr(el) for el in list(base64.standard_b64encode(content))]) - def load_and_save_image(url, user_callback): def callback(content): content = to_base64(content=content) @@ -31,7 +22,25 @@ def load_and_save_image(url, user_callback): user_callback(content) thread = ImageLoader(url, callback) thread.start() - sublime.set_timeout_async(lambda: thread.join(), TIMEOUT) + sublime.set_timeout_async(lambda: thread.join(), TIMEOUT * 1000) + +def get_base64_saver(loading, url): + def callback(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): @@ -42,42 +51,45 @@ class ImageLoader(Thread): def run(self): page = urllib.request.urlopen(self.url, None, TIMEOUT) + # self.callback(self.url, page.read()) self.callback(page.read()) class ImageManager(object): - currently_loading = [] + """ + 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): - if imageurl in ImageManager.currently_loading: - return None - def callback(content): - try: - ImageManager.currently_loading.remove(imageurl) - except ValueError: - sublime.error_message('Internal error: Trying to remove an URL' - 'from loading_url, but not found. Please' - 'report to the issue tracker.') - sublime.run_command('open_url', { - 'url': 'https://github.com/math2001/MarkdownLivePreview/' - 'issues/new' - }) + def get(imageurl, user_callback=None): + # if imageurl in ImageManager.loading.keys(): + # return None - user_callback(content) - - ImageManager.currently_loading.append(imageurl) - try: - with open(CACHE_FILE, 'r') as fp: - lines = fp.readlines() - except FileNotFoundError: - pass + 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: + cache(imageurl, temp_cached) + del ImageManager.loading[imageurl] + return temp_cached else: - for line in lines: - url, base64 = line.split(SEPARATOR, 1) - if url == imageurl: - return callback(base64) - else: - print(url + '\n' + imageurl) - load_and_save_image(imageurl, callback) + # 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) diff --git a/markdown2.py b/lib/markdown2.py similarity index 100% rename from markdown2.py rename to lib/markdown2.py diff --git a/loading.png b/loading.png new file mode 100644 index 0000000..183ae2d Binary files /dev/null and b/loading.png differ diff --git a/md_in_popup.py b/md_in_popup.py index 1ff429a..173f44f 100644 --- a/md_in_popup.py +++ b/md_in_popup.py @@ -1,69 +1,45 @@ # -*- encoding: utf-8 -*- import sublime import sublime_plugin -from . import markdown2 import os.path import re -import base64 - -from .image_manager import ImageManager - -from .escape_amp import * from html.parser import HTMLParser +from .lib import markdown2 +from .image_manager import ImageManager +from .escape_amp import * +from .functions import * + # 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 - - -def get_content_till(string, char_to_look_for, start=0): - i = start - while i < len(string): - i += 1 - if string[i] == char_to_look_for: - return string[start:i], i - -def to_base64(path): - if not os.path.exists(path): - return to_base64(os.path.join(os.path.dirname(__file__), '404.png')) - - with open(path, 'rb') as fp: - content = fp.read() - return 'data:image/png;base64,' + ''.join([chr(el) for el in list(base64.standard_b64encode(content))]) +def plugin_loaded(): + global STYLE_FILE + STYLE_FILE = os.path.join(sublime.packages_path(), 'User', 'MarkdownLivePreview.css') def replace_img_src_base64(html): """Really messy, but it works (should be updated)""" index = -1 tag_start = '