feat(SublimeText2.EditorPackages): cache packages

This commit is contained in:
Iristyle
2013-04-04 08:55:15 -04:00
parent d65666cdfc
commit c3efdad2c2
274 changed files with 26863 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
*.pyc
SmartMarkdown.sublime-project
SmartMarkdown.sublime-workspace
release.sh
test/*
.ropeproject

View File

@@ -0,0 +1,26 @@
[
{ "keys": ["ctrl+;", "ctrl+n"], "command": "headline_move",
"args": {"forward": true, "same_level": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+;", "ctrl+p"], "command": "headline_move",
"args": {"forward": false, "same_level": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+;", "ctrl+f"], "command": "headline_move",
"args": {"forward": true, "same_level": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+;", "ctrl+b"], "command": "headline_move",
"args": {"forward": false, "same_level": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
}
]

View File

@@ -0,0 +1,26 @@
[
{ "keys": ["ctrl+c", "ctrl+n"], "command": "headline_move",
"args": {"forward": true, "same_level": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+c", "ctrl+p"], "command": "headline_move",
"args": {"forward": false, "same_level": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+c", "ctrl+f"], "command": "headline_move",
"args": {"forward": true, "same_level": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+c", "ctrl+b"], "command": "headline_move",
"args": {"forward": false, "same_level": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
}
]

View File

@@ -0,0 +1,26 @@
[
{ "keys": ["ctrl+;", "ctrl+n"], "command": "headline_move",
"args": {"forward": true, "same_level": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+;", "ctrl+p"], "command": "headline_move",
"args": {"forward": false, "same_level": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+;", "ctrl+f"], "command": "headline_move",
"args": {"forward": true, "same_level": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["ctrl+;", "ctrl+b"], "command": "headline_move",
"args": {"forward": false, "same_level": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
}
]

View File

@@ -0,0 +1,27 @@
[
{
"caption": "Pandoc: Render Markdown to temp PDF and View",
"command": "pandoc_render",
"args":{"open_after":true, "target":"pdf", "save_result":false}
},
{
"caption": "Pandoc: Render Markdown to temp HTML and View",
"command": "pandoc_render",
"args":{"open_after":true, "target":"html", "save_result":false}
},
{
"caption": "Pandoc: Render Markdown to HTML",
"command": "pandoc_render",
"args":{"open_after":false, "target":"html", "save_result":true}
},
{
"caption": "Pandoc: Render Markdown to PDF",
"command": "pandoc_render",
"args":{"open_after":false, "target":"pdf", "save_result":true}
},
{
"caption": "Pandoc: Render Markdown DocX",
"command": "pandoc_render",
"args":{"open_after":false, "target":"docx", "save_result":true}
}
]

View File

@@ -0,0 +1,72 @@
[
{ "keys": ["tab"], "command": "smart_folding", "context":
[
{ "key": "selector", "operator": "equal", "operand": "markup.heading.markdown" }
]
},
{ "keys": ["shift+tab"], "command": "global_folding", "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" }
]
},
{ "keys": ["enter"], "command": "smart_list", "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*([-+\\**]|\\d+\\.+)\\s+" }
]
},
{ "keys": ["enter"], "command": "smart_list", "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" },
{ "key": "selector", "operator": "equal", "operand": "markup.list" }
]
},
{ "keys": ["tab"], "command": "smart_table",
"args": {"forward": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*(\\||\\+[-=])",
"match_all": true}
]
},
{ "keys": ["tab"], "command": "smart_table",
"args": {"forward": true}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*" },
{ "key": "following_text", "operator": "regex_contains", "operand": "\\s*(\\||\\+[-=])",
"match_all": true}
]
},
{ "keys": ["shift+tab"], "command": "smart_table",
"args": {"forward": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*(\\||\\+[-=])",
"match_all": true}
]
},
{ "keys": ["shift+tab"], "command": "smart_table",
"args": {"forward": false}, "context":
[
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown" },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*" },
{ "key": "following_text", "operator": "regex_contains", "operand": "\\s*(\\||\\+[-=])",
"match_all": true}
]
},
{
"keys": ["super+shift+."], "command": "change_heading_level",
"args": {"up": true}, "context":
[
{"key": "selector", "operator": "equal", "operand": "text.html.markdown"}
]
},
{
"keys": ["super+shift+,"], "command": "change_heading_level",
"args": {"up": false}, "context":
[
{"key": "selector", "operator": "equal", "operand": "text.html.markdown"}
]
}
]

View File

@@ -0,0 +1,70 @@
[
{
"caption": "Preferences",
"mnemonic": "n",
"id": "preferences",
"children":
[
{
"caption": "Package Settings",
"mnemonic": "P",
"id": "package-settings",
"children":
[
{
"caption": "SmartMarkdown",
"children":
[
{
"command": "open_file", "args":
{
"file": "${packages}/SmartMarkdown/SmartMarkdown.sublime-settings"
},
"caption": "Settings Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/SmartMarkdown.sublime-settings"
},
"caption": "Settings User"
},
{ "caption": "-" },
{
"command": "open_file", "args":
{
"file": "${packages}/SmartMarkdown/Default.sublime-keymap"
},
"caption": "Key Bindings Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings User"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/Default (Linux).sublime-keymap",
"platform": "Linux"
},
"caption": "Key Bindings User"
},
{
"command": "open_file",
"args": {
"file": "${packages}/User/Default (Windows).sublime-keymap",
"platform": "Windows"
},
"caption": "Key Bindings User"
}
]
}
]
}
]
}
]

View File

@@ -0,0 +1,11 @@
{
/* Please specify the PATH of pdflatex if you wanna generate PDF */
"tex_path": ["/usr/local/texlive/2011/bin/x86_64-darwin",
"/usr/local/texlive/2012/bin/x86_64-darwin"],
/* Provide your arguments here as a list e.g.: ["--latex-engine=xelatex", "--toc"]
arguments that are separated by space must be in separate slots. e.g. ["-H", "template.tex"] */
"pandoc_args": [],
"pandoc_args_pdf": [],
"pandoc_args_html": [],
"pandoc_args_docx": []
}

