Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 119acbb092 | |||
| 7c4354fb2e | |||
| b93aea6698 | |||
| b3fb5779d3 | |||
| 7bdda5f5c7 | |||
| 7257cb467e | |||
| 40a563fb1e | |||
| 3e0d6ad265 | |||
| f65a068b4e | |||
| bc328642e7 | |||
| d2053be41e | |||
| eb48b1c79f | |||
| 8317fa738c | |||
| 3be12b0539 | |||
| c92d78fb20 | |||
| 30d75f159d | |||
| 52e4b917e5 | |||
| 48a68b2a79 | |||
| 8eb0172eb4 | |||
| 52e35fb610 | |||
| 84f809e57f |
@ -1,4 +1,16 @@
|
|||||||
{
|
{
|
||||||
|
// As soon as you open a markdown file, it opens the window preview
|
||||||
"markdown_live_preview_on_open": false,
|
"markdown_live_preview_on_open": false,
|
||||||
"load_from_internet_when_starts": ["http://", "https://"]
|
|
||||||
|
// If an image starts with one of those strings, then it will be loaded from internet
|
||||||
|
"load_from_internet_when_starts": ["http://", "https://"],
|
||||||
|
|
||||||
|
// When the preview is opened, the markdown file is closed in the origin window and reopend in
|
||||||
|
// the preview window. If this option is set to 'true', then the markdown file will NOT be
|
||||||
|
// closed in the origin window
|
||||||
|
"keep_open_when_opening_preview": false,
|
||||||
|
|
||||||
|
// Choose what to do with YAML/TOML (---/+++ respectively) headers
|
||||||
|
// Valid values: "wrap_in_pre", "remove".
|
||||||
|
"header_action": "wrap_in_pre"
|
||||||
}
|
}
|
||||||
|
|||||||
65
MLPApi.py
65
MLPApi.py
@ -8,6 +8,7 @@ from html.parser import HTMLParser
|
|||||||
|
|
||||||
from .lib import markdown2 as md2
|
from .lib import markdown2 as md2
|
||||||
from .lib.pre_tables import pre_tables
|
from .lib.pre_tables import pre_tables
|
||||||
|
|
||||||
from .escape_amp import *
|
from .escape_amp import *
|
||||||
from .functions import *
|
from .functions import *
|
||||||
from .setting_names import *
|
from .setting_names import *
|
||||||
@ -16,27 +17,11 @@ from random import randint as rnd
|
|||||||
|
|
||||||
__folder__ = os.path.dirname(__file__)
|
__folder__ = os.path.dirname(__file__)
|
||||||
|
|
||||||
USER_STYLE_FILE = os.path.join(os.path.dirname(__folder__), 'User', 'MarkdownLivePreview.css')
|
|
||||||
|
|
||||||
# used to store the phantom's set
|
# used to store the phantom's set
|
||||||
windows_phantom_set = {}
|
windows_phantom_set = {}
|
||||||
|
|
||||||
|
|
||||||
def plugin_loaded():
|
|
||||||
global DEFAULT_STYLE_FILE
|
|
||||||
if os.path.exists(os.path.join(__folder__, 'default.css')):
|
|
||||||
with open(os.path.join(__folder__, 'default.css')) as fp:
|
|
||||||
DEFAULT_STYLE_FILE = fp.read()
|
|
||||||
else:
|
|
||||||
DEFAULT_STYLE_FILE = sublime.load_resource('Packages/MarkdownLivePreview/default.css')
|
|
||||||
|
|
||||||
def get_preview_name(md_view):
|
|
||||||
file_name = md_view.file_name()
|
|
||||||
name = md_view.name() \
|
|
||||||
or os.path.basename(file_name) if file_name else None \
|
|
||||||
or 'Untitled'
|
|
||||||
return name + ' - Preview'
|
|
||||||
|
|
||||||
def create_preview(window, file_name):
|
def create_preview(window, file_name):
|
||||||
preview = window.new_file()
|
preview = window.new_file()
|
||||||
|
|
||||||
@ -47,33 +32,49 @@ def create_preview(window, file_name):
|
|||||||
|
|
||||||
return preview
|
return preview
|
||||||
|
|
||||||
def get_style():
|
def markdown2html(md, basepath, color_scheme):
|
||||||
content = ''.join([line.strip() + ' ' for line in DEFAULT_STYLE_FILE.splitlines()])
|
|
||||||
if os.path.exists(USER_STYLE_FILE):
|
# removes/format the YAML/TOML header.
|
||||||
with open(USER_STYLE_FILE) as fp:
|
md = manage_header(md, get_settings().get('header_action'))
|
||||||
content += '\n' + fp.read() + '\n'
|
|
||||||
return content
|
html = '<style>\n{}\n</style>\n'.format(get_style(color_scheme))
|
||||||
|
|
||||||
|
|
||||||
def markdown2html(md, basepath):
|
|
||||||
html = ''
|
|
||||||
html += '<style>\n{}\n</style>\n'.format(get_style())
|
|
||||||
# pre_with_br
|
|
||||||
html += pre_with_br(pre_tables(md2.markdown(md, extras=['fenced-code-blocks',
|
|
||||||
'no-code-highlighting', 'tables'])))
|
|
||||||
# the option no-code-highlighting does not exists in the official version of markdown2 for now
|
# the option no-code-highlighting does not exists in the official version of markdown2 for now
|
||||||
# I personaly edited the file (markdown2.py:1743)
|
# I personaly edited the file (markdown2.py:1743)
|
||||||
|
html += md2.markdown(md, extras=['fenced-code-blocks', 'tables'])
|
||||||
|
|
||||||
html = html.replace(' ', ' espace;') # save where are the spaces
|
# tables aren't supported by the Phantoms
|
||||||
|
# This function transforms them into aligned ASCII tables and displays them in a <pre> block
|
||||||
|
# (the ironic thing is that they aren't supported either :D)
|
||||||
|
html = pre_tables(html)
|
||||||
|
|
||||||
|
# pre block are not supported by the Phantoms.
|
||||||
|
# This functions replaces the \n in them with <br> so that it does (1/2)
|
||||||
|
html = pre_with_br(html)
|
||||||
|
|
||||||
|
# comments aren't supported by the Phantoms
|
||||||
|
# Simply removes them using bs4, so you can be sadic and type `<!-- hey hey! -->`, these one
|
||||||
|
# won't be stripped!
|
||||||
|
html = strip_html_comments(html)
|
||||||
|
|
||||||
# exception, again, because <pre> aren't supported by the phantoms
|
# exception, again, because <pre> aren't supported by the phantoms
|
||||||
html = html.replace(' espace;', '<i class="space">.</i>')
|
# so, because this is monosaped font, I just replace it with a '.' and make transparent ;)
|
||||||
html = replace_img_src_base64(html, basepath=os.path.dirname(basepath))
|
html = html.replace(' ', '<i class="space">.</i>')
|
||||||
|
|
||||||
|
# Phantoms have problem with images size when they're loaded from an url/path
|
||||||
|
# So, the solution is to convert them to base64
|
||||||
|
html = replace_img_src_base64(html, basepath=basepath)
|
||||||
|
|
||||||
|
# BeautifulSoup uses the <br/> but the sublime phantoms do not support them...
|
||||||
|
html = html.replace('<br/>', '<br />').replace('<hr/>', '<hr />')
|
||||||
|
|
||||||
return html
|
return html
|
||||||
|
|
||||||
def show_html(md_view, preview):
|
def show_html(md_view, preview):
|
||||||
global windows_phantom_set
|
global windows_phantom_set
|
||||||
html = markdown2html(get_view_content(md_view), os.path.dirname(md_view.file_name()))
|
html = markdown2html(get_view_content(md_view), os.path.dirname(md_view.file_name()),
|
||||||
|
os.path.join(sublime.packages_path(), '..', md_view.settings().get('color_scheme')))
|
||||||
|
|
||||||
phantom_set = windows_phantom_set.setdefault(preview.window().id(),
|
phantom_set = windows_phantom_set.setdefault(preview.window().id(),
|
||||||
sublime.PhantomSet(preview, 'markdown_live_preview'))
|
sublime.PhantomSet(preview, 'markdown_live_preview'))
|
||||||
|
|||||||
@ -15,6 +15,7 @@ class NewMarkdownLivePreviewCommand(sublime_plugin.ApplicationCommand):
|
|||||||
|
|
||||||
current_view = sublime.active_window().active_view()
|
current_view = sublime.active_window().active_view()
|
||||||
file_name = current_view.file_name()
|
file_name = current_view.file_name()
|
||||||
|
if get_settings().get('keep_open_when_opening_preview') is False:
|
||||||
current_view.close()
|
current_view.close()
|
||||||
if file_name is None:
|
if file_name is None:
|
||||||
return sublime.error_message('MarkdownLivePreview: Not supporting '
|
return sublime.error_message('MarkdownLivePreview: Not supporting '
|
||||||
@ -70,12 +71,14 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
def on_activated_async(self, view):
|
def on_activated_async(self, view):
|
||||||
vsettings = view.settings()
|
vsettings = view.settings()
|
||||||
|
|
||||||
if (is_markdown_view(view)
|
if (is_markdown_view(view) and get_settings().get(ON_OPEN)
|
||||||
and get_settings().get('markdown_live_preview_on_open')
|
|
||||||
and not vsettings.get(PREVIEW_ENABLED)
|
and not vsettings.get(PREVIEW_ENABLED)
|
||||||
and vsettings.get('syntax') != 'Packages/MarkdownLivePreview/' + \
|
and vsettings.get('syntax') != 'Packages/MarkdownLivePreview/' + \
|
||||||
'.sublime/MarkdownLivePreviewSyntax' + \
|
'.sublime/MarkdownLivePreviewSyntax' + \
|
||||||
'.hidden-tmLanguage'):
|
'.hidden-tmLanguage'
|
||||||
|
and not any(filter(lambda window: window.settings().get(PREVIEW_WINDOW) is True,
|
||||||
|
sublime.windows()))):
|
||||||
|
# print("MarkdownLivePreview.py:81", 'open window')
|
||||||
sublime.run_command('new_markdown_live_preview')
|
sublime.run_command('new_markdown_live_preview')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
79
README.md
79
README.md
@ -4,8 +4,7 @@ This is a sublime text **3** plugin that allows you to preview your markdown ins
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
**None! There is no dependency! It uses [markdown2](https://github.com/trentm/python-markdown2) but
|
**None! There is no dependency!** It uses [markdown2](https://github.com/trentm/python-markdown2) but it's a one file plugin, so it's included in the package.
|
||||||
**it's a one file plugin, so it's included in the package.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ MarkdownLivePreview is available on the default channel of
|
|||||||
4. hit <kbd>enter</kbd>
|
4. hit <kbd>enter</kbd>
|
||||||
|
|
||||||
to have MarkdownLivePreview working on your computer. Cool right? You can
|
to have MarkdownLivePreview working on your computer. Cool right? You can
|
||||||
[thank package control](https://packagecontrol.io/say_thanks) for this.
|
[thank package control](https://packagecontrol.io/say_thanks) for this. :wink:
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
@ -33,84 +32,12 @@ Once you're done, close whichever file and it'll close the entire window.
|
|||||||
*Notice that it will close the entire window if you close __whichever__ file. It means that if you*
|
*Notice that it will close the entire window if you close __whichever__ file. It means that if you*
|
||||||
*open a random file in this window, and then close it, it'll close the entire window still*
|
*open a random file in this window, and then close it, it'll close the entire window still*
|
||||||
|
|
||||||
### Settings
|
For further infos, please [read the docs](https://math2001.github.io/MarkdownLivePreview/)!
|
||||||
|
|
||||||
- `markdown_live_preview_on_open`: if set to `true`, as soon as you open a markdown file, the
|
|
||||||
preview window will popup (thanks to[@ooing](https://github.com/ooing) for its
|
|
||||||
[suggestion](https://github.com/math2001/MarkdownLivePreview/issues/7#issue-199464852)). Default to
|
|
||||||
`false`
|
|
||||||
- `load_from_internet_when_starts`: every images that starts with any of the string specified in
|
|
||||||
this list will be loaded from internet. Default to `["http://", "https://"]`
|
|
||||||
|
|
||||||
Note: To edit your settings, search up in the command palette
|
|
||||||
`Preferences: MarkdownLivePreview Settings`, or by using the menu:
|
|
||||||
*Preferences → Packages Settings → MarkdownLivePreview → Settings*;. It's not your global settings,
|
|
||||||
but only the `MarkdownLivePreview`'s one
|
|
||||||
|
|
||||||
### Syntax Specific Settings
|
|
||||||
|
|
||||||
This in an other "type" of setting. :laughing: If you have a look at the syntax of the preview file
|
|
||||||
(not the markdown one, really the preview), you'll see that the syntax is
|
|
||||||
`MarkdownLivePreviewSyntax`. This mean that you can specify specific settings for this specific
|
|
||||||
syntax (such as `word_wrap: true`, `rulers: []`, etc).
|
|
||||||
|
|
||||||
To do so, you can
|
|
||||||
|
|
||||||
1. focus the *preview* (<kbd>ctrl+2</kbd> to focus the second group, so, by default, the
|
|
||||||
preview's group)
|
|
||||||
2. search up in the command palette `Preferences: Settings Syntax Specific`. It's in the *right*
|
|
||||||
file that you can add the settings you want (not the left one).
|
|
||||||
|
|
||||||
Note: MarkdownLivePreview will actualy look in this file for settings that aren't supported by
|
|
||||||
default. Here they are:
|
|
||||||
|
|
||||||
- `show_tabs`
|
|
||||||
- `show_minimap`
|
|
||||||
- `show_status_bar`
|
|
||||||
- `show_sidebar`
|
|
||||||
- `show_menus`
|
|
||||||
|
|
||||||
They talk for themself, don't they? All of them takes a boolean (`true` or `false`). Note that those
|
|
||||||
settings are *window* specific, not just view specific (that's why they aren't supported). It means
|
|
||||||
that they'll affect the entire window, and every view in it.
|
|
||||||
|
|
||||||
Here is an example of syntax specific settings for MarkdownLivePreviewSyntax:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"show_menus": false,
|
|
||||||
"show_tabs": false,
|
|
||||||
"show_minimap": false,
|
|
||||||
"gutter": false,
|
|
||||||
"rulers": [],
|
|
||||||
"word_wrap": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And here's what you'll get:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
*Note: to close a file, you can do <kbd>ctrl+w</kbd> (on Mac OS, it's <kbd>cmd+w</kbd>)*
|
|
||||||
|
|
||||||
### Clear the cache
|
|
||||||
|
|
||||||
MarkdownLivePreview caches every images it loads from internet (otherwise, you'd never see your
|
|
||||||
images, or you'd need to have a *really* fast internet connection :smile:). So, if for some reason
|
|
||||||
you want to clear the cache (a simple file), you can do so from the command palette by running `
|
|
||||||
|
|
||||||
### Demo
|
### Demo
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Custom css
|
|
||||||
|
|
||||||
It is possible to set your own css. But, be carefull, you have to respect
|
|
||||||
[those rules][st-css-rules]. Just go to
|
|
||||||
`Preferences → Package Settings → MarkdownLivePreview → Style - CSS`. It will open a css file, here:
|
|
||||||
`$packages/User/MarkdownLivePreview.css`. Just save it and it will automatically use it instead of
|
|
||||||
the default one.
|
|
||||||
|
|
||||||
### Somethings wrong!!
|
### Somethings wrong!!
|
||||||
|
|
||||||
If you find that something's wrong with this package, you can let me know by raising an issue on the
|
If you find that something's wrong with this package, you can let me know by raising an issue on the
|
||||||
|
|||||||
@ -25,7 +25,7 @@ code {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
div.codehilite {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
background-color: var(--light-bg);
|
background-color: var(--light-bg);
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"*": {
|
"*": {
|
||||||
"*": [
|
"*": [
|
||||||
"bs4"
|
"bs4",
|
||||||
|
"pygments"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
@ -61,6 +61,28 @@ As told in the introduction, MarkdownLivePreview is very easy to use:
|
|||||||
|
|
||||||
That's it. That's all you need to do to preview your markdown!
|
That's it. That's all you need to do to preview your markdown!
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
To edit MarkdownLivePreview's settings, you just need to search in the command palette
|
||||||
|
`Preferences: MarkdownLivePreview Settings`, or from the menus:
|
||||||
|
*Preferences → Package Settings → MarkdownLivePreview → Settings*
|
||||||
|
|
||||||
|
Do not edit the left file (by default, you cannot), but the right one. This right file will
|
||||||
|
override the default one (on the left), and will be saved in your `User` folder, which makes it easy
|
||||||
|
to back up.
|
||||||
|
|
||||||
|
- `markdown_live_preview_on_open`: if set to `true`, as soon as you open a markdown file, the
|
||||||
|
preview window will popup (thanks to[@ooing][] for its [suggestion][@ooing suggestion]). Default to
|
||||||
|
`false`
|
||||||
|
- `load_from_internet_when_starts`: every images that starts with any of the string specified in
|
||||||
|
this list will be loaded from internet. Default to `["http://", "https://"]`
|
||||||
|
- `header_action`: If you're writing a blog with some markdown and a static website generator, you
|
||||||
|
probably have a YAML header. By default, this header will be displayed in a `pre` block. If you want
|
||||||
|
to hide it, then just change the value to `remove`. Thanks to [@tanhanjay][] for
|
||||||
|
[letting me know][front-matter-issue]!
|
||||||
|
- `keep_open_when_opening_preview`: Each time the preview window is opened, the original markdown
|
||||||
|
view is closed. If you want to keep it opened, just set this setting to `true`
|
||||||
|
|
||||||
### Custom CSS
|
### Custom CSS
|
||||||
|
|
||||||
If you want to, you can add custom `CSS` to the MarkdownLivePreview's default stylesheet.
|
If you want to, you can add custom `CSS` to the MarkdownLivePreview's default stylesheet.
|
||||||
@ -121,7 +143,7 @@ As you probably guessed those settings takes a bool for value (`true` or `false`
|
|||||||
|
|
||||||
### Recommendation
|
### Recommendation
|
||||||
|
|
||||||
Here's what I'd recommend (and use):
|
Here's what I'd recommend for your MarkdownLivePreviewSyntax's settings (and what I use):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -134,15 +156,19 @@ Here's what I'd recommend (and use):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And here's what you'll get (With the awesome [Boxy Theme][] and its [Monokai Color Scheme][]):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
On Windows at least, you can press <kbd>alt</kbd> to focus (so show) the menu, even if they're
|
On Windows at least, you can press <kbd>alt</kbd> to focus (so show) the menus, even if they're
|
||||||
originally hidden
|
originally hidden.
|
||||||
|
|
||||||
That's it! I hope you'll enjoy using this package! If it's the case, please let your friends know
|
That's it! I hope you'll enjoy using this package! If it's the case, please let your friends know
|
||||||
about it, and even myself by sending me a [tweet][] or staring the repo
|
about it, and even myself by sending me a [tweet][] or staring the repo!
|
||||||
<iframe
|
<iframe
|
||||||
src="https://ghbtns.com/github-btn.html?user=math2001&repo=MarkdownLivePreview&type=star&count=true&size=large"
|
src="https://ghbtns.com/github-btn.html?user=math2001&repo=MarkdownLivePreview&type=star&count=true&size=large"
|
||||||
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>!
|
frameborder="0" scrolling="0" style="width: 120px; height: 30px; vertical-align: bottom"></iframe>
|
||||||
|
|
||||||
[st]: https://sublimetext.com
|
[st]: https://sublimetext.com
|
||||||
[Markdown Extended]: https://packagecontrol.io/packages/Markdown%20Extended
|
[Markdown Extended]: https://packagecontrol.io/packages/Markdown%20Extended
|
||||||
@ -150,3 +176,9 @@ frameborder="0" scrolling="0" width="160px" height="30px"></iframe>!
|
|||||||
[install-pck-con]: https://packagecontrol.io/installation
|
[install-pck-con]: https://packagecontrol.io/installation
|
||||||
[tweet]: https://twitter.com/_math2001
|
[tweet]: https://twitter.com/_math2001
|
||||||
[GitHub repo]: https://github.com/math2001/MarkdownLivePreview/issues
|
[GitHub repo]: https://github.com/math2001/MarkdownLivePreview/issues
|
||||||
|
[@ooing]: https://github.com/ooing
|
||||||
|
[@ooing suggestion]: https://github.com/math2001/MarkdownLivePreview/issues/7#issue-199464852
|
||||||
|
[@tanhanjay]: https://github.com/tanhanjay
|
||||||
|
[front-matter-issue]: https://github.com/math2001/MarkdownLivePreview/issues/17
|
||||||
|
[Boxy Theme]: https://packagecontrol.io/packages/Boxy%20Theme
|
||||||
|
[Monokai Color Scheme]: https://github.com/ihodev/sublime-boxy#boxy-monokai--predawn
|
||||||
|
|||||||
24
docs/license.md
Normal file
24
docs/license.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
This project is published under MIT license.
|
||||||
|
|
||||||
|
> The MIT License is a permissive license that is short and to the point. It lets people do anything
|
||||||
|
> they want with your code as long as they provide attribution back to you and don’t hold you
|
||||||
|
> liable.
|
||||||
|
>
|
||||||
|
> — *from [choosealicense.com](http://choosealicense.com), by [GitHub](https://github.com)*
|
||||||
|
|
||||||
|
Copyright 2017 Mathieu PATUREL
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||||
|
andassociated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||||
|
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||||
|
portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||||
|
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@ -1,5 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: Demo
|
||||||
|
description: Preview your markdown right in Sublime Text!
|
||||||
|
hope: You'll enjoy using it!
|
||||||
|
---
|
||||||
|
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
|
<!-- supports comments -->
|
||||||
|
|
||||||
|
And `<!-- vicious ones ;) -->`
|
||||||
|
|
||||||
Some `inline code` with *italic* and **bold** text.
|
Some `inline code` with *italic* and **bold** text.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -16,6 +26,7 @@ if you is moods.curious:
|
|||||||
- you need
|
- you need
|
||||||
- todos
|
- todos
|
||||||
|
|
||||||
|
|
||||||
| ID | Name |
|
| ID | Name |
|
||||||
|-----------|-------|
|
|-----------|-------|
|
||||||
| 56 | Matt |
|
| 56 | Matt |
|
||||||
@ -24,7 +35,7 @@ if you is moods.curious:
|
|||||||
| 45 | John |
|
| 45 | John |
|
||||||
| `<table>` | `><` |
|
| `<table>` | `><` |
|
||||||
|
|
||||||
[Sublime Text Logo](https://upload.wikimedia.org/wikipedia/en/4/4c/Sublime_Text_Logo.png)
|

|
||||||
|
|
||||||
Some plugin I just *need*:
|
Some plugin I just *need*:
|
||||||
|
|
||||||
90
functions.py
90
functions.py
@ -4,39 +4,58 @@ import os.path
|
|||||||
import sublime
|
import sublime
|
||||||
import re
|
import re
|
||||||
from .image_manager import ImageManager
|
from .image_manager import ImageManager
|
||||||
from bs4 import BeautifulSoup
|
from .lib.pygments_from_theme import pygments_from_theme
|
||||||
|
from bs4 import BeautifulSoup, Comment as html_comment
|
||||||
|
|
||||||
def plugin_loaded():
|
def plugin_loaded():
|
||||||
global error404, loading
|
global error404, loading, DEFAULT_STYLE, USER_STYLE_FILE
|
||||||
loading = sublime.load_resource('Packages/MarkdownLivePreview/loading.txt')
|
loading = sublime.load_resource('Packages/MarkdownLivePreview/loading.txt')
|
||||||
error404 = sublime.load_resource('Packages/MarkdownLivePreview/404.txt')
|
error404 = sublime.load_resource('Packages/MarkdownLivePreview/404.txt')
|
||||||
|
|
||||||
|
DEFAULT_STYLE = sublime.load_resource('Packages/MarkdownLivePreview/default.css')
|
||||||
|
USER_STYLE_FILE = os.path.join(sublime.packages_path(), 'User', "MarkdownLivePreview.css")
|
||||||
|
|
||||||
|
MATCH_YAML_HEADER = re.compile(r'^([\-\+])\1{2}\n(?P<content>.+)\n\1{3}\n', re.DOTALL)
|
||||||
|
|
||||||
|
def strip_html_comments(html):
|
||||||
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
|
for element in soup.find_all(text=lambda text: isinstance(text, html_comment)):
|
||||||
|
element.extract()
|
||||||
|
return str(soup)
|
||||||
|
|
||||||
|
def manage_header(md, action):
|
||||||
|
matchobj = MATCH_YAML_HEADER.match(md)
|
||||||
|
if not matchobj:
|
||||||
|
return md
|
||||||
|
if action == 'remove':
|
||||||
|
return md[len(matchobj.group(0)):]
|
||||||
|
elif action == 'wrap_in_pre':
|
||||||
|
return '<pre><code>' + matchobj.group('content') + '</code></pre>' \
|
||||||
|
+ md[len(matchobj.group(0)):]
|
||||||
|
|
||||||
|
raise ValueError('Got an unknown action: "{}"'.format(action))
|
||||||
|
|
||||||
|
def get_preview_name(md_view):
|
||||||
|
file_name = md_view.file_name()
|
||||||
|
name = md_view.name() \
|
||||||
|
or os.path.basename(file_name) if file_name else None \
|
||||||
|
or 'Untitled'
|
||||||
|
return name + ' - Preview'
|
||||||
|
|
||||||
def replace_img_src_base64(html, basepath):
|
def replace_img_src_base64(html, basepath):
|
||||||
"""Really messy, but it works (should be updated)"""
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
index = -1
|
load_from_internet_starters = get_settings().get('load_from_internet_when_starts')
|
||||||
tag_start = '<img src="'
|
for img in soup.find_all('img'):
|
||||||
shtml, html = html, list(html)
|
if img['src'].startswith('data:image/'):
|
||||||
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
|
continue
|
||||||
if ''.join(path).startswith(tuple(get_settings().get('load_from_internet' + \
|
elif img['src'].startswith(tuple(load_from_internet_starters)):
|
||||||
'_when_starts', []))):
|
image = ImageManager.get(img['src']) or loading
|
||||||
image = ImageManager.get(''.join(path))
|
else: # this is a local image
|
||||||
image = image or loading
|
image = to_base64(os.path.join(basepath, img['src']))
|
||||||
|
|
||||||
else:
|
img['src'] = image
|
||||||
# local image
|
|
||||||
path = ''.join(path)
|
return str(soup)
|
||||||
path = os.path.join(basepath, path)
|
|
||||||
image = to_base64(path)
|
|
||||||
html[index+len(tag_start):end] = image
|
|
||||||
shtml = ''.join(html)
|
|
||||||
return ''.join(html)
|
|
||||||
|
|
||||||
def is_markdown_view(view):
|
def is_markdown_view(view):
|
||||||
return 'markdown' in view.scope_name(0)
|
return 'markdown' in view.scope_name(0)
|
||||||
@ -87,12 +106,27 @@ def get_view_from_id(window, id):
|
|||||||
def get_settings():
|
def get_settings():
|
||||||
return sublime.load_settings('MarkdownLivePreview.sublime-settings')
|
return sublime.load_settings('MarkdownLivePreview.sublime-settings')
|
||||||
|
|
||||||
|
|
||||||
|
def _pre_with_spaces(code):
|
||||||
|
for tag in code.find_all(text=True):
|
||||||
|
tag.replace_with(BeautifulSoup(str(tag).replace('\t', ' ' * 4).replace(' ', '<i class="space">.</i>').replace('\n', '<br />'), 'html.parser'))
|
||||||
|
return code
|
||||||
|
|
||||||
def pre_with_br(html):
|
def pre_with_br(html):
|
||||||
"""Because the phantoms of sublime text does not support <pre> blocks
|
"""Because the phantoms of sublime text does not support <pre> blocks
|
||||||
this function replaces every \n with a <br> in a <pre>"""
|
this function replaces every \n with a <br> in a <pre>"""
|
||||||
soup = BeautifulSoup(html)
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
for pre in soup.find_all('pre'):
|
for pre in soup.find_all('pre'):
|
||||||
code = pre.find('code')
|
code = pre.find('code')
|
||||||
code.replaceWith(BeautifulSoup(''.join(str(node) for node in pre.contents) \
|
code.replace_with(_pre_with_spaces(code))
|
||||||
.replace('\n', '<br/>').replace(' ', '<i class="space">.</i>'), 'html.parser'))
|
return str(soup)
|
||||||
return str(soup).replace('<br/>', '<br />')
|
|
||||||
|
|
||||||
|
def get_style(color_scheme):
|
||||||
|
css = DEFAULT_STYLE
|
||||||
|
if os.path.exists(USER_STYLE_FILE):
|
||||||
|
with open(USER_STYLE_FILE) as fp:
|
||||||
|
css += '\n' + fp.read() + '\n'
|
||||||
|
if color_scheme:
|
||||||
|
css += pygments_from_theme(color_scheme)
|
||||||
|
return ''.join([line.strip() + ' ' for line in css.splitlines()])
|
||||||
|
|||||||
167
lib/pygments_from_theme.py
Normal file
167
lib/pygments_from_theme.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from xml.dom.minidom import parse
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
class Style:
|
||||||
|
# .highlight is the wrapper class for highlighting therefore
|
||||||
|
# all css rules are prefixed with .highlight
|
||||||
|
PREFIX = '.codehilite'
|
||||||
|
|
||||||
|
# -----------------------------------------
|
||||||
|
# Params
|
||||||
|
# name: the name of the class
|
||||||
|
# args: each argument is an array.
|
||||||
|
# Each array consists of css properties
|
||||||
|
# that is either a color or font style
|
||||||
|
# ----------------------------------------
|
||||||
|
|
||||||
|
def __init__(self, name, *args):
|
||||||
|
self.name = name # Name of the class
|
||||||
|
self.rules = {} # The css rules
|
||||||
|
for arr in args:
|
||||||
|
for value in arr:
|
||||||
|
# Only define properties if they are already not defined
|
||||||
|
# This allows "cascading" if rules to be applied
|
||||||
|
if value.startswith('#') and 'color' not in self.rules:
|
||||||
|
self.rules['color'] = value
|
||||||
|
else:
|
||||||
|
if 'italic' in value and 'font-style' not in self.rules:
|
||||||
|
self.rules['font-style'] = 'italic'
|
||||||
|
if 'underline' in value and 'text-decoration' not in self.rules:
|
||||||
|
self.rules['text-decoration'] = 'underline'
|
||||||
|
if 'bold' in value and 'font-weight' not in self.rules:
|
||||||
|
self.rules['font-weight'] = 'bold'
|
||||||
|
|
||||||
|
# Helper method for creating the css rule
|
||||||
|
def _join_attr(self):
|
||||||
|
temp = []
|
||||||
|
if(len(self.rules) == 0):
|
||||||
|
return ''
|
||||||
|
for key in self.rules:
|
||||||
|
temp.append(key + ': ' + self.rules[key])
|
||||||
|
return '; '.join(temp) + ';'
|
||||||
|
|
||||||
|
def toString(self):
|
||||||
|
joined = self._join_attr()
|
||||||
|
if joined:
|
||||||
|
return "%s .%s { %s }" % (Style.PREFIX, self.name, joined)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
# Crappy xml parsing function for getting the
|
||||||
|
# colors and font styles from colortheme file
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings(file_name):
|
||||||
|
settings = defaultdict(lambda: [])
|
||||||
|
dom = parse(file_name)
|
||||||
|
arr = dom.getElementsByTagName('array')[0]
|
||||||
|
editor_cfg = arr.getElementsByTagName('dict')[0].getElementsByTagName('dict')[0]
|
||||||
|
editor_vals = editor_cfg.getElementsByTagName('string')
|
||||||
|
background = editor_vals[0].firstChild.nodeValue
|
||||||
|
text_color = editor_vals[2].firstChild.nodeValue
|
||||||
|
settings['editor_bg'] = background
|
||||||
|
settings['text_color'] = text_color
|
||||||
|
for node in arr.childNodes:
|
||||||
|
if node.nodeName == "dict":
|
||||||
|
try:
|
||||||
|
setting = node.getElementsByTagName('string')[1].firstChild.nodeValue
|
||||||
|
attrs = []
|
||||||
|
values = node.getElementsByTagName('dict')[0].getElementsByTagName('string')
|
||||||
|
for v in values:
|
||||||
|
if v.firstChild:
|
||||||
|
a = str(v.firstChild.nodeValue).strip()
|
||||||
|
attrs.append(a)
|
||||||
|
for s in setting.split(', '):
|
||||||
|
settings[s] = attrs
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
def pygments_from_theme(file):
|
||||||
|
settings = get_settings(file)
|
||||||
|
styles = []
|
||||||
|
|
||||||
|
#Generic
|
||||||
|
styles.append(Style('ge', ['italic']))
|
||||||
|
styles.append(Style('gs', ['bold']))
|
||||||
|
|
||||||
|
# Comments
|
||||||
|
styles.append(Style('c', settings['comment']))
|
||||||
|
styles.append(Style('cp', settings['comment']))
|
||||||
|
styles.append(Style('c1', settings['comment']))
|
||||||
|
styles.append(Style('cs', settings['comment']))
|
||||||
|
styles.append(Style('cm', settings['comment.block'], settings['comment']))
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
styles.append(Style('m', settings['constant.numeric'], settings['constant.other'], settings['constant'], settings['support.constant']))
|
||||||
|
styles.append(Style('mf', settings['constant.numeric'], settings['constant.other'], settings['constant'], settings['support.constant']))
|
||||||
|
styles.append(Style('mi', settings['constant.numeric'], settings['constant.other'], settings['constant'], settings['support.constant']))
|
||||||
|
styles.append(Style('mo', settings['constant.numeric'], settings['constant.other'], settings['constant'], settings['support.constant']))
|
||||||
|
styles.append(Style('se', settings['constant.language'], settings['constant.other'], settings['constant'], settings['support.constant']))
|
||||||
|
styles.append(Style('kc', settings['constant.language'], settings['constant.other'], settings['constant'], settings['support.constant']))
|
||||||
|
|
||||||
|
#Keywords
|
||||||
|
styles.append(Style('k', settings['entity.name.type'], settings['support.type'], settings['keyword']))
|
||||||
|
styles.append(Style('kd', settings['storage.type'], settings['storage']))
|
||||||
|
styles.append(Style('kn', settings['support.function.construct'], settings['keyword.control'], settings['keyword']))
|
||||||
|
styles.append(Style('kt', settings['entity.name.type'], settings['support.type'], settings['support.constant']))
|
||||||
|
|
||||||
|
#String
|
||||||
|
styles.append(Style('settings', settings['string.quoted.double'], settings['string.quoted'], settings['string']))
|
||||||
|
styles.append(Style('sb', settings['string.quoted.double'], settings['string.quoted'], settings['string']))
|
||||||
|
styles.append(Style('sc', settings['string.quoted.single'], settings['string.quoted'], settings['string']))
|
||||||
|
styles.append(Style('sd', settings['string.quoted.double'], settings['string.quoted'], settings['string']))
|
||||||
|
styles.append(Style('s2', settings['string.quoted.double'], settings['string.quoted'], settings['string']))
|
||||||
|
styles.append(Style('sh', settings['string']))
|
||||||
|
styles.append(Style('si', settings['string.interpolated'], settings['string']))
|
||||||
|
styles.append(Style('sx', settings['string.other'], settings['string']))
|
||||||
|
styles.append(Style('sr', settings['string.regexp'], settings['string']))
|
||||||
|
styles.append(Style('s1', settings['string.quoted.single'], settings['string']))
|
||||||
|
styles.append(Style('ss', settings['string']))
|
||||||
|
|
||||||
|
#Name
|
||||||
|
styles.append(Style('na', settings['entity.other.attribute-name'], settings['entity.other']))
|
||||||
|
styles.append(Style('bp', settings['variable.language'], settings['variable']))
|
||||||
|
styles.append(Style('nc', settings['entity.name.class'], settings['entity.other.inherited-class'], settings['support.class']))
|
||||||
|
styles.append(Style('no', settings['constant.language'], settings['constant']))
|
||||||
|
styles.append(Style('nd', settings['entity.name.class']))
|
||||||
|
styles.append(Style('ne', settings['entity.name.class']))
|
||||||
|
styles.append(Style('nf', settings['entity.name.function'], settings['support.function']))
|
||||||
|
styles.append(Style('nt', settings['entity.name.tag'], settings['keyword']))
|
||||||
|
styles.append(Style('nv', settings['variable'], [settings['text_color']]))
|
||||||
|
styles.append(Style('vc', settings['variable.language']))
|
||||||
|
styles.append(Style('vg', settings['variable.language']))
|
||||||
|
styles.append(Style('vi', settings['variable.language']))
|
||||||
|
|
||||||
|
#Operator
|
||||||
|
styles.append(Style('ow', settings['keyword.operator'], settings['keyword.operator'], settings['keyword']))
|
||||||
|
styles.append(Style('o', settings['keyword.operator'], settings['keyword.operator'], settings['keyword']))
|
||||||
|
|
||||||
|
# Text
|
||||||
|
styles.append(Style('n', [settings['text_color']]))
|
||||||
|
styles.append(Style('nl', [settings['text_color']]))
|
||||||
|
styles.append(Style('nn', [settings['text_color']]))
|
||||||
|
styles.append(Style('nx', [settings['text_color']]))
|
||||||
|
styles.append(Style('bp', settings['variable.language'], settings['variable'], [settings['text_color']]))
|
||||||
|
styles.append(Style('p', [settings['text_color']]))
|
||||||
|
|
||||||
|
css = '{} {{ background-color: {}; color: {}; }}\n'.format(Style.PREFIX, settings['editor_bg'], settings['text_color'])
|
||||||
|
for st in styles:
|
||||||
|
css_style = st.toString()
|
||||||
|
if css_style:
|
||||||
|
css += css_style + '\n'
|
||||||
|
|
||||||
|
return css
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if len(args) < 1:
|
||||||
|
print("Please provide the .tmTheme file!", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(pygments_from_theme(args[0]))
|
||||||
@ -2,5 +2,6 @@
|
|||||||
"install": "messages/install.txt",
|
"install": "messages/install.txt",
|
||||||
"1.1.2": "messages/1.1.2.txt",
|
"1.1.2": "messages/1.1.2.txt",
|
||||||
"2.0.1": "messages/2.0.1.txt",
|
"2.0.1": "messages/2.0.1.txt",
|
||||||
"2.2.1": "messages/2.2.0.txt"
|
"2.2.1": "messages/2.2.0.txt",
|
||||||
|
"2.4.1": "messages/2.4.txt"
|
||||||
}
|
}
|
||||||
|
|||||||
10
messages/2.4.txt
Normal file
10
messages/2.4.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Sorry to interrupt you... :(
|
||||||
|
|
||||||
|
Some stuff changed on MarkdownLivePreview. It now supports YAML/TOML front matters. You can hide it,
|
||||||
|
or show it in a pre block (edit your settings for this).
|
||||||
|
|
||||||
|
Hope you'll enjoy it!
|
||||||
|
|
||||||
|
Tip of the day: If you want a VIM-like search feature, then just press 'ctrl/cmd+i'
|
||||||
|
(Find → Incremental find). You can still go the next match by pressing 'f3', and to
|
||||||
|
the previous one by pressing 'shift+f3'
|
||||||
@ -6,3 +6,4 @@ IS_PREVIEW = 'is_markdown_live_preview'
|
|||||||
IS_HIDDEN = 'is_hidden_markdown_live_preview'
|
IS_HIDDEN = 'is_hidden_markdown_live_preview'
|
||||||
MD_VIEW_ID = 'markdown_live_preview_md_id'
|
MD_VIEW_ID = 'markdown_live_preview_md_id'
|
||||||
PREVIEW_WINDOW = 'markdown_live_preview_window'
|
PREVIEW_WINDOW = 'markdown_live_preview_window'
|
||||||
|
ON_OPEN = 'markdown_live_preview_on_open'
|
||||||
|
|||||||
12
test.md
Normal file
12
test.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
```python
|
||||||
|
import this
|
||||||
|
|
||||||
|
if you.are('new'):
|
||||||
|
print('Welcome!')
|
||||||
|
if you.are('brand new'):
|
||||||
|
print("You'll see, python's just awesome")
|
||||||
|
else:
|
||||||
|
print('Hello!')
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
@ -4,15 +4,17 @@ Fast:
|
|||||||
Medium:
|
Medium:
|
||||||
☐ auto refresh preview if loading images
|
☐ auto refresh preview if loading images
|
||||||
☐ use alt attribute for 404 error
|
☐ use alt attribute for 404 error
|
||||||
|
☐ optimize usage of BeautifulSoup
|
||||||
|
|
||||||
Long:
|
Long:
|
||||||
☐ support anchor (TOC) @big
|
☐ support anchor (TOC) @big
|
||||||
|
|
||||||
Unknown:
|
Unknown:
|
||||||
|
☐ properly convert tmtheme to css
|
||||||
|
|
||||||
___________________
|
___________________
|
||||||
Archive:
|
Archive:
|
||||||
|
✔ add settings to keep md view open #13 @done Sat 11 Feb 2017 at 09:10 @project(Fast)
|
||||||
✔ fix custom css @bug @done Sun 22 Jan 2017 at 18:40 @project(Medium)
|
✔ fix custom css @bug @done Sun 22 Jan 2017 at 18:40 @project(Medium)
|
||||||
✘ check how many times is the show_html function called @cancelled Sun 22 Jan 2017 at 18:40 @project(Unknown)
|
✘ check how many times is the show_html function called @cancelled Sun 22 Jan 2017 at 18:40 @project(Unknown)
|
||||||
✔ sync scroll @needsUpdate(because of images) @done Sun 22 Jan 2017 at 18:39 @project(Fast)
|
✔ sync scroll @needsUpdate(because of images) @done Sun 22 Jan 2017 at 18:39 @project(Fast)
|
||||||
Reference in New Issue
Block a user