View File

@@ -0,0 +1,263 @@
"""Some utility functions for working with headline of Markdown.
Terminologies
- Headline :: The headline entity OR the text of the headline
- Content :: The content under the current headline. It stops after
encountering a headline with the same or higher level OR EOF.
"""
# Author: Muchenxuan Tong <demon386@gmail.com>
import re
import sublime
try:
from .utilities import is_region_void
except ValueError:
from utilities import is_region_void
MATCH_PARENT = 1 # Match headlines at the same or higher level
MATCH_CHILD = 2 # Match headlines at the same or lower level
MATCH_SILBING = 3 # Only Match headlines at the same level.
MATCH_ANY = 4 # Any headlines would be matched.
ANY_LEVEL = -1 # level used when MATCH_ANY is used as match type
def region_of_content_of_headline_at_point(view, from_point):
"""Extract the region of the content of under current headline."""
_, level = headline_and_level_at_point(view, from_point)
if level == None:
return None
if is_content_empty_at_point(view, from_point):
return None
line_num, _ = view.rowcol(from_point)
content_line_start_point = view.text_point(line_num + 1, 0)
next_headline, _ = find_headline(view, \
content_line_start_point, \
level, \
True, \
MATCH_PARENT)
if not is_region_void(next_headline):
end_pos = next_headline.a - 1
else:
end_pos = view.size()
return sublime.Region(content_line_start_point, end_pos)
def headline_and_level_at_point(view, from_point, search_above_and_down=False):
"""Return the current headline and level.
If from_point is inside a headline, then return the headline and level.
Otherwise depends on the argument it might search above and down.
"""
line_region = view.line(from_point)
line_content = view.substr(line_region)
# Update the level in case it's headline.ANY_LEVEL
level = _extract_level_from_headline(line_content)
# Search above and down
if level is None and search_above_and_down:
# Search above
headline_region, _ = find_headline(view,\
from_point,\
ANY_LEVEL,
False,
skip_folded=True)
if not is_region_void(headline_region):
line_content, level = headline_and_level_at_point(view,\
headline_region.a)
# Search down
if level is None:
headline_region, _ = find_headline(view,\
from_point,\
ANY_LEVEL,
True,
skip_folded=True)
if not is_region_void(headline_region):
line_content, level = headline_and_level_at_point(view, headline_region.a)
return line_content, level
def _extract_level_from_headline(headline):
"""Extract the level of headline, None if not found.
"""
re_string = _get_re_string(ANY_LEVEL, MATCH_ANY)
match = re.match(re_string, headline)
if match:
return len(match.group(1))
else:
return None
def is_content_empty_at_point(view, from_point):
"""Check if the content under the current headline is empty.
For implementation, check if next line is a headline a the same
or higher level.
"""
_, level = headline_and_level_at_point(view, from_point)
if level is None:
raise ValueError("from_point must be inside a valid headline.")
line_num, _ = view.rowcol(from_point)
next_line_region = view.line(view.text_point(line_num + 1, 0))
next_line_content = view.substr(next_line_region)
next_line_level = _extract_level_from_headline(next_line_content)
# Note that EOF works too in this case.
if next_line_level and next_line_level <= level:
return True
else:
return False
def find_headline(view, from_point, level, forward=True, \
match_type=MATCH_ANY, skip_headline_at_point=False, \
skip_folded=False):
"""Return the region of the next headline or EOF.
Parameters
----------
view: sublime.view
from_point: int
From which to find.
level: int
The headline level to match.
forward: boolean
Search forward or backward
match_type: int
MATCH_SILBING, MATCH_PARENT, MATCH_CHILD or MATCH_ANY.
skip_headline_at_point: boolean
When searching whether skip the headline at point
skip_folded: boolean
Whether to skip the folded region
Returns
-------
match_region: int
Matched region, or None if not found.
match_level: int
The level of matched headline, or None if not found.
"""
if skip_headline_at_point:
# Move the point to the next line if we are
# current in a headline already.
from_point = _get_new_point_if_already_in_headline(view, from_point,
forward)
re_string = _get_re_string(level, match_type)
if forward:
match_region = view.find(re_string, from_point)
else:
all_match_regions = view.find_all(re_string)
match_region = _nearest_region_among_matches_from_point(view, \
all_match_regions, \
from_point, \
False, \
skip_folded)
if skip_folded:
while (_is_region_folded(match_region, view)):
from_point = match_region.b
match_region = view.find(re_string, from_point)
if not is_region_void(match_region):
if not is_scope_headline(view, match_region.a):
return find_headline(view, match_region.a, level, forward, \
match_type, True, skip_folded)
else:
## Extract the level of matched headlines according to the region
headline = view.substr(match_region)
match_level = _extract_level_from_headline(headline)
else:
match_level = None
return (match_region, match_level)
def _get_re_string(level, match_type=MATCH_ANY):
"""Get regular expression string according to match type.
Return regular expression string, rather than compiled string. Since
sublime's view.find function needs string.
Parameters
----------
match_type: int
MATCH_SILBING, MATCH_PARENT, MATCH_CHILD or ANY_LEVEL.
"""
if match_type == MATCH_ANY:
re_string = r'^(#+)\s.*'
else:
try:
if match_type == MATCH_PARENT:
re_string = r'^(#{1,%d})\s.*' % level
elif match_type == MATCH_CHILD:
re_string = r'^(#{%d,})\s.*' % level
elif match_type == MATCH_SILBING:
re_string = r'^(#{%d,%d})\s.*' % (level, level)
except ValueError:
print("match_type has to be specified if level isn't ANY_LEVE")
return re_string
def _get_new_point_if_already_in_headline(view, from_point, forward=True):
line_content = view.substr(view.line(from_point))
if _extract_level_from_headline(line_content):
line_num, _ = view.rowcol(from_point)
if forward:
return view.text_point(line_num + 1, 0)
else:
return view.text_point(line_num, 0) - 1
else:
return from_point
def is_scope_headline(view, from_point):
return view.score_selector(from_point, "markup.heading") > 0 or \
view.score_selector(from_point, "meta.block-level.markdown") > 0
def _nearest_region_among_matches_from_point(view, all_match_regions, \
from_point, forward=False,
skip_folded=True):
"""Find the nearest matched region among all matched regions.
None if not found.
"""
nearest_region = None
for r in all_match_regions:
if not forward and r.b <= from_point and \
(not nearest_region or r.a > nearest_region.a):
candidate = r
elif forward and r.a >= from_point and \
(not nearest_region or r.b < nearest_region.b):
candidate = r
else:
continue
if skip_folded and not _is_region_folded(candidate, view):
nearest_region = candidate
return nearest_region
def _is_region_folded(region, view):
for i in view.folded_regions():
if i.contains(region):
return True
return False

View File

@@ -0,0 +1,21 @@
"""This file is contributed by [David Smith](https://github.com/djs070)
"""
import sublime
import sublime_plugin
class ChangeHeadingLevelCommand(sublime_plugin.TextCommand):
def run(self, edit, up=True):
for region in self.view.sel():
line = self.view.line(region)
if up:
# Increase heading level
if not self.view.substr(line)[0] in ['#', ' ']:
self.view.insert(edit, line.begin(), " ")
self.view.insert(edit, line.begin(), "#")
else:
# Decrease heading level
if self.view.substr(line)[0] == '#':
self.view.erase(edit, sublime.Region(line.begin(), line.begin() + 1))
if self.view.substr(line)[0] == ' ':
self.view.erase(edit, sublime.Region(line.begin(), line.begin() + 1))

View File

@@ -0,0 +1,61 @@
"""This module provides commands for easily moving between headilnes.
The feature is borrowed from [Org-mode](http://org-mode.org).
"""
# Author: Muchenxuan Tong <demon386@gmail.com>
import sublime
import sublime_plugin
try:
from . import headline
from .utilities import is_region_void
except ValueError:
import headline
from utilities import is_region_void
class HeadlineMoveCommand(sublime_plugin.TextCommand):
def run(self, edit, forward=True, same_level=True):
"""Move between headlines, forward or backward.
If same_level is true, only move to headline with the same level
or higher level.
"""
new_sel = []
if same_level:
level_type = headline.MATCH_PARENT
else:
level_type = headline.MATCH_ANY
for region in self.view.sel():
if same_level:
_, level = headline.headline_and_level_at_point(self.view,\
region.a,
search_above_and_down=True)
if level is None:
return
else:
level = headline.ANY_LEVEL
match_region, _ = headline.find_headline(self.view, \
region.a, \
level, \
forward, \
level_type, \
skip_headline_at_point=True,\
skip_folded=True)
if is_region_void(match_region):
return
new_sel.append(sublime.Region(match_region.a, match_region.a))
self.adjust_view(new_sel)
def adjust_view(self, new_sel):
self.view.sel().clear()
for region in new_sel:
self.view.sel().add(region)
self.view.show(region)

View File

@@ -0,0 +1,7 @@
Copyright (C) <2012> Muchenxuan Tong <demon386@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 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.

View File

@@ -0,0 +1 @@
{"url": "https://github.com/demon386/SmartMarkdown", "version": "2013.03.23.12.24.10", "description": "A plugin for facilitating editing markdown in Sublime Text 2. Features are borrowed from Org mode of Emacs."}

View File

@@ -0,0 +1,124 @@
"""This file is initially forked from
[SublimePandoc](https://github.com/jclement/SublimePandoc)
by [DanielMe](https://github.com/DanielMe/)
@todo naming convention should be foo_bar rather than fooBar.
@bug PDF export doesn't work in my Mac, gonna check it later.
2012-07-02: Muchenxuan Tong changed some stylical errors (with SublimeLinter)
"""
import sublime
import sublime_plugin
import webbrowser
import tempfile
import os
import os.path
import sys
import subprocess
from subprocess import PIPE
class PandocRenderCommand(sublime_plugin.TextCommand):
def is_enabled(self):
return self.view.score_selector(0, "text.html.markdown") > 0
def is_visible(self):
return True
def run(self, edit, target="pdf", open_after=True, save_result=False):
if target not in ["html", "docx", "pdf"]:
raise Exception("Format %s currently unsopported" % target)
self.setting = sublime.load_settings("SmartMarkdown.sublime-settings")
encoding = self.view.encoding()
if encoding == 'Undefined':
encoding = 'UTF-8'
elif encoding == 'Western (Windows 1252)':
encoding = 'windows-1252'
contents = self.view.substr(sublime.Region(0, self.view.size()))
contents = contents.encode(encoding)
file_name = self.view.file_name()
if file_name:
os.chdir(os.path.dirname(file_name))
# write buffer to temporary file
# This is useful because it means we don't need to save the buffer
tmp_md = tempfile.NamedTemporaryFile(delete=False, suffix=".md")
tmp_md.write(contents)
tmp_md.close()
# output file...
suffix = "." + target
if save_result:
output_name = os.path.splitext(self.view.file_name())[0] + suffix
if not self.view.file_name():
raise Exception("Please safe the buffer before trying to export with pandoc.")
else:
output = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
output.close()
output_name = output.name
args = self.pandoc_args(target)
self.run_pandoc(tmp_md.name, output_name, args)
if open_after:
self.open_result(output_name, target)
#os.unlink(tmp_md.name)
def run_pandoc(self, infile, outfile, args):
cmd = ['pandoc'] + args
cmd += [infile, "-o", outfile]
# Merge the path in settings
setting_path = self.setting.get("tex_path", [])
for p in setting_path:
if p not in os.environ["PATH"]:
os.environ["PATH"] += ":" + p
try:
# Use the current directory as working dir whenever possible
file_name = self.view.file_name()
if file_name:
working_dir = os.path.dirname(file_name)
p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE,
cwd=working_dir)
else:
p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE)
p.wait()
out, err = p.communicate()
if err:
raise Exception("Command: %s\n" % " ".join(cmd) + "\nErrors: " + err)
except Exception as e:
sublime.error_message("Fail to generate output.\n{0}".format(e))
def pandoc_args(self, target):
"""
Create a list of arguments for the pandoc command
depending on the target.
TODO: Actually do something sensible here
"""
# Merge the args in settings
args = self.setting.get("pandoc_args", [])
if target == "pdf":
args += self.setting.get("pandoc_args_pdf", [])
if target == "html":
args += self.setting.get("pandoc_args_html", []) + ['-t', 'html5']
if target == "docx":
args += self.setting.get("pandoc_args_docx", []) + ['-t', 'docx']
return args
def open_result(self, outfile, target):
if target == "html":
webbrowser.open_new_tab(outfile)
elif sys.platform == "win32":
os.startfile(outfile)
elif "mac" in sys.platform or "darwin" in sys.platform:
os.system("open %s" % outfile)
print(outfile)
elif "posix" in sys.platform or "linux" in sys.platform:
os.system("xdg-open %s" % outfile)

View File

@@ -0,0 +1,59 @@
# SmartMarkdown for Sublime Text 2 & 3
Author: Muchenxuan Tong (demon386@gmail.com)
## Introduction
The plugin is aimed at making editing Markdown in Sublime Text 2 easier and more powerful. Ideally, I hope we can bring several amazing features of [Org-mode](http://org-mode.org) of Emacs into Sublime Text.
## Done
- **Smart Headline folding / unfolding**. Right now you can fold / unfold headlines by pressing **TAB** on it. I assume you use the following formats: # Section; ## Subsection; ### Subsubsection ...
- **Global Headline Folding / unfolding**. **Shift+Tab** to Fold / Unfold all at any position.
- **Smart Order / Unordered list**. When editing lists, you can just press **ENTER** and this plugin will automatically continue the list. Once the content of the list becomes empty it will stop.
- **Move between headlines**.
- Use **Ctrl+c Ctrl+n** to move to the next headline (any level); **Ctrl+c Ctrl+p** to the previous one, for Mac. (**Ctrl+; Ctrl+n** and **Ctrl+; Ctrl+p** for Windows and Linux)
- Use **Ctrl+c Ctrl+f** to move to the next headline (same level or higher level); **Ctrl+c Ctrl+b** to the previous one, for Mac. (**Ctrl+; Ctrlf** and **Ctrl+; Ctrl+b** for Windows and Linux)
- **Adjust headline level** Added by [David Smith](https://github.com/djs070).
- **Super+Shift+,** for decreasing and **Super+Shift+.** for increasing headline levels.
- **Smart table**
- Currently, the smart table suppose only the Grid table format of [Pandoc](http://johnmacfarlane.net/pandoc/README.html). Use monospaced fonts, otherwise it would appear bizarre.
- The behavior is like the table in Org-mode. If you are unfamiliar with Org-mode, just use | (vertical line) to separate the column (e.g. | header1 | header 2 |), and use the **TAB** to reformat the table at point. Everything would fall into the place. Add +- and then press TAB for adding separator between rows. Add += and then press TAB for adding separator between header and the table body. Read the Grid tables section of [Pandoc Userg's Guide](http://johnmacfarlane.net/pandoc/README.html#tables) for more information.
- Use **TAB** to move forward a cell in table, **Shift+TAB** to move backward.
- Personally I plan to use grid table as a basis and add command for converting to other table formats if necessary.
- **Basic Pandoc integration with Pandoc** By integrating [SublimePandoc](https://github.com/jclement/SublimePandoc). Added by [DanielMe](https://github.com/DanielMe/).
- **Note**: If you need to generate PDF output, please make sure you have pdflatex available ([MacTeX](http://www.tug.org/mactex/2012/) for Mac, or TeX Live for other OS). Please also specify "tex_path" in the package settings (Preference - Package Settings - SmartMarkdown - Settings - User (see Settings - Default as an example.))
## Todo
- **Embeded R & Python Code for reproducible research**
- **Better Pandoc integration** Actual support for different Pandoc command line options etc.
- ...
## What's new
### v0.2: Support for Sublime Text 3 (added by [UNOwen](https://github.com/UNOwen).)
### v0.1.6: Add support and bindings for headline level changing. (added by [David Smith](https://github.com/djs070).) The key bindings are: **Super+Shift+,** for decreasing and **Super+Shift+.** for increasing.
### v0.1.5: Basic smart table (grid table) support added. Basic Pandoc intergration (added by [DanielMe](https://github.com/DanielMe/).)
### v0.1.3: Add support for global headling folding / unfolding.
### v0.1.2: Move between headlines supported!
- Use **Ctrl+c Ctrl+n** to move to the next headline (any level); **Ctrl+c Ctrl+p** to the previous one.
- Use **Ctrl+c Ctrl+f** to move to the next headline (same level or higher level); **Ctrl+c Ctrl+b** to the previous one.
- Fixed a bug on bullet list. Thanks to quodlibet (fixed in v0.1.1).
### v0.1.0: Created!
- Smart Headline folding / unfolding is supported.
- Smart Lists is supported.
## For Developers
- Whenever possible, please obey the [PEP 8](http://www.python.org/dev/peps/pep-0008/) style guide. This can be checked easily with the plugin SublimeLinter.
- git-flow is recommended (but not enforced) as a development work flow. For instruction please read [Why aren't you using git-flow?](http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/). To adapt it, a command line tool [gitflow](https://github.com/nvie/gitflow/) is highly recommended.
- Please work on the develop branch, it's newer than master. the master branch is for users.
# License
The plugin is licensed under the MIT license.
Copyright (C) <2012> Muchenxuan Tong <demon386@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 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.

View File

@@ -0,0 +1,195 @@
"""Smart folding is a feature borrowed from [Org-mode](http://org-mode.org).
It enables folding / unfolding the headlines by simply pressing TAB on headlines.
Global headline folding / unfolding is recommended to be trigged by Shift + TAB,
at anywhere.
"""
# Author: Muchenxuan Tong <demon386@gmail.com>
import re
import sublime
import sublime_plugin
try:
from . import headline
from .utilities import is_region_void
except ValueError:
import headline
from utilities import is_region_void
HEADLINE_PATTERN = re.compile(r'^(#+)\s.*')
class SmartFoldingCommand(sublime_plugin.TextCommand):
"""Smart folding is used to fold / unfold headline at the point.
It's designed to bind to TAB key, and if the current line is not
a headline, a \t would be inserted.
"""
def run(self, edit):
ever_matched = False
for region in self.view.sel():
matched = self.fold_or_unfold_headline_at_point(region.a)
if matched:
ever_matched = True
if not ever_matched:
for r in self.view.sel():
self.view.insert(edit, r.a, '\t')
self.view.show(r)
def fold_or_unfold_headline_at_point(self, from_point):
"""Smart folding of the current headline.
Unfold only when it's totally folded. Otherwise fold it.
"""
_, level = headline.headline_and_level_at_point(self.view,
from_point)
# Not a headline, cancel
if level is None or not headline.is_scope_headline(self.view, from_point):
return False
content_region = headline.region_of_content_of_headline_at_point(self.view,
from_point)
# If the content is empty, Nothing needs to be done.
if content_region is None:
# Return True because there is a headline anyway.
return True
# Check if content region is folded to decide the action.
if self.is_region_totally_folded(content_region):
self.unfold_yet_fold_subheads(content_region, level)
else:
self.view.fold(content_region)
return True
def is_region_totally_folded(self, region):
"""Decide if the region is folded. Treat empty region as folded."""
if (region is None) or (region.a == region.b):
return True
for i in self.view.folded_regions():
if i.contains(region):
return True
return False
def unfold_yet_fold_subheads(self, region, level):
"""Unfold the region while keeping the subheadlines folded."""
## First unfold all
self.view.unfold(region)
## Fold subheads
child_headline_region, _ = headline.find_headline(self.view, region.a, level, True, \
headline.MATCH_CHILD)
while (not is_region_void(child_headline_region) and child_headline_region.b <= region.b):
child_content_region = headline.region_of_content_of_headline_at_point(self.view,
child_headline_region.a)
if child_content_region is not None:
self.view.fold(child_content_region)
search_start_point = child_content_region.b
else:
search_start_point = child_headline_region.b
child_headline_region, _ = headline.find_headline(self.view, \
search_start_point, level, True, \
headline.MATCH_CHILD,
skip_headline_at_point=True)
class GlobalFoldingCommand(SmartFoldingCommand):
"""Global folding / unfolding headlines at any point.
Unfold only when top-level headlines are totally folded.
Otherwise fold.
"""
def run(self, edit):
if self.is_global_folded():
# Unfold all
self.unfold_all()
else:
self.fold_all()
def is_global_folded(self):
"""Check if all headlines are folded.
"""
region, level = headline.find_headline(self.view, 0, \
headline.ANY_LEVEL, True)
# Treating no heeadline as folded, since unfolded all makes
# no harm in this situation.
if is_region_void(region):
return True
point = region.a
# point can be zero
while (point is not None and region):
region = headline.region_of_content_of_headline_at_point(self.view, \
point)
if not is_region_void(region):
point = region.b
if not self.is_region_totally_folded(region):
return False
else:
region, level = headline.find_headline(self.view, point, \
headline.ANY_LEVEL, \
True,
skip_headline_at_point=True)
if not is_region_void(region):
point = region.a
return True
def unfold_all(self):
self.view.unfold(sublime.Region(0, self.view.size()))
self.view.show(self.view.sel()[0])
def fold_all(self):
region, level = headline.find_headline(self.view, \
0, \
headline.ANY_LEVEL, \
True)
# At this point, headline region is sure to exist, otherwise it would be
# treated as gobal folded. (self.is_global_folded() would return True)
point = region.a
# point can be zero
while (point is not None and region):
region = headline.region_of_content_of_headline_at_point(self.view, \
point)
if not is_region_void(region):
point = region.b
self.view.fold(region)
region, level = headline.find_headline(self.view, point, \
headline.ANY_LEVEL,
True, \
skip_headline_at_point=True)
if not is_region_void(region):
point = region.a
self.adjust_cursors_and_view()
def adjust_cursors_and_view(self):
"""After folder, adjust cursors and view.
If the current point is inside the folded region, move it move
otherwise it's easy to perform some unintentional editing.
"""
folded_regions = self.view.folded_regions()
new_sel = []
for r in self.view.sel():
for folded in folded_regions:
if folded.contains(r):
new_sel.append(sublime.Region(folded.b, folded.b))
break
else:
new_sel.append(r)
self.view.sel().clear()
for r in new_sel:
self.view.sel().add(r)
self.view.show(r)

View File

@@ -0,0 +1,57 @@
"""Smart list is used to automatially continue the current list."""
# Author: Muchenxuan Tong <demon386@gmail.com>
import re
import sublime
import sublime_plugin
ORDER_LIST_PATTERN = re.compile(r"(\s*)(\d+)(\.\s+)\S+")
UNORDER_LIST_PATTERN = re.compile(r"(\s*[-+\**]+)(\s+)\S+")
EMPTY_LIST_PATTERN = re.compile(r"(\s*([-+\**]|\d+\.+))\s+$")
class SmartListCommand(sublime_plugin.TextCommand):
def run(self, edit):
for region in self.view.sel():
line_region = self.view.line(region)
# the content before point at the current line.
before_point_region = sublime.Region(line_region.a,
region.a)
before_point_content = self.view.substr(before_point_region)
# Disable smart list when folded.
folded = False
for i in self.view.folded_regions():
if i.contains(before_point_region):
self.view.insert(edit, region.a, '\n')
folded = True
if folded:
break
match = EMPTY_LIST_PATTERN.match(before_point_content)
if match:
self.view.erase(edit, before_point_region)
break
match = ORDER_LIST_PATTERN.match(before_point_content)
if match:
insert_text = match.group(1) + \
str(int(match.group(2)) + 1) + \
match.group(3)
self.view.insert(edit, region.a, "\n" + insert_text)
break
match = UNORDER_LIST_PATTERN.match(before_point_content)
if match:
insert_text = match.group(1) + match.group(2)
self.view.insert(edit, region.a, "\n" + insert_text)
break
self.view.insert(edit, region.a, '\n')
self.adjust_view()
def adjust_view(self):
for region in self.view.sel():
self.view.show(region)

View File

@@ -0,0 +1,87 @@
"""Smart is inspired by the Table behavior of Org-mode.
Markdown itself doesn't support grid table, yet pandoc does.
@todo: add a key binding for converting grid table to the simple one
"""
# Author: Muchenxuan Tong <demon386@gmail.com>
# LICENSE: MIT
import sublime
import sublime_plugin
try:
from . import table
except ValueError:
import table
class SmartTable(sublime_plugin.TextCommand):
def run(self, edit, forward=True):
new_sel = []
for r in self.view.sel():
point = r.a
for i in self.view.folded_regions():
if i.contains(sublime.Region(point, point)):
return
t = table.convert_table_at_point_as_list(self.view, point)
t = table.reformat_table_list(t)
t_str = table.convert_table_list_to_str(t)
# Both are 0-based
cur_row_num, cur_col_num = table.get_point_row_and_col(self.view, point)
table_row_num = len(t)
line_num, _ = self.view.rowcol(point)
start_line_num = line_num - cur_row_num
start_point = self.view.text_point(line_num - cur_row_num, 0)
end_line_num = line_num + table_row_num - cur_row_num - 1
end_line_start_point = self.view.text_point(end_line_num, 0)
end_point = self.view.line(end_line_start_point).b
# Erase the previous table region, use the new one for substitution.
self.view.erase(edit, sublime.Region(start_point, end_point))
self.view.insert(edit, start_point, t_str)
if forward:
if cur_col_num is None or cur_col_num >= len(t[0]) - 1:
line_num += 1
while(table.is_line_separator(self.view, line_num)):
line_num += 1
cur_col_num = 0
else:
cur_col_num += 1
else:
if cur_col_num is None or cur_col_num <= 0:
line_num -= 1
while(table.is_line_separator(self.view, line_num)):
line_num -= 1
cur_col_num = len(t[0]) - 1
else:
cur_col_num -= 1
# Add a new line when at the end of the table.
if line_num < start_line_num or line_num > end_line_num:
col_pos = 0
if line_num > end_line_num:
self.view.insert(edit, self.view.text_point(line_num, 0), "\n")
else:
col_pos = self.calculate_col_point(t, cur_col_num)
new_sel.append(self.view.text_point(line_num, col_pos))
self.view.sel().clear()
for r in new_sel:
self.view.sel().add(r)
self.view.show(r)
def calculate_col_point(self, formatted_table, col_num):
i = 0
while table.SEPARATOR_PATTERN.match(formatted_table[i][0]):
i += 1
cols_length = [len(j) for j in formatted_table[i]]
point = 2
for i in range(col_num):
point += cols_length[i] + 3
return point

View File

@@ -0,0 +1,207 @@
"""Utilities function for working with grid table of Pandoc
Terminologies
- Table list :: This is not a list of tables, but rather converting the table as
a nested python list. Each row is a sub-list in the table list.
"""
# Author: Muchenxuan Tong <demon386@gmail.com>
# LICENSE: MIT
import re
import copy
import sublime
try:
from . import utilities
except ValueError:
import utilities
TABLE_PATTERN = re.compile(r"\s*\|")
SEPARATOR_PATTERN = re.compile(r"\s*(\+[=-])")
def convert_table_at_point_as_list(view, from_point):
"""Get the table at the point.
Transform the table to python list.
Returns
-------
table: list
A nested list representing the table.
indent: "str" (@todo not impelmented yet)
String of indentation, used in every row.
"""
table_above = convert_table_above_or_below_as_list(view, from_point, above=True)
table_below = convert_table_above_or_below_as_list(view, from_point, above=False)
row_at_point = convert_row_at_point_as_list(view, from_point)
table = table_above + [row_at_point] + table_below
return table
def convert_table_above_or_below_as_list(view, from_point, above):
"""Convert the table above the point as python list.
Returns
-------
table: list
A nested list representing the table.
"""
line_num, _ = view.rowcol(from_point)
line_num += - 1 if above else 1
line_text = utilities.text_at_line(view, line_num)
table = []
while line_text and (TABLE_PATTERN.match(line_text) or
SEPARATOR_PATTERN.match(line_text)):
table.append(_convert_row_text_as_list(line_text))
line_num += -1 if above else 1
line_text = utilities.text_at_line(view, line_num)
if above:
table = table[::-1]
return table
def convert_row_at_point_as_list(view, from_point):
"""Convert the row at point as a python list.
"""
line_num, _ = view.rowcol(from_point)
line_text = utilities.text_at_line(view, line_num)
return _convert_row_text_as_list(line_text)
def _convert_row_text_as_list(row_text):
"""Convert the text of a row into a python list.
Paramters
---------
row_text: str
The text of the row.
Returns
-------
lst: list
The converted list.
"""
split_row = row_text.split("|")
if len(split_row) > 2 and split_row[-1].strip() == "":
lst = split_row[1:-1]
else:
lst = split_row[1:]
match = SEPARATOR_PATTERN.match(row_text)
if match:
lst = [match.group(1)]
return [i.strip() for i in lst]
def reformat_table_list(table):
"""Reformat & align the table list.
After this, every column is of the same length,
and every row is of the same number of column.
"""
cols_num = max([len(row) for row in table])
cols_length = _get_cols_length(table, cols_num)
new_table = []
for row in table:
new_row = []
if not SEPARATOR_PATTERN.match(row[0]):
for i in range(cols_num):
try:
col = row[i]
new_row.append(col + " " * (cols_length[i] - len(col)))
except:
new_row.append(" " * cols_length[i])
else:
marker = row[0][1]
for i in range(cols_num):
new_row.append(marker * (cols_length[i] + 2))
# Add a mark for recognization
new_row[0] = "+" + new_row[0]
new_table.append(new_row)
return new_table
def convert_table_list_to_str(table):
"""Convert the python list to str for outputing.
"""
table_str = ""
table = copy.deepcopy(table)
for row in table:
if SEPARATOR_PATTERN.match(row[0]):
row[0] = row[0][1:] # Remove the mark added in reformat_table_list
row_str = "+"
for col_str in row:
row_str += col_str + "+"
else:
row_str = "|"
for col_str in row:
row_str += " " + col_str + " " + "|"
table_str += row_str + "\n"
return table_str[:-1]
def _get_cols_length(table, cols_num):
"""Return the max length of every columns.
"""
cols_length = [0] * cols_num
for row in table:
for (i, col) in enumerate(row):
col_len = len(col)
if col_len > cols_length[i]:
cols_length[i] = col_len
return cols_length
def get_point_row_and_col(view, from_point):
"""Return the row and col the current point is in the table.
"""
line_num, _ = view.rowcol(from_point)
line_num -= 1
line_text = utilities.text_at_line(view, line_num)
row_num = 0
while line_text and (TABLE_PATTERN.match(line_text) or
SEPARATOR_PATTERN.match(line_text)):
row_num += 1
line_num -= 1
line_text = utilities.text_at_line(view, line_num)
line_start_point = view.line(from_point)
region = sublime.Region(line_start_point.a, from_point)
precedding_text = view.substr(region)
split_row = precedding_text.split("|")
if len(split_row) >= 2:
col_num = len(split_row) - 2
elif split_row[0].strip() == "":
col_num = -1
else:
col_num = None
return (row_num, col_num)
def is_line_separator(view, line_num):
"""Check if the current line is a separator.
"""
text = utilities.text_at_line(view, line_num)
if text and SEPARATOR_PATTERN.match(text):
return True
else:
return False

View File

@@ -0,0 +1,23 @@
"""Some utility functions for working with sublime.
"""
def text_at_line(view, line_num):
"""Return the content at line. None if out of boundary."""
if line_num < 0:
return None
max_line_num, _ = view.rowcol(view.size())
if line_num > max_line_num:
return None
point = view.text_point(line_num, 0)
line_region = view.line(point)
return view.substr(line_region)
def is_region_void(region):
if region == None:
return True
if region.a == -1 and region.b == -1:
return True
return False