mirror of
https://github.com/kennethreitz-archive/kcode.tmbundle.git
synced 2026-06-05 07:36:13 +00:00
All set. Best of Sparkup + Zen Coding for now
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>beforeRunningCommand</key>
|
||||
<string>nop</string>
|
||||
<key>command</key>
|
||||
<string>#!/usr/bin/env python
|
||||
import sys; import os; sys.path.append(os.getenv('TM_BUNDLE_SUPPORT')); import sparkup
|
||||
|
||||
# You may change these options to your liking.
|
||||
# Those starting with # are comments (disabled).
|
||||
options = {
|
||||
'textmate': True,
|
||||
'no-last-newline': True,
|
||||
#'start-guide-format': 'Begin %s',
|
||||
#'end-guide-format': 'End %s',
|
||||
}
|
||||
|
||||
sparkup.Router().start(options=options)</string>
|
||||
<key>fallbackInput</key>
|
||||
<string>line</string>
|
||||
<key>input</key>
|
||||
<string>selection</string>
|
||||
<key>keyEquivalent</key>
|
||||
<string>@e</string>
|
||||
<key>name</key>
|
||||
<string>Sizzle Expand</string>
|
||||
<key>output</key>
|
||||
<string>insertAsSnippet</string>
|
||||
<key>scope</key>
|
||||
<string>text.html</string>
|
||||
<key>uuid</key>
|
||||
<string>73A48D2B-D843-42A1-A288-0D1A6380043B</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>beforeRunningCommand</key>
|
||||
<string>nop</string>
|
||||
<key>input</key>
|
||||
<string>selection</string>
|
||||
<key>name</key>
|
||||
<string>Sparkup Expand</string>
|
||||
<key>output</key>
|
||||
<string>replaceSelectedText</string>
|
||||
<key>uuid</key>
|
||||
<string>EF1F0ADB-1A4D-411A-9BDB-FF9837072CB2</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>beforeRunningCommand</key>
|
||||
<string>nop</string>
|
||||
<key>command</key>
|
||||
<string>cd "$TM_BUNDLE_PATH"
|
||||
|
||||
echo "<pre>"
|
||||
[ -d ".git" ] && git pull
|
||||
[ ! -d .git ] && echo "You must install the bundle using Git in order to update via this command. More info: http://github.com/kennethreitz/kCode.tmBundle"
|
||||
|
||||
osascript -e 'tell app "TextMate" to reload bundles'
|
||||
|
||||
echo "</pre>"</string>
|
||||
<key>input</key>
|
||||
<string>selection</string>
|
||||
<key>name</key>
|
||||
<string>Update Bundle via Git</string>
|
||||
<key>output</key>
|
||||
<string>discard</string>
|
||||
<key>uuid</key>
|
||||
<string>FC3E137B-6E92-40C9-BAF6-ED13182D7269</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>commands</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>argument</key>
|
||||
<dict>
|
||||
<key>beforeRunningCommand</key>
|
||||
<string>nop</string>
|
||||
<key>command</key>
|
||||
<string>python "$TM_BUNDLE_SUPPORT"/balance_tag.py</string>
|
||||
<key>input</key>
|
||||
<string>document</string>
|
||||
<key>keyEquivalent</key>
|
||||
<string>@d</string>
|
||||
<key>name</key>
|
||||
<string>start</string>
|
||||
<key>output</key>
|
||||
<string>showAsTooltip</string>
|
||||
<key>uuid</key>
|
||||
<string>zen-balance-outward-1</string>
|
||||
</dict>
|
||||
<key>command</key>
|
||||
<string>executeCommandWithOptions:</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>command</key>
|
||||
<string>findNext:</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>keyEquivalent</key>
|
||||
<string>@d</string>
|
||||
<key>name</key>
|
||||
<string>Balance Tag</string>
|
||||
<key>uuid</key>
|
||||
<string>zen-balance-outward-2</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>commands</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>argument</key>
|
||||
<dict>
|
||||
<key>beforeRunningCommand</key>
|
||||
<string>nop</string>
|
||||
<key>command</key>
|
||||
<string>python "$TM_BUNDLE_SUPPORT"/remove_tag.py</string>
|
||||
<key>input</key>
|
||||
<string>document</string>
|
||||
<key>keyEquivalent</key>
|
||||
<string>@K</string>
|
||||
<key>name</key>
|
||||
<string>start</string>
|
||||
<key>output</key>
|
||||
<string>showAsTooltip</string>
|
||||
<key>uuid</key>
|
||||
<string>zen-remove-tag-1</string>
|
||||
</dict>
|
||||
<key>command</key>
|
||||
<string>executeCommandWithOptions:</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>command</key>
|
||||
<string>findNext:</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>argument</key>
|
||||
<dict>
|
||||
<key>beforeRunningCommand</key>
|
||||
<string>nop</string>
|
||||
<key>command</key>
|
||||
<string>python "$TM_BUNDLE_SUPPORT"/applescript.py</string>
|
||||
<key>input</key>
|
||||
<string>document</string>
|
||||
<key>keyEquivalent</key>
|
||||
<string>@K</string>
|
||||
<key>name</key>
|
||||
<string>start</string>
|
||||
<key>output</key>
|
||||
<string>showAsTooltip</string>
|
||||
<key>uuid</key>
|
||||
<string>zen-remove-tag-2</string>
|
||||
</dict>
|
||||
<key>command</key>
|
||||
<string>executeCommandWithOptions:</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>keyEquivalent</key>
|
||||
<string>$@</string>
|
||||
<key>name</key>
|
||||
<string>Remove Tag</string>
|
||||
<key>uuid</key>
|
||||
<string>zen-remove-tag-3</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from zen_editor import ZenEditor
|
||||
|
||||
editor = ZenEditor()
|
||||
editor.run_applescript()
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from zencoding import zen_core as zen_coding
|
||||
from zen_editor import ZenEditor
|
||||
|
||||
editor = ZenEditor()
|
||||
zen_coding.run_action('match_pair', editor)
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from zencoding import zen_core as zen_coding
|
||||
from zen_editor import ZenEditor
|
||||
|
||||
editor = ZenEditor()
|
||||
zen_coding.run_action('remove_tag', editor)
|
||||
+1087
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
High-level editor interface that communicates with TextMate editor.
|
||||
In order to work correctly, you should set set the commands
|
||||
input to “Entire Document”
|
||||
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
import os
|
||||
import sys
|
||||
import zencoding.zen_core as zen
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
class ZenEditor():
|
||||
def __init__(self, context=None):
|
||||
self._content = ''
|
||||
"Editor's content"
|
||||
|
||||
self.apple_script = os.path.join(os.getenv('TM_BUNDLE_SUPPORT'), 'pasteboard.scpt')
|
||||
zen.set_newline(os.getenv('TM_LINE_ENDING', zen.get_newline()))
|
||||
self.set_context(context)
|
||||
|
||||
def _get_selected_text(self):
|
||||
"Returns selected text"
|
||||
return os.getenv('TM_SELECTED_TEXT', '')
|
||||
|
||||
def set_context(self, context=None):
|
||||
"""
|
||||
Setup underlying editor context. You should call this method
|
||||
<code>before</code> using any Zen Coding action.
|
||||
@param context: context object
|
||||
"""
|
||||
self._content = sys.stdin.read()
|
||||
|
||||
def get_selection_range(self):
|
||||
"""
|
||||
Returns character indexes of selected text
|
||||
@return: list of start and end indexes
|
||||
"""
|
||||
line_num = int(os.getenv('TM_INPUT_START_LINE', os.getenv('TM_LINE_NUMBER', 1)))
|
||||
head_lines = self.get_content().splitlines(True)[0:line_num - 1]
|
||||
head_len = len(''.join(head_lines))
|
||||
start, end = self.get_current_line_range()
|
||||
|
||||
return start + head_len, end + head_len
|
||||
|
||||
|
||||
def create_selection(self, start, end=None):
|
||||
"""
|
||||
Creates selection from <code>start</code> to <code>end</code> character
|
||||
indexes. If <code>end</code> is ommited, this method should place caret
|
||||
and <code>start</code> index
|
||||
"""
|
||||
self.set_caret_pos(start)
|
||||
if end is not None:
|
||||
selected_text = self.get_content()[start:end]
|
||||
# copy selected text to Mac OS' pasteboard to use it
|
||||
# as a part of macros sequence for 'find next' action
|
||||
subprocess.Popen(['pbcopy', '-pboard', 'find'], stdin=subprocess.PIPE).communicate(selected_text)
|
||||
|
||||
def get_current_line_range(self):
|
||||
"""
|
||||
Returns current line's start and end indexes
|
||||
@return: list of start and end indexes
|
||||
@example
|
||||
start, end = zen_editor.get_current_line_range();
|
||||
print('%s, %s' % (start, end))
|
||||
"""
|
||||
start = int(os.getenv('TM_INPUT_START_LINE_INDEX', os.getenv('TM_LINE_INDEX', 0)))
|
||||
return start, start + len(self._get_selected_text())
|
||||
|
||||
def get_caret_pos(self):
|
||||
""" Returns current caret position """
|
||||
return self.get_selection_range()[0]
|
||||
|
||||
def set_caret_pos(self, pos):
|
||||
"""
|
||||
Set new caret position
|
||||
@type pos: int
|
||||
"""
|
||||
# figure out line and column vars
|
||||
head = zen.split_by_lines(self.get_content()[0:pos])
|
||||
line = max(len(head), 1)
|
||||
column = pos - len(zen.get_newline().join(head[0:-1]))
|
||||
|
||||
subprocess.Popen(['open', 'txmt://open/?line=%d&column=%d' % (line, column)]).communicate()
|
||||
|
||||
def get_current_line(self):
|
||||
"""
|
||||
Returns content of current line
|
||||
@return: str
|
||||
"""
|
||||
return os.getenv('TM_CURRENT_LINE', '')
|
||||
|
||||
def replace_content(self, value, start=None, end=None):
|
||||
"""
|
||||
Replace editor's content or it's part (from <code>start</code> to
|
||||
<code>end</code> index). If <code>value</code> contains
|
||||
<code>caret_placeholder</code>, the editor will put caret into
|
||||
this position. If you skip <code>start</code> and <code>end</code>
|
||||
arguments, the whole target's content will be replaced with
|
||||
<code>value</code>.
|
||||
|
||||
If you pass <code>start</code> argument only,
|
||||
the <code>value</code> will be placed at <code>start</code> string
|
||||
index of current content.
|
||||
|
||||
If you pass <code>start</code> and <code>end</code> arguments,
|
||||
the corresponding substring of current target's content will be
|
||||
replaced with <code>value</code>
|
||||
@param value: Content you want to paste
|
||||
@type value: str
|
||||
@param start: Start index of editor's content
|
||||
@type start: int
|
||||
@param end: End index of editor's content
|
||||
@type end: int
|
||||
"""
|
||||
# For content replacement we need to use macro syntaxt.
|
||||
# First, create selection and then write AppleScript file that
|
||||
# will replace selected text with new one
|
||||
if start is None: start = 0
|
||||
if end is None: end = len(self.get_content())
|
||||
self.create_selection(start, end)
|
||||
|
||||
value = self.add_placeholders(value)
|
||||
|
||||
fp = open(self.apple_script, 'w')
|
||||
fp.write('tell application "TextMate" to insert "%s" with as snippet' % (value.replace('\\', '\\\\').replace('"', '\\"'),))
|
||||
fp.close()
|
||||
|
||||
|
||||
def get_content(self):
|
||||
"""
|
||||
Returns editor's content
|
||||
@return: str
|
||||
"""
|
||||
return self._content
|
||||
|
||||
def get_syntax(self):
|
||||
"""
|
||||
Returns current editor's syntax mode
|
||||
@return: str
|
||||
"""
|
||||
scope = os.getenv('TM_SCOPE')
|
||||
default_type = 'html'
|
||||
doc_type = None
|
||||
try:
|
||||
if 'xsl' in scope:
|
||||
doc_type = 'xsl'
|
||||
else:
|
||||
doc_type = re.findall(r'\bhtml|css|xml|haml\b', scope)[-1]
|
||||
except:
|
||||
doc_type = default_type
|
||||
|
||||
if not doc_type: doc_type = default_type
|
||||
|
||||
return doc_type
|
||||
|
||||
def get_profile_name(self):
|
||||
"""
|
||||
Returns current output profile name (@see zen_coding#setup_profile)
|
||||
@return {String}
|
||||
"""
|
||||
return 'xhtml'
|
||||
|
||||
def run_applescript(self):
|
||||
"""
|
||||
TextMate-specific action that calls AppleScript defined in
|
||||
<code>replace_content()</code> method which replaces selected text
|
||||
with new one
|
||||
"""
|
||||
if os.path.exists(self.apple_script):
|
||||
subprocess.Popen(['osascript', self.apple_script], stderr=subprocess.PIPE).communicate()
|
||||
os.remove(self.apple_script)
|
||||
|
||||
def prompt(self, title):
|
||||
"""
|
||||
Prompt user with CocoaDialog
|
||||
@param title: Popup title
|
||||
@return: str
|
||||
"""
|
||||
args = ['CocoaDialog', 'standard-inputbox', '--title', title, '‑‑no‑newline']
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()
|
||||
|
||||
output = p[0].splitlines()
|
||||
if output[0] == '2' or not output[1]:
|
||||
return None
|
||||
else:
|
||||
return output[1]
|
||||
|
||||
def add_placeholders(self, text):
|
||||
_ix = [0]
|
||||
|
||||
def get_ix(m):
|
||||
_ix[0] += 1
|
||||
return '$%s' % _ix[0]
|
||||
|
||||
text = re.sub(r'\$', '\\$', text)
|
||||
return re.sub(zen.get_caret_placeholder(), get_ix, text)
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
# import all filters
|
||||
__sub_modules = []
|
||||
__prefix = 'zencoding.filters'
|
||||
__filter_dir = os.path.dirname(__file__)
|
||||
sys.path.append(__filter_dir)
|
||||
|
||||
filter_map = {}
|
||||
for file in os.listdir(__filter_dir):
|
||||
name, ext = os.path.splitext(file)
|
||||
if ext.lower() == '.py':
|
||||
__sub_modules.append(name)
|
||||
|
||||
__filters = __import__(__prefix, globals(), locals(), __sub_modules)
|
||||
for key in dir(__filters):
|
||||
__module = getattr(__filters, key)
|
||||
if hasattr(__module, '__name__') and __module.__name__.startswith(__prefix + '.') and hasattr(__module, 'process'):
|
||||
if hasattr(__module, 'alias'):
|
||||
filter_map[__module.alias] = __module.process
|
||||
else:
|
||||
filter_map[__module.__name__[len(__prefix) + 1:]] = __module.process
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Comment important tags (with 'id' and 'class' attributes)
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
from zencoding import zen_core as zen_coding
|
||||
|
||||
alias = 'c'
|
||||
"Filter name alias (if not defined, ZC will use module name)"
|
||||
|
||||
def add_comments(node, i):
|
||||
|
||||
"""
|
||||
Add comments to tag
|
||||
@type node: ZenNode
|
||||
@type i: int
|
||||
"""
|
||||
id_attr = node.get_attribute('id')
|
||||
class_attr = node.get_attribute('class')
|
||||
nl = zen_coding.get_newline()
|
||||
|
||||
if id_attr or class_attr:
|
||||
comment_str = ''
|
||||
padding = node.parent and node.parent.padding or ''
|
||||
if id_attr: comment_str += '#' + id_attr
|
||||
if class_attr: comment_str += '.' + class_attr
|
||||
|
||||
node.start = node.start.replace('<', '<!-- ' + comment_str + ' -->' + nl + padding + '<', 1)
|
||||
node.end = node.end.replace('>', '>' + nl + padding + '<!-- /' + comment_str + ' -->', 1)
|
||||
|
||||
# replace counters
|
||||
node.start = zen_coding.replace_counter(node.start, i + 1)
|
||||
node.end = zen_coding.replace_counter(node.end, i + 1)
|
||||
|
||||
def process(tree, profile):
|
||||
if profile['tag_nl'] is False:
|
||||
return tree
|
||||
|
||||
for i, item in enumerate(tree.children):
|
||||
if item.is_block():
|
||||
add_comments(item, i)
|
||||
process(item, profile)
|
||||
|
||||
return tree
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Filter for escaping unsafe XML characters: <, >, &
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
import re
|
||||
|
||||
alias = 'e'
|
||||
"Filter name alias (if not defined, ZC will use module name)"
|
||||
|
||||
char_map = {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'&': '&'
|
||||
}
|
||||
|
||||
re_chars = re.compile(r'[<>&]')
|
||||
|
||||
def escape_chars(text):
|
||||
return re_chars.sub(lambda m: char_map[m.group(0)], text)
|
||||
|
||||
def process(tree, profile=None):
|
||||
for item in tree.children:
|
||||
item.start = escape_chars(item.start)
|
||||
item.end = escape_chars(item.end)
|
||||
|
||||
process(item)
|
||||
|
||||
return tree
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Format CSS properties: add space after property name:
|
||||
padding:0; -> padding: 0;
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
import re
|
||||
|
||||
alias = 'fc'
|
||||
"Filter name alias (if not defined, ZC will use module name)"
|
||||
|
||||
re_css_prop = re.compile(r'([\w\-]+\s*:)\s*')
|
||||
|
||||
def process(tree, profile):
|
||||
for item in tree.children:
|
||||
# CSS properties are always snippets
|
||||
if item.type == 'snippet':
|
||||
item.start = re_css_prop.sub(r'\1 ', item.start)
|
||||
|
||||
process(item, profile)
|
||||
|
||||
return tree
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Generic formatting filter: creates proper indentation for each tree node,
|
||||
placing "%s" placeholder where the actual output should be. You can use
|
||||
this filter to preformat tree and then replace %s placeholder to whatever you
|
||||
need. This filter should't be called directly from editor as a part
|
||||
of abbreviation.
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
"""
|
||||
import re
|
||||
from zencoding import zen_core as zen_coding
|
||||
|
||||
alias = '_format'
|
||||
"Filter name alias (if not defined, ZC will use module name)"
|
||||
|
||||
child_token = '${child}'
|
||||
placeholder = '%s'
|
||||
|
||||
def get_newline():
|
||||
return zen_coding.get_newline()
|
||||
|
||||
|
||||
def get_indentation():
|
||||
return zen_coding.get_indentation()
|
||||
|
||||
def has_block_sibling(item):
|
||||
"""
|
||||
Test if passed node has block-level sibling element
|
||||
@type item: ZenNode
|
||||
@return: bool
|
||||
"""
|
||||
return item.parent and item.parent.has_block_children()
|
||||
|
||||
def is_very_first_child(item):
|
||||
"""
|
||||
Test if passed itrem is very first child of the whole tree
|
||||
@type tree: ZenNode
|
||||
"""
|
||||
return item.parent and not item.parent.parent and not item.previous_sibling
|
||||
|
||||
def should_break_line(node, profile):
|
||||
"""
|
||||
Need to add line break before element
|
||||
@type node: ZenNode
|
||||
@type profile: dict
|
||||
@return: bool
|
||||
"""
|
||||
if not profile['inline_break']:
|
||||
return False
|
||||
|
||||
# find toppest non-inline sibling
|
||||
while node.previous_sibling and node.previous_sibling.is_inline():
|
||||
node = node.previous_sibling
|
||||
|
||||
if not node.is_inline():
|
||||
return False
|
||||
|
||||
# calculate how many inline siblings we have
|
||||
node_count = 1
|
||||
node = node.next_sibling
|
||||
while node:
|
||||
if node.is_inline():
|
||||
node_count += 1
|
||||
else:
|
||||
break
|
||||
node = node.next_sibling
|
||||
|
||||
return node_count >= profile['inline_break']
|
||||
|
||||
def should_break_child(node, profile):
|
||||
"""
|
||||
Need to add newline because <code>item</code> has too many inline children
|
||||
@type node: ZenNode
|
||||
@type profile: dict
|
||||
@return: bool
|
||||
"""
|
||||
# we need to test only one child element, because
|
||||
# has_block_children() method will do the rest
|
||||
return node.children and should_break_line(node.children[0], profile)
|
||||
|
||||
def process_snippet(item, profile, level=0):
|
||||
"""
|
||||
Processes element with <code>snippet</code> type
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@param level: Depth level
|
||||
@type level: int
|
||||
"""
|
||||
data = item.source.value;
|
||||
|
||||
if not data:
|
||||
# snippet wasn't found, process it as tag
|
||||
return process_tag(item, profile, level)
|
||||
|
||||
item.start = placeholder
|
||||
item.end = placeholder
|
||||
|
||||
padding = item.parent.padding if item.parent else get_indentation() * level
|
||||
|
||||
if not is_very_first_child(item):
|
||||
item.start = get_newline() + padding + item.start
|
||||
|
||||
# adjust item formatting according to last line of <code>start</code> property
|
||||
parts = data.split(child_token)
|
||||
lines = zen_coding.split_by_lines(parts[0] or '')
|
||||
padding_delta = get_indentation()
|
||||
|
||||
if len(lines) > 1:
|
||||
m = re.match(r'^(\s+)', lines[-1])
|
||||
if m:
|
||||
padding_delta = m.group(1)
|
||||
|
||||
item.padding = padding + padding_delta
|
||||
|
||||
return item
|
||||
|
||||
def process_tag(item, profile, level=0):
|
||||
"""
|
||||
Processes element with <code>tag</code> type
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@param level: Depth level
|
||||
@type level: int
|
||||
"""
|
||||
if not item.name:
|
||||
# looks like it's a root element
|
||||
return item
|
||||
|
||||
item.start = placeholder
|
||||
item.end = placeholder
|
||||
|
||||
is_unary = item.is_unary() and not item.children
|
||||
|
||||
# formatting output
|
||||
if profile['tag_nl'] is not False:
|
||||
padding = item.parent.padding if item.parent else get_indentation() * level
|
||||
force_nl = profile['tag_nl'] is True
|
||||
should_break = should_break_line(item, profile)
|
||||
|
||||
# formatting block-level elements
|
||||
if ((item.is_block() or should_break) and item.parent) or force_nl:
|
||||
# snippet children should take different formatting
|
||||
if not item.parent or (item.parent.type != 'snippet' and not is_very_first_child(item)):
|
||||
item.start = get_newline() + padding + item.start
|
||||
|
||||
if item.has_block_children() or should_break_child(item, profile) or (force_nl and not is_unary):
|
||||
item.end = get_newline() + padding + item.end
|
||||
|
||||
if item.has_tags_in_content() or (force_nl and not item.has_children() and not is_unary):
|
||||
item.start += get_newline() + padding + get_indentation()
|
||||
|
||||
elif item.is_inline() and has_block_sibling(item) and not is_very_first_child(item):
|
||||
item.start = get_newline() + padding + item.start
|
||||
|
||||
item.padding = padding + get_indentation()
|
||||
|
||||
return item
|
||||
|
||||
def process(tree, profile, level=0):
|
||||
"""
|
||||
Processes simplified tree, making it suitable for output as HTML structure
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@param level: Depth level
|
||||
@type level: int
|
||||
"""
|
||||
|
||||
for item in tree.children:
|
||||
if item.type == 'tag':
|
||||
item = process_tag(item, profile, level)
|
||||
else:
|
||||
item = process_snippet(item, profile, level)
|
||||
|
||||
if item.content:
|
||||
item.content = zen_coding.pad_string(item.content, item.padding)
|
||||
|
||||
process(item, profile, level + 1)
|
||||
|
||||
return tree
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Filter that produces HAML tree
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
from zencoding import zen_core as zen_coding
|
||||
|
||||
child_token = '${child}'
|
||||
|
||||
def make_attributes_string(tag, profile):
|
||||
"""
|
||||
Creates HTML attributes string from tag according to profile settings
|
||||
@type tag: ZenNode
|
||||
@type profile: dict
|
||||
"""
|
||||
# make attribute string
|
||||
attrs = ''
|
||||
attr_quote = profile['attr_quotes'] == 'single' and "'" or '"'
|
||||
cursor = profile['place_cursor'] and zen_coding.get_caret_placeholder() or ''
|
||||
|
||||
# use short notation for ID and CLASS attributes
|
||||
for a in tag.attributes:
|
||||
name_lower = a['name'].lower()
|
||||
if name_lower == 'id':
|
||||
attrs += '#' + (a['value'] or cursor)
|
||||
elif name_lower == 'class':
|
||||
attrs += '.' + (a['value'] or cursor)
|
||||
|
||||
other_attrs = []
|
||||
|
||||
# process other attributes
|
||||
for a in tag.attributes:
|
||||
name_lower = a['name'].lower()
|
||||
if name_lower != 'id' and name_lower != 'class':
|
||||
attr_name = profile['attr_case'] == 'upper' and a['name'].upper() or name_lower
|
||||
other_attrs.append(':' + attr_name + ' => ' + attr_quote + (a['value'] or cursor) + attr_quote)
|
||||
|
||||
if other_attrs:
|
||||
attrs += '{' + ', '.join(other_attrs) + '}'
|
||||
|
||||
return attrs
|
||||
|
||||
def _replace(placeholder, value):
|
||||
if placeholder:
|
||||
return placeholder % value
|
||||
else:
|
||||
return value
|
||||
|
||||
def process_snippet(item, profile, level=0):
|
||||
"""
|
||||
Processes element with <code>snippet</code> type
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@type level: int
|
||||
"""
|
||||
data = item.source.value
|
||||
|
||||
if not data:
|
||||
# snippet wasn't found, process it as tag
|
||||
return process_tag(item, profile, level)
|
||||
|
||||
tokens = data.split(child_token)
|
||||
if len(tokens) < 2:
|
||||
start = tokens[0]
|
||||
end = ''
|
||||
else:
|
||||
start, end = tokens
|
||||
|
||||
padding = item.parent and item.parent.padding or ''
|
||||
|
||||
item.start = _replace(item.start, zen_coding.pad_string(start, padding))
|
||||
item.end = _replace(item.end, zen_coding.pad_string(end, padding))
|
||||
|
||||
return item
|
||||
|
||||
def has_block_sibling(item):
|
||||
"""
|
||||
Test if passed node has block-level sibling element
|
||||
@type item: ZenNode
|
||||
@return: bool
|
||||
"""
|
||||
return item.parent and item.parent.has_block_children()
|
||||
|
||||
def process_tag(item, profile, level=0):
|
||||
"""
|
||||
Processes element with <code>tag</code> type
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@type level: int
|
||||
"""
|
||||
if not item.name:
|
||||
# looks like it's root element
|
||||
return item
|
||||
|
||||
attrs = make_attributes_string(item, profile)
|
||||
cursor = profile['place_cursor'] and zen_coding.get_caret_placeholder() or ''
|
||||
self_closing = ''
|
||||
is_unary = item.is_unary() and not item.children
|
||||
|
||||
if profile['self_closing_tag'] and is_unary:
|
||||
self_closing = '/'
|
||||
|
||||
# define tag name
|
||||
tag_name = '%' + (profile['tag_case'] == 'upper' and item.name.upper() or item.name.lower())
|
||||
|
||||
if tag_name.lower() == '%div' and '{' not in attrs:
|
||||
# omit div tag
|
||||
tag_name = ''
|
||||
|
||||
item.end = ''
|
||||
item.start = _replace(item.start, tag_name + attrs + self_closing)
|
||||
|
||||
if not item.children and not is_unary:
|
||||
item.start += cursor
|
||||
|
||||
return item
|
||||
|
||||
def process(tree, profile, level=0):
|
||||
"""
|
||||
Processes simplified tree, making it suitable for output as HTML structure
|
||||
@type tree: ZenNode
|
||||
@type profile: dict
|
||||
@type level: int
|
||||
"""
|
||||
if level == 0:
|
||||
# preformat tree
|
||||
tree = zen_coding.run_filters(tree, profile, '_format')
|
||||
|
||||
for i, item in enumerate(tree.children):
|
||||
if item.type == 'tag':
|
||||
process_tag(item, profile, level)
|
||||
else:
|
||||
process_snippet(item, profile, level)
|
||||
|
||||
# replace counters
|
||||
item.start = zen_coding.replace_counter(item.start, i + 1)
|
||||
item.end = zen_coding.replace_counter(item.end, i + 1)
|
||||
process(item, profile, level + 1)
|
||||
|
||||
return tree
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Filter that produces HTML tree
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
from zencoding import zen_core as zen_coding
|
||||
|
||||
child_token = '${child}'
|
||||
|
||||
def make_attributes_string(tag, profile):
|
||||
"""
|
||||
Creates HTML attributes string from tag according to profile settings
|
||||
@type tag: ZenNode
|
||||
@type profile: dict
|
||||
"""
|
||||
# make attribute string
|
||||
attrs = ''
|
||||
attr_quote = profile['attr_quotes'] == 'single' and "'" or '"'
|
||||
cursor = profile['place_cursor'] and zen_coding.get_caret_placeholder() or ''
|
||||
|
||||
# process other attributes
|
||||
for a in tag.attributes:
|
||||
attr_name = profile['attr_case'] == 'upper' and a['name'].upper() or a['name'].lower()
|
||||
attrs += ' ' + attr_name + '=' + attr_quote + (a['value'] or cursor) + attr_quote
|
||||
|
||||
return attrs
|
||||
|
||||
def _replace(placeholder, value):
|
||||
if placeholder:
|
||||
return placeholder % value
|
||||
else:
|
||||
return value
|
||||
|
||||
def process_snippet(item, profile, level):
|
||||
"""
|
||||
Processes element with <code>snippet</code> type
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@type level: int
|
||||
"""
|
||||
data = item.source.value;
|
||||
|
||||
if not data:
|
||||
# snippet wasn't found, process it as tag
|
||||
return process_tag(item, profile, level)
|
||||
|
||||
tokens = data.split(child_token)
|
||||
if len(tokens) < 2:
|
||||
start = tokens[0]
|
||||
end = ''
|
||||
else:
|
||||
start, end = tokens
|
||||
|
||||
padding = item.parent and item.parent.padding or ''
|
||||
|
||||
item.start = _replace(item.start, zen_coding.pad_string(start, padding))
|
||||
item.end = _replace(item.end, zen_coding.pad_string(end, padding))
|
||||
|
||||
return item
|
||||
|
||||
|
||||
def has_block_sibling(item):
|
||||
"""
|
||||
Test if passed node has block-level sibling element
|
||||
@type item: ZenNode
|
||||
@return: bool
|
||||
"""
|
||||
return item.parent and item.parent.has_block_children()
|
||||
|
||||
def process_tag(item, profile, level):
|
||||
"""
|
||||
Processes element with <code>tag</code> type
|
||||
@type item: ZenNode
|
||||
@type profile: dict
|
||||
@type level: int
|
||||
"""
|
||||
if not item.name:
|
||||
# looks like it's root element
|
||||
return item
|
||||
|
||||
attrs = make_attributes_string(item, profile)
|
||||
cursor = profile['place_cursor'] and zen_coding.get_caret_placeholder() or ''
|
||||
self_closing = ''
|
||||
is_unary = item.is_unary() and not item.children
|
||||
start= ''
|
||||
end = ''
|
||||
|
||||
if profile['self_closing_tag'] == 'xhtml':
|
||||
self_closing = ' /'
|
||||
elif profile['self_closing_tag'] is True:
|
||||
self_closing = '/'
|
||||
|
||||
# define opening and closing tags
|
||||
tag_name = profile['tag_case'] == 'upper' and item.name.upper() or item.name.lower()
|
||||
if is_unary:
|
||||
start = '<' + tag_name + attrs + self_closing + '>'
|
||||
item.end = ''
|
||||
else:
|
||||
start = '<' + tag_name + attrs + '>'
|
||||
end = '</' + tag_name + '>'
|
||||
|
||||
item.start = _replace(item.start, start)
|
||||
item.end = _replace(item.end, end)
|
||||
|
||||
if not item.children and not is_unary:
|
||||
item.start += cursor
|
||||
|
||||
return item
|
||||
|
||||
def process(tree, profile, level=0):
|
||||
"""
|
||||
Processes simplified tree, making it suitable for output as HTML structure
|
||||
@type tree: ZenNode
|
||||
@type profile: dict
|
||||
@type level: int
|
||||
"""
|
||||
if level == 0:
|
||||
# preformat tree
|
||||
tree = zen_coding.run_filters(tree, profile, '_format')
|
||||
|
||||
for i, item in enumerate(tree.children):
|
||||
if item.type == 'tag':
|
||||
process_tag(item, profile, level)
|
||||
else:
|
||||
process_snippet(item, profile, level)
|
||||
|
||||
# replace counters
|
||||
item.start = zen_coding.replace_counter(item.start, i + 1)
|
||||
item.end = zen_coding.replace_counter(item.end, i + 1)
|
||||
process(item, profile, level + 1)
|
||||
|
||||
return tree
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Filter for trimming "select" attributes from some tags that contains
|
||||
child elements
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
import re
|
||||
|
||||
tags = {
|
||||
'xsl:variable': 1,
|
||||
'xsl:with-param': 1
|
||||
}
|
||||
|
||||
re_attr = re.compile(r'\s+select\s*=\s*([\'"]).*?\1')
|
||||
|
||||
def trim_attribute(node):
|
||||
"""
|
||||
Removes "select" attribute from node
|
||||
@type node: ZenNode
|
||||
"""
|
||||
node.start = re_attr.sub('', node.start)
|
||||
|
||||
def process(tree, profile):
|
||||
for item in tree.children:
|
||||
if item.type == 'tag' and item.name.lower() in tags and item.children:
|
||||
trim_attribute(item)
|
||||
|
||||
process(item, profile)
|
||||
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Context-independent xHTML pair matcher
|
||||
Use method <code>match(html, start_ix)</code> to find matching pair.
|
||||
If pair was found, this function returns a list of indexes where tag pair
|
||||
starts and ends. If pair wasn't found, <code>None</code> will be returned.
|
||||
|
||||
The last matched (or unmatched) result is saved in <code>last_match</code>
|
||||
dictionary for later use.
|
||||
|
||||
@author: Sergey Chikuyonok (serge.che@gmail.com)
|
||||
'''
|
||||
import re
|
||||
|
||||
start_tag = r'<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>\s]+))?)*)\s*(\/?)>'
|
||||
end_tag = r'<\/([\w\:\-]+)[^>]*>'
|
||||
attr = r'([\w\-:]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?'
|
||||
|
||||
"Last matched HTML pair"
|
||||
last_match = {
|
||||
'opening_tag': None, # Tag() or Comment() object
|
||||
'closing_tag': None, # Tag() or Comment() object
|
||||
'start_ix': -1,
|
||||
'end_ix': -1
|
||||
}
|
||||
|
||||
cur_mode = 'xhtml'
|
||||
"Current matching mode"
|
||||
|
||||
def set_mode(new_mode):
|
||||
global cur_mode
|
||||
if new_mode != 'html': new_mode = 'xhtml'
|
||||
cur_mode = new_mode
|
||||
|
||||
def make_map(elems):
|
||||
"""
|
||||
Create dictionary of elements for faster searching
|
||||
@param elems: Elements, separated by comma
|
||||
@type elems: str
|
||||
"""
|
||||
obj = {}
|
||||
for elem in elems.split(','):
|
||||
obj[elem] = True
|
||||
|
||||
return obj
|
||||
|
||||
# Empty Elements - HTML 4.01
|
||||
empty = make_map("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
|
||||
|
||||
# Block Elements - HTML 4.01
|
||||
block = make_map("address,applet,blockquote,button,center,dd,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");
|
||||
|
||||
# Inline Elements - HTML 4.01
|
||||
inline = make_map("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
|
||||
|
||||
# Elements that you can, intentionally, leave open
|
||||
# (and which close themselves)
|
||||
close_self = make_map("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
|
||||
|
||||
# Attributes that have their values filled in disabled="disabled"
|
||||
fill_attrs = make_map("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
|
||||
|
||||
#Special Elements (can contain anything)
|
||||
# serge.che: parsing data inside <scipt> elements is a "feature"
|
||||
special = make_map("style");
|
||||
|
||||
class Tag():
|
||||
"""Matched tag"""
|
||||
def __init__(self, match, ix):
|
||||
"""
|
||||
@type match: MatchObject
|
||||
@param match: Matched HTML tag
|
||||
@type ix: int
|
||||
@param ix: Tag's position
|
||||
"""
|
||||
global cur_mode
|
||||
|
||||
name = match.group(1).lower()
|
||||
self.name = name
|
||||
self.full_tag = match.group(0)
|
||||
self.start = ix
|
||||
self.end = ix + len(self.full_tag)
|
||||
self.unary = ( len(match.groups()) > 2 and bool(match.group(3)) ) or (name in empty and cur_mode == 'html')
|
||||
self.type = 'tag'
|
||||
self.close_self = (name in close_self and cur_mode == 'html')
|
||||
|
||||
class Comment():
|
||||
"Matched comment"
|
||||
def __init__(self, start, end):
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.type = 'comment'
|
||||
|
||||
def make_range(opening_tag=None, closing_tag=None, ix=0):
|
||||
"""
|
||||
Makes selection ranges for matched tag pair
|
||||
@type opening_tag: Tag
|
||||
@type closing_tag: Tag
|
||||
@type ix: int
|
||||
@return list
|
||||
"""
|
||||
start_ix, end_ix = -1, -1
|
||||
|
||||
if opening_tag and not closing_tag: # unary element
|
||||
start_ix = opening_tag.start
|
||||
end_ix = opening_tag.end
|
||||
elif opening_tag and closing_tag: # complete element
|
||||
if (opening_tag.start < ix and opening_tag.end > ix) or (closing_tag.start <= ix and closing_tag.end > ix):
|
||||
start_ix = opening_tag.start
|
||||
end_ix = closing_tag.end;
|
||||
else:
|
||||
start_ix = opening_tag.end
|
||||
end_ix = closing_tag.start
|
||||
|
||||
return start_ix, end_ix
|
||||
|
||||
def save_match(opening_tag=None, closing_tag=None, ix=0):
|
||||
"""
|
||||
Save matched tag for later use and return found indexes
|
||||
@type opening_tag: Tag
|
||||
@type closing_tag: Tag
|
||||
@type ix: int
|
||||
@return list
|
||||
"""
|
||||
last_match['opening_tag'] = opening_tag;
|
||||
last_match['closing_tag'] = closing_tag;
|
||||
|
||||
last_match['start_ix'], last_match['end_ix'] = make_range(opening_tag, closing_tag, ix)
|
||||
|
||||
return last_match['start_ix'] != -1 and (last_match['start_ix'], last_match['end_ix']) or (None, None)
|
||||
|
||||
def match(html, start_ix, mode='xhtml'):
|
||||
"""
|
||||
Search for matching tags in <code>html</code>, starting from
|
||||
<code>start_ix</code> position. The result is automatically saved
|
||||
in <code>last_match</code> property
|
||||
"""
|
||||
return _find_pair(html, start_ix, mode, save_match)
|
||||
|
||||
def find(html, start_ix, mode='xhtml'):
|
||||
"""
|
||||
Search for matching tags in <code>html</code>, starting from
|
||||
<code>start_ix</code> position.
|
||||
"""
|
||||
return _find_pair(html, start_ix, mode)
|
||||
|
||||
def get_tags(html, start_ix, mode='xhtml'):
|
||||
"""
|
||||
Search for matching tags in <code>html</code>, starting from
|
||||
<code>start_ix</code> position. The difference between
|
||||
<code>match</code> function itself is that <code>get_tags</code>
|
||||
method doesn't save matched result in <code>last_match</code> property
|
||||
and returns array of opening and closing tags
|
||||
This method is generally used for lookups
|
||||
"""
|
||||
return _find_pair(html, start_ix, mode, lambda op, cl=None, ix=0: (op, cl) if op and op.type == 'tag' else None)
|
||||
|
||||
|
||||
def _find_pair(html, start_ix, mode='xhtml', action=make_range):
|
||||
"""
|
||||
Search for matching tags in <code>html</code>, starting from
|
||||
<code>start_ix</code> position
|
||||
|
||||
@param html: Code to search
|
||||
@type html: str
|
||||
|
||||
@param start_ix: Character index where to start searching pair
|
||||
(commonly, current caret position)
|
||||
@type start_ix: int
|
||||
|
||||
@param action: Function that creates selection range
|
||||
@type action: function
|
||||
|
||||
@return: list
|
||||
"""
|
||||
|
||||
forward_stack = []
|
||||
backward_stack = []
|
||||
opening_tag = None
|
||||
closing_tag = None
|
||||
html_len = len(html)
|
||||
|
||||
set_mode(mode)
|
||||
|
||||
def has_match(substr, start=None):
|
||||
if start is None:
|
||||
start = ix
|
||||
|
||||
return html.find(substr, start) == start
|
||||
|
||||
|
||||
def find_comment_start(start_pos):
|
||||
while start_pos:
|
||||
if html[start_pos] == '<' and has_match('<!--', start_pos):
|
||||
break
|
||||
|
||||
start_pos -= 1
|
||||
|
||||
return start_pos
|
||||
|
||||
# find opening tag
|
||||
ix = start_ix - 1
|
||||
while ix >= 0:
|
||||
ch = html[ix]
|
||||
if ch == '<':
|
||||
check_str = html[ix:]
|
||||
m = re.match(end_tag, check_str)
|
||||
if m: # found closing tag
|
||||
tmp_tag = Tag(m, ix)
|
||||
if tmp_tag.start < start_ix and tmp_tag.end > start_ix: # direct hit on searched closing tag
|
||||
closing_tag = tmp_tag
|
||||
else:
|
||||
backward_stack.append(tmp_tag)
|
||||
else:
|
||||
m = re.match(start_tag, check_str)
|
||||
if m: # found opening tag
|
||||
tmp_tag = Tag(m, ix);
|
||||
if tmp_tag.unary:
|
||||
if tmp_tag.start < start_ix and tmp_tag.end > start_ix: # exact match
|
||||
return action(tmp_tag, None, start_ix)
|
||||
elif backward_stack and backward_stack[-1].name == tmp_tag.name:
|
||||
backward_stack.pop()
|
||||
else: # found nearest unclosed tag
|
||||
opening_tag = tmp_tag
|
||||
break
|
||||
elif check_str.startswith('<!--'): # found comment start
|
||||
end_ix = check_str.find('-->') + ix + 3;
|
||||
if ix < start_ix and end_ix >= start_ix:
|
||||
return action(Comment(ix, end_ix))
|
||||
elif ch == '-' and has_match('-->'): # found comment end
|
||||
# search left until comment start is reached
|
||||
ix = find_comment_start(ix)
|
||||
|
||||
ix -= 1
|
||||
|
||||
if not opening_tag:
|
||||
return action(None)
|
||||
|
||||
# find closing tag
|
||||
if not closing_tag:
|
||||
ix = start_ix
|
||||
while ix < html_len:
|
||||
ch = html[ix]
|
||||
if ch == '<':
|
||||
check_str = html[ix:]
|
||||
m = re.match(start_tag, check_str)
|
||||
if m: # found opening tag
|
||||
tmp_tag = Tag(m, ix);
|
||||
if not tmp_tag.unary:
|
||||
forward_stack.append(tmp_tag)
|
||||
else:
|
||||
m = re.match(end_tag, check_str)
|
||||
if m: #found closing tag
|
||||
tmp_tag = Tag(m, ix);
|
||||
if forward_stack and forward_stack[-1].name == tmp_tag.name:
|
||||
forward_stack.pop()
|
||||
else: # found matched closing tag
|
||||
closing_tag = tmp_tag;
|
||||
break
|
||||
elif has_match('<!--'): # found comment
|
||||
ix += check_str.find('-->') + 3
|
||||
continue
|
||||
elif ch == '-' and has_match('-->'):
|
||||
# looks like cursor was inside comment with invalid HTML
|
||||
if not forward_stack or forward_stack[-1].type != 'comment':
|
||||
end_ix = ix + 3
|
||||
return action(Comment( find_comment_start(ix), end_ix ))
|
||||
|
||||
ix += 1
|
||||
|
||||
return action(opening_tag, closing_tag, start_ix)
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
my_zen_settings = {
|
||||
'html': {
|
||||
'abbreviations': {
|
||||
'jq': '<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>',
|
||||
'demo': '<div id="demo"></div>'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
'''
|
||||
Zen Coding's settings parser
|
||||
Created on Jun 14, 2009
|
||||
|
||||
@author: sergey
|
||||
'''
|
||||
from copy import deepcopy
|
||||
|
||||
import re
|
||||
import types
|
||||
from zen_settings import zen_settings
|
||||
|
||||
_original_settings = deepcopy(zen_settings)
|
||||
|
||||
TYPE_ABBREVIATION = 'zen-tag',
|
||||
TYPE_EXPANDO = 'zen-expando',
|
||||
TYPE_REFERENCE = 'zen-reference';
|
||||
""" Reference to another abbreviation or tag """
|
||||
|
||||
re_tag = r'^<([\w\-]+(?:\:[\w\-]+)?)((?:\s+[\w\-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>\s]+))?)*)\s*(\/?)>'
|
||||
"Regular expression for XML tag matching"
|
||||
|
||||
re_attrs = r'([\w\-]+)\s*=\s*([\'"])(.*?)\2'
|
||||
"Regular expression for matching XML attributes"
|
||||
|
||||
class Entry:
|
||||
"""
|
||||
Unified object for parsed data
|
||||
"""
|
||||
def __init__(self, entry_type, key, value):
|
||||
"""
|
||||
@type entry_type: str
|
||||
@type key: str
|
||||
@type value: dict
|
||||
"""
|
||||
self.type = entry_type
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
def _make_expando(key, value):
|
||||
"""
|
||||
Make expando from string
|
||||
@type key: str
|
||||
@type value: str
|
||||
@return: Entry
|
||||
"""
|
||||
return Entry(TYPE_EXPANDO, key, value)
|
||||
|
||||
def _make_abbreviation(key, tag_name, attrs, is_empty=False):
|
||||
"""
|
||||
Make abbreviation from string
|
||||
@param key: Abbreviation key
|
||||
@type key: str
|
||||
@param tag_name: Expanded element's tag name
|
||||
@type tag_name: str
|
||||
@param attrs: Expanded element's attributes
|
||||
@type attrs: str
|
||||
@param is_empty: Is expanded element empty or not
|
||||
@type is_empty: bool
|
||||
@return: dict
|
||||
"""
|
||||
result = {
|
||||
'name': tag_name,
|
||||
'is_empty': is_empty
|
||||
};
|
||||
|
||||
if attrs:
|
||||
result['attributes'] = [];
|
||||
for m in re.findall(re_attrs, attrs):
|
||||
result['attributes'].append({
|
||||
'name': m[0],
|
||||
'value': m[2]
|
||||
})
|
||||
|
||||
return Entry(TYPE_ABBREVIATION, key, result)
|
||||
|
||||
def _parse_abbreviations(obj):
|
||||
"""
|
||||
Parses all abbreviations inside dictionary
|
||||
@param obj: dict
|
||||
"""
|
||||
for key, value in obj.items():
|
||||
key = key.strip()
|
||||
if key[-1] == '+':
|
||||
# this is expando, leave 'value' as is
|
||||
obj[key] = _make_expando(key, value)
|
||||
else:
|
||||
m = re.search(re_tag, value)
|
||||
if m:
|
||||
obj[key] = _make_abbreviation(key, m.group(1), m.group(2), (m.group(3) == '/'))
|
||||
else:
|
||||
# assume it's reference to another abbreviation
|
||||
obj[key] = Entry(TYPE_REFERENCE, key, value)
|
||||
|
||||
def parse(settings):
|
||||
"""
|
||||
Parse user's settings. This function must be called *before* any activity
|
||||
in zen coding (for example, expanding abbreviation)
|
||||
@type settings: dict
|
||||
"""
|
||||
for p, value in settings.items():
|
||||
if p == 'abbreviations':
|
||||
_parse_abbreviations(value)
|
||||
elif p == 'extends':
|
||||
settings[p] = [v.strip() for v in value.split(',')]
|
||||
elif type(value) == types.DictType:
|
||||
parse(value)
|
||||
|
||||
|
||||
def extend(parent, child):
|
||||
"""
|
||||
Recursevly extends parent dictionary with children's keys. Used for merging
|
||||
default settings with user's
|
||||
@type parent: dict
|
||||
@type child: dict
|
||||
"""
|
||||
for p, value in child.items():
|
||||
if type(value) == types.DictType:
|
||||
if p not in parent:
|
||||
parent[p] = {}
|
||||
extend(parent[p], value)
|
||||
else:
|
||||
parent[p] = value
|
||||
|
||||
|
||||
|
||||
def create_maps(obj):
|
||||
"""
|
||||
Create hash maps on certain string properties of zen settings
|
||||
@type obj: dict
|
||||
"""
|
||||
for p, value in obj.items():
|
||||
if p == 'element_types':
|
||||
for k, v in value.items():
|
||||
if isinstance(v, str):
|
||||
value[k] = [el.strip() for el in v.split(',')]
|
||||
elif type(value) == types.DictType:
|
||||
create_maps(value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
|
||||
def get_settings(user_settings=None):
|
||||
"""
|
||||
Main function that gather all settings and returns parsed dictionary
|
||||
@param user_settings: A dictionary of user-defined settings
|
||||
"""
|
||||
settings = deepcopy(_original_settings)
|
||||
create_maps(settings)
|
||||
|
||||
if user_settings:
|
||||
user_settings = deepcopy(user_settings)
|
||||
create_maps(user_settings)
|
||||
extend(settings, user_settings)
|
||||
|
||||
# now we need to parse final set of settings
|
||||
parse(settings)
|
||||
|
||||
return settings
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,645 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Middleware layer that communicates between editor and Zen Coding.
|
||||
This layer describes all available Zen Coding actions, like
|
||||
"Expand Abbreviation".
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
"""
|
||||
from zencoding import zen_core as zen_coding
|
||||
from zencoding import html_matcher
|
||||
import re
|
||||
|
||||
def find_abbreviation(editor):
|
||||
"""
|
||||
Search for abbreviation in editor from current caret position
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@return: str
|
||||
"""
|
||||
start, end = editor.get_selection_range()
|
||||
if start != end:
|
||||
# abbreviation is selected by user
|
||||
return editor.get_content()[start, end];
|
||||
|
||||
# search for new abbreviation from current caret position
|
||||
cur_line_start, cur_line_end = editor.get_current_line_range()
|
||||
return zen_coding.extract_abbreviation(editor.get_content()[cur_line_start:start])
|
||||
|
||||
def expand_abbreviation(editor, syntax=None, profile_name=None):
|
||||
"""
|
||||
Find from current caret position and expand abbreviation in editor
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param syntax: Syntax type (html, css, etc.)
|
||||
@type syntax: str
|
||||
@param profile_name: Output profile name (html, xml, xhtml)
|
||||
@type profile_name: str
|
||||
@return: True if abbreviation was expanded successfully
|
||||
"""
|
||||
if syntax is None: syntax = editor.get_syntax()
|
||||
if profile_name is None: profile_name = editor.get_profile_name()
|
||||
|
||||
range_start, caret_pos = editor.get_selection_range()
|
||||
abbr = find_abbreviation(editor)
|
||||
content = ''
|
||||
|
||||
if abbr:
|
||||
content = zen_coding.expand_abbreviation(abbr, syntax, profile_name)
|
||||
if content:
|
||||
editor.replace_content(content, caret_pos - len(abbr), caret_pos)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def expand_abbreviation_with_tab(editor, syntax, profile_name='xhtml'):
|
||||
"""
|
||||
A special version of <code>expandAbbreviation</code> function: if it can't
|
||||
find abbreviation, it will place Tab character at caret position
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param syntax: Syntax type (html, css, etc.)
|
||||
@type syntax: str
|
||||
@param profile_name: Output profile name (html, xml, xhtml)
|
||||
@type profile_name: str
|
||||
"""
|
||||
if not expand_abbreviation(editor, syntax, profile_name):
|
||||
editor.replace_content(zen_coding.get_variable('indentation'), editor.get_caret_pos())
|
||||
|
||||
return True
|
||||
|
||||
def match_pair(editor, direction='out', syntax=None):
|
||||
"""
|
||||
Find and select HTML tag pair
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param direction: Direction of pair matching: 'in' or 'out'.
|
||||
@type direction: str
|
||||
"""
|
||||
direction = direction.lower()
|
||||
if syntax is None: syntax = editor.get_profile_name()
|
||||
|
||||
range_start, range_end = editor.get_selection_range()
|
||||
cursor = range_end
|
||||
content = editor.get_content()
|
||||
rng = None
|
||||
|
||||
old_open_tag = html_matcher.last_match['opening_tag']
|
||||
old_close_tag = html_matcher.last_match['closing_tag']
|
||||
|
||||
if direction == 'in' and old_open_tag and range_start != range_end:
|
||||
# user has previously selected tag and wants to move inward
|
||||
if not old_close_tag:
|
||||
# unary tag was selected, can't move inward
|
||||
return False
|
||||
elif old_open_tag.start == range_start:
|
||||
if content[old_open_tag.end] == '<':
|
||||
# test if the first inward tag matches the entire parent tag's content
|
||||
_r = html_matcher.find(content, old_open_tag.end + 1, syntax)
|
||||
if _r[0] == old_open_tag.end and _r[1] == old_close_tag.start:
|
||||
rng = html_matcher.match(content, old_open_tag.end + 1, syntax)
|
||||
else:
|
||||
rng = (old_open_tag.end, old_close_tag.start)
|
||||
else:
|
||||
rng = (old_open_tag.end, old_close_tag.start)
|
||||
else:
|
||||
new_cursor = content[0:old_close_tag.start].find('<', old_open_tag.end)
|
||||
search_pos = new_cursor + 1 if new_cursor != -1 else old_open_tag.end
|
||||
rng = html_matcher.match(content, search_pos, syntax)
|
||||
else:
|
||||
rng = html_matcher.match(content, cursor, syntax)
|
||||
|
||||
if rng and rng[0] is not None:
|
||||
editor.create_selection(rng[0], rng[1])
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def match_pair_inward(editor):
|
||||
return match_pair(editor, 'in')
|
||||
|
||||
def match_pair_outward(editor):
|
||||
return match_pair(editor, 'out')
|
||||
|
||||
def narrow_to_non_space(text, start, end):
|
||||
"""
|
||||
Narrow down text indexes, adjusting selection to non-space characters
|
||||
@type text: str
|
||||
@type start: int
|
||||
@type end: int
|
||||
@return: list
|
||||
"""
|
||||
# narrow down selection until first non-space character
|
||||
while start < end:
|
||||
if not text[start].isspace():
|
||||
break
|
||||
|
||||
start += 1
|
||||
|
||||
while end > start:
|
||||
end -= 1
|
||||
if not text[end].isspace():
|
||||
end += 1
|
||||
break
|
||||
|
||||
return start, end
|
||||
|
||||
def wrap_with_abbreviation(editor, abbr, syntax=None, profile_name=None):
|
||||
"""
|
||||
Wraps content with abbreviation
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param syntax: Syntax type (html, css, etc.)
|
||||
@type syntax: str
|
||||
@param profile_name: Output profile name (html, xml, xhtml)
|
||||
@type profile_name: str
|
||||
"""
|
||||
if not abbr: return None
|
||||
|
||||
if syntax is None: syntax = editor.get_syntax()
|
||||
if profile_name is None: profile_name = editor.get_profile_name()
|
||||
|
||||
start_offset, end_offset = editor.get_selection_range()
|
||||
content = editor.get_content()
|
||||
|
||||
if start_offset == end_offset:
|
||||
# no selection, find tag pair
|
||||
rng = html_matcher.match(content, start_offset, profile_name)
|
||||
|
||||
if rng[0] is None: # nothing to wrap
|
||||
return None
|
||||
else:
|
||||
start_offset, end_offset = rng
|
||||
|
||||
start_offset, end_offset = narrow_to_non_space(content, start_offset, end_offset)
|
||||
line_bounds = get_line_bounds(content, start_offset)
|
||||
padding = get_line_padding(content[line_bounds[0]:line_bounds[1]])
|
||||
|
||||
new_content = content[start_offset:end_offset]
|
||||
result = zen_coding.wrap_with_abbreviation(abbr, unindent_text(new_content, padding), syntax, profile_name)
|
||||
|
||||
if result:
|
||||
editor.replace_content(result, start_offset, end_offset)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def unindent(editor, text):
|
||||
"""
|
||||
Unindent content, thus preparing text for tag wrapping
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param text: str
|
||||
@return str
|
||||
"""
|
||||
return unindent_text(text, get_current_line_padding(editor))
|
||||
|
||||
def unindent_text(text, pad):
|
||||
"""
|
||||
Removes padding at the beginning of each text's line
|
||||
@type text: str
|
||||
@type pad: str
|
||||
"""
|
||||
lines = zen_coding.split_by_lines(text)
|
||||
|
||||
for i,line in enumerate(lines):
|
||||
if line.startswith(pad):
|
||||
lines[i] = line[len(pad):]
|
||||
|
||||
return zen_coding.get_newline().join(lines)
|
||||
|
||||
def get_current_line_padding(editor):
|
||||
"""
|
||||
Returns padding of current editor's line
|
||||
@return str
|
||||
"""
|
||||
return get_line_padding(editor.get_current_line())
|
||||
|
||||
def get_line_padding(line):
|
||||
"""
|
||||
Returns padding of current editor's line
|
||||
@return str
|
||||
"""
|
||||
m = re.match(r'^(\s+)', line)
|
||||
return m and m.group(0) or ''
|
||||
|
||||
def find_new_edit_point(editor, inc=1, offset=0):
|
||||
"""
|
||||
Search for new caret insertion point
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param inc: Search increment: -1 — search left, 1 — search right
|
||||
@param offset: Initial offset relative to current caret position
|
||||
@return: -1 if insertion point wasn't found
|
||||
"""
|
||||
cur_point = editor.get_caret_pos() + offset
|
||||
content = editor.get_content()
|
||||
max_len = len(content)
|
||||
next_point = -1
|
||||
re_empty_line = r'^\s+$'
|
||||
|
||||
def get_line(ix):
|
||||
start = ix
|
||||
while start >= 0:
|
||||
c = content[start]
|
||||
if c == '\n' or c == '\r': break
|
||||
start -= 1
|
||||
|
||||
return content[start:ix]
|
||||
|
||||
while cur_point < max_len and cur_point > 0:
|
||||
cur_point += inc
|
||||
cur_char = content[cur_point]
|
||||
next_char = content[cur_point + 1]
|
||||
prev_char = content[cur_point - 1]
|
||||
|
||||
if cur_char in '"\'':
|
||||
if next_char == cur_char and prev_char == '=':
|
||||
# empty attribute
|
||||
next_point = cur_point + 1
|
||||
elif cur_char == '>' and next_char == '<':
|
||||
# between tags
|
||||
next_point = cur_point + 1
|
||||
elif cur_char in '\r\n':
|
||||
# empty line
|
||||
if re.search(re_empty_line, get_line(cur_point - 1)):
|
||||
next_point = cur_point
|
||||
|
||||
if next_point != -1: break
|
||||
|
||||
return next_point
|
||||
|
||||
def prev_edit_point(editor):
|
||||
"""
|
||||
Move caret to previous edit point
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
cur_pos = editor.get_caret_pos()
|
||||
new_point = find_new_edit_point(editor, -1)
|
||||
|
||||
if new_point == cur_pos:
|
||||
# we're still in the same point, try searching from the other place
|
||||
new_point = find_new_edit_point(editor, -1, -2)
|
||||
|
||||
if new_point != -1:
|
||||
editor.set_caret_pos(new_point)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def next_edit_point(editor):
|
||||
"""
|
||||
Move caret to next edit point
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
new_point = find_new_edit_point(editor, 1)
|
||||
if new_point != -1:
|
||||
editor.set_caret_pos(new_point)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def insert_formatted_newline(editor, mode='html'):
|
||||
"""
|
||||
Inserts newline character with proper indentation
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param mode: Syntax mode (only 'html' is implemented)
|
||||
@type mode: str
|
||||
"""
|
||||
caret_pos = editor.get_caret_pos()
|
||||
nl = zen_coding.get_newline()
|
||||
pad = zen_coding.get_variable('indentation')
|
||||
|
||||
if mode == 'html':
|
||||
# let's see if we're breaking newly created tag
|
||||
pair = html_matcher.get_tags(editor.get_content(), editor.get_caret_pos(), editor.get_profile_name())
|
||||
|
||||
if pair[0] and pair[1] and pair[0]['type'] == 'tag' and pair[0]['end'] == caret_pos and pair[1]['start'] == caret_pos:
|
||||
editor.replace_content(nl + pad + zen_coding.get_caret_placeholder() + nl, caret_pos)
|
||||
else:
|
||||
editor.replace_content(nl, caret_pos)
|
||||
else:
|
||||
editor.replace_content(nl, caret_pos)
|
||||
|
||||
return True
|
||||
|
||||
def select_line(editor):
|
||||
"""
|
||||
Select line under cursor
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
start, end = editor.get_current_line_range();
|
||||
editor.create_selection(start, end)
|
||||
return True
|
||||
|
||||
def go_to_matching_pair(editor):
|
||||
"""
|
||||
Moves caret to matching opening or closing tag
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
content = editor.get_content()
|
||||
caret_pos = editor.get_caret_pos()
|
||||
|
||||
if content[caret_pos] == '<':
|
||||
# looks like caret is outside of tag pair
|
||||
caret_pos += 1
|
||||
|
||||
tags = html_matcher.get_tags(content, caret_pos, editor.get_profile_name())
|
||||
|
||||
if tags and tags[0]:
|
||||
# match found
|
||||
open_tag, close_tag = tags
|
||||
|
||||
if close_tag: # exclude unary tags
|
||||
if open_tag['start'] <= caret_pos and open_tag['end'] >= caret_pos:
|
||||
editor.set_caret_pos(close_tag['start'])
|
||||
elif close_tag['start'] <= caret_pos and close_tag['end'] >= caret_pos:
|
||||
editor.set_caret_pos(open_tag['start'])
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def merge_lines(editor):
|
||||
"""
|
||||
Merge lines spanned by user selection. If there's no selection, tries to find
|
||||
matching tags and use them as selection
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
start, end = editor.get_selection_range()
|
||||
if start == end:
|
||||
# find matching tag
|
||||
pair = html_matcher.match(editor.get_content(), editor.get_caret_pos(), editor.get_profile_name())
|
||||
if pair and pair[0] is not None:
|
||||
start, end = pair
|
||||
|
||||
if start != end:
|
||||
# got range, merge lines
|
||||
text = editor.get_content()[start:end]
|
||||
lines = map(lambda s: re.sub(r'^\s+', '', s), zen_coding.split_by_lines(text))
|
||||
text = re.sub(r'\s{2,}', ' ', ''.join(lines))
|
||||
editor.replace_content(text, start, end)
|
||||
editor.create_selection(start, start + len(text))
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def toggle_comment(editor):
|
||||
"""
|
||||
Toggle comment on current editor's selection or HTML tag/CSS rule
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
syntax = editor.get_syntax()
|
||||
if syntax == 'css':
|
||||
return toggle_css_comment(editor)
|
||||
else:
|
||||
return toggle_html_comment(editor)
|
||||
|
||||
def toggle_html_comment(editor):
|
||||
"""
|
||||
Toggle HTML comment on current selection or tag
|
||||
@type editor: ZenEditor
|
||||
@return: True if comment was toggled
|
||||
"""
|
||||
start, end = editor.get_selection_range()
|
||||
content = editor.get_content()
|
||||
|
||||
if start == end:
|
||||
# no selection, find matching tag
|
||||
pair = html_matcher.get_tags(content, editor.get_caret_pos(), editor.get_profile_name())
|
||||
if pair and pair[0]: # found pair
|
||||
start = pair[0].start
|
||||
end = pair[1] and pair[1].end or pair[0].end
|
||||
|
||||
return generic_comment_toggle(editor, '<!--', '-->', start, end)
|
||||
|
||||
def toggle_css_comment(editor):
|
||||
"""
|
||||
Simple CSS commenting
|
||||
@type editor: ZenEditor
|
||||
@return: True if comment was toggled
|
||||
"""
|
||||
start, end = editor.get_selection_range()
|
||||
|
||||
if start == end:
|
||||
# no selection, get current line
|
||||
start, end = editor.get_current_line_range()
|
||||
|
||||
# adjust start index till first non-space character
|
||||
start, end = narrow_to_non_space(editor.get_content(), start, end)
|
||||
|
||||
return generic_comment_toggle(editor, '/*', '*/', start, end)
|
||||
|
||||
def search_comment(text, pos, start_token, end_token):
|
||||
"""
|
||||
Search for nearest comment in <code>str</code>, starting from index <code>from</code>
|
||||
@param text: Where to search
|
||||
@type text: str
|
||||
@param pos: Search start index
|
||||
@type pos: int
|
||||
@param start_token: Comment start string
|
||||
@type start_token: str
|
||||
@param end_token: Comment end string
|
||||
@type end_token: str
|
||||
@return: None if comment wasn't found, list otherwise
|
||||
"""
|
||||
start_ch = start_token[0]
|
||||
end_ch = end_token[0]
|
||||
comment_start = -1
|
||||
comment_end = -1
|
||||
|
||||
def has_match(tx, start):
|
||||
return text[start:start + len(tx)] == tx
|
||||
|
||||
|
||||
# search for comment start
|
||||
while pos:
|
||||
pos -= 1
|
||||
if text[pos] == start_ch and has_match(start_token, pos):
|
||||
comment_start = pos
|
||||
break
|
||||
|
||||
if comment_start != -1:
|
||||
# search for comment end
|
||||
pos = comment_start
|
||||
content_len = len(text)
|
||||
while content_len >= pos:
|
||||
pos += 1
|
||||
if text[pos] == end_ch and has_match(end_token, pos):
|
||||
comment_end = pos + len(end_token)
|
||||
break
|
||||
|
||||
if comment_start != -1 and comment_end != -1:
|
||||
return comment_start, comment_end
|
||||
else:
|
||||
return None
|
||||
|
||||
def generic_comment_toggle(editor, comment_start, comment_end, range_start, range_end):
|
||||
"""
|
||||
Generic comment toggling routine
|
||||
@type editor: ZenEditor
|
||||
@param comment_start: Comment start token
|
||||
@type comment_start: str
|
||||
@param comment_end: Comment end token
|
||||
@type comment_end: str
|
||||
@param range_start: Start selection range
|
||||
@type range_start: int
|
||||
@param range_end: End selection range
|
||||
@type range_end: int
|
||||
@return: bool
|
||||
"""
|
||||
content = editor.get_content()
|
||||
caret_pos = [editor.get_caret_pos()]
|
||||
new_content = None
|
||||
|
||||
def adjust_caret_pos(m):
|
||||
caret_pos[0] -= len(m.group(0))
|
||||
return ''
|
||||
|
||||
def remove_comment(text):
|
||||
"""
|
||||
Remove comment markers from string
|
||||
@param {Sting} str
|
||||
@return {String}
|
||||
"""
|
||||
text = re.sub(r'^' + re.escape(comment_start) + r'\s*', adjust_caret_pos, text)
|
||||
return re.sub(r'\s*' + re.escape(comment_end) + '$', '', text)
|
||||
|
||||
def has_match(tx, start):
|
||||
return content[start:start + len(tx)] == tx
|
||||
|
||||
# first, we need to make sure that this substring is not inside comment
|
||||
comment_range = search_comment(content, caret_pos[0], comment_start, comment_end)
|
||||
|
||||
if comment_range and comment_range[0] <= range_start and comment_range[1] >= range_end:
|
||||
# we're inside comment, remove it
|
||||
range_start, range_end = comment_range
|
||||
new_content = remove_comment(content[range_start:range_end])
|
||||
else:
|
||||
# should add comment
|
||||
# make sure that there's no comment inside selection
|
||||
new_content = '%s %s %s' % (comment_start, re.sub(re.escape(comment_start) + r'\s*|\s*' + re.escape(comment_end), '', content[range_start:range_end]), comment_end)
|
||||
|
||||
# adjust caret position
|
||||
caret_pos[0] += len(comment_start) + 1
|
||||
|
||||
# replace editor content
|
||||
if new_content is not None:
|
||||
d = caret_pos[0] - range_start
|
||||
new_content = new_content[0:d] + zen_coding.get_caret_placeholder() + new_content[d:]
|
||||
editor.replace_content(unindent(editor, new_content), range_start, range_end)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def split_join_tag(editor, profile_name=None):
|
||||
"""
|
||||
Splits or joins tag, e.g. transforms it into a short notation and vice versa:
|
||||
<div></div> → <div /> : join
|
||||
<div /> → <div></div> : split
|
||||
@param editor: Editor instance
|
||||
@type editor: ZenEditor
|
||||
@param profile_name: Profile name
|
||||
@type profile_name: str
|
||||
"""
|
||||
caret_pos = editor.get_caret_pos()
|
||||
profile = zen_coding.get_profile(profile_name or editor.get_profile_name())
|
||||
caret = zen_coding.get_caret_placeholder()
|
||||
|
||||
# find tag at current position
|
||||
pair = html_matcher.get_tags(editor.get_content(), caret_pos, profile_name or editor.get_profile_name())
|
||||
if pair and pair[0]:
|
||||
new_content = pair[0].full_tag
|
||||
|
||||
if pair[1]: # join tag
|
||||
closing_slash = ''
|
||||
if profile['self_closing_tag'] is True:
|
||||
closing_slash = '/'
|
||||
elif profile['self_closing_tag'] == 'xhtml':
|
||||
closing_slash = ' /'
|
||||
|
||||
new_content = re.sub(r'\s*>$', closing_slash + '>', new_content)
|
||||
|
||||
# add caret placeholder
|
||||
if len(new_content) + pair[0].start < caret_pos:
|
||||
new_content += caret
|
||||
else:
|
||||
d = caret_pos - pair[0].start
|
||||
new_content = new_content[0:d] + caret + new_content[d:]
|
||||
|
||||
editor.replace_content(new_content, pair[0].start, pair[1].end)
|
||||
else: # split tag
|
||||
nl = zen_coding.get_newline()
|
||||
pad = zen_coding.get_variable('indentation')
|
||||
|
||||
# define tag content depending on profile
|
||||
tag_content = profile['tag_nl'] is True and nl + pad + caret + nl or caret
|
||||
|
||||
new_content = '%s%s</%s>' % (re.sub(r'\s*\/>$', '>', new_content), tag_content, pair[0].name)
|
||||
editor.replace_content(new_content, pair[0].start, pair[0].end)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_line_bounds(text, pos):
|
||||
"""
|
||||
Returns line bounds for specific character position
|
||||
@type text: str
|
||||
@param pos: Where to start searching
|
||||
@type pos: int
|
||||
@return: list
|
||||
"""
|
||||
start = 0
|
||||
end = len(text) - 1
|
||||
|
||||
# search left
|
||||
for i in range(pos - 1, 0, -1):
|
||||
if text[i] in '\n\r':
|
||||
start = i + 1
|
||||
break
|
||||
|
||||
# search right
|
||||
for i in range(pos, len(text)):
|
||||
if text[i] in '\n\r':
|
||||
end = i
|
||||
break
|
||||
|
||||
return start, end
|
||||
|
||||
def remove_tag(editor):
|
||||
"""
|
||||
Gracefully removes tag under cursor
|
||||
@type editor: ZenEditor
|
||||
"""
|
||||
caret_pos = editor.get_caret_pos()
|
||||
content = editor.get_content()
|
||||
|
||||
# search for tag
|
||||
pair = html_matcher.get_tags(content, caret_pos, editor.get_profile_name())
|
||||
if pair and pair[0]:
|
||||
if not pair[1]:
|
||||
# simply remove unary tag
|
||||
editor.replace_content(zen_coding.get_caret_placeholder(), pair[0].start, pair[0].end)
|
||||
else:
|
||||
tag_content_range = narrow_to_non_space(content, pair[0].end, pair[1].start)
|
||||
start_line_bounds = get_line_bounds(content, tag_content_range[0])
|
||||
start_line_pad = get_line_padding(content[start_line_bounds[0]:start_line_bounds[1]])
|
||||
tag_content = content[tag_content_range[0]:tag_content_range[1]]
|
||||
|
||||
tag_content = unindent_text(tag_content, start_line_pad)
|
||||
editor.replace_content(zen_coding.get_caret_placeholder() + tag_content, pair[0].start, pair[1].end)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,128 @@
|
||||
'''
|
||||
High-level editor interface that communicates with underlying editor (like
|
||||
Espresso, Coda, etc.) or browser.
|
||||
Basically, you should call <code>set_context(obj)</code> method to
|
||||
set up undelying editor context before using any other method.
|
||||
|
||||
This interface is used by <i>zen_actions.py</i> for performing different
|
||||
actions like <b>Expand abbreviation</b>
|
||||
|
||||
@example
|
||||
import zen_editor
|
||||
zen_editor.set_context(obj);
|
||||
//now you are ready to use editor object
|
||||
zen_editor.get_selection_range();
|
||||
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
'''
|
||||
class ZenEditor():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def set_context(self, context):
|
||||
"""
|
||||
Setup underlying editor context. You should call this method
|
||||
<code>before</code> using any Zen Coding action.
|
||||
@param context: context object
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_selection_range(self):
|
||||
"""
|
||||
Returns character indexes of selected text
|
||||
@return: list of start and end indexes
|
||||
@example
|
||||
start, end = zen_editor.get_selection_range();
|
||||
print('%s, %s' % (start, end))
|
||||
"""
|
||||
return 0, 0
|
||||
|
||||
|
||||
def create_selection(self, start, end=None):
|
||||
"""
|
||||
Creates selection from <code>start</code> to <code>end</code> character
|
||||
indexes. If <code>end</code> is ommited, this method should place caret
|
||||
and <code>start</code> index
|
||||
@type start: int
|
||||
@type end: int
|
||||
@example
|
||||
zen_editor.create_selection(10, 40)
|
||||
# move caret to 15th character
|
||||
zen_editor.create_selection(15)
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_current_line_range(self):
|
||||
"""
|
||||
Returns current line's start and end indexes
|
||||
@return: list of start and end indexes
|
||||
@example
|
||||
start, end = zen_editor.get_current_line_range();
|
||||
print('%s, %s' % (start, end))
|
||||
"""
|
||||
return 0, 0
|
||||
|
||||
def get_caret_pos(self):
|
||||
""" Returns current caret position """
|
||||
return 0
|
||||
|
||||
def set_caret_pos(self, pos):
|
||||
"""
|
||||
Set new caret position
|
||||
@type pos: int
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_current_line(self):
|
||||
"""
|
||||
Returns content of current line
|
||||
@return: str
|
||||
"""
|
||||
return ''
|
||||
|
||||
def replace_content(self, value, start=None, end=None):
|
||||
"""
|
||||
Replace editor's content or it's part (from <code>start</code> to
|
||||
<code>end</code> index). If <code>value</code> contains
|
||||
<code>caret_placeholder</code>, the editor will put caret into
|
||||
this position. If you skip <code>start</code> and <code>end</code>
|
||||
arguments, the whole target's content will be replaced with
|
||||
<code>value</code>.
|
||||
|
||||
If you pass <code>start</code> argument only,
|
||||
the <code>value</code> will be placed at <code>start</code> string
|
||||
index of current content.
|
||||
|
||||
If you pass <code>start</code> and <code>end</code> arguments,
|
||||
the corresponding substring of current target's content will be
|
||||
replaced with <code>value</code>
|
||||
@param value: Content you want to paste
|
||||
@type value: str
|
||||
@param start: Start index of editor's content
|
||||
@type start: int
|
||||
@param end: End index of editor's content
|
||||
@type end: int
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_content(self):
|
||||
"""
|
||||
Returns editor's content
|
||||
@return: str
|
||||
"""
|
||||
return ''
|
||||
|
||||
def get_syntax(self):
|
||||
"""
|
||||
Returns current editor's syntax mode
|
||||
@return: str
|
||||
"""
|
||||
return 'html'
|
||||
|
||||
def get_profile_name(self):
|
||||
"""
|
||||
Returns current output profile name (@see zen_coding#setup_profile)
|
||||
@return {String}
|
||||
"""
|
||||
return 'xhtml'
|
||||
@@ -0,0 +1,727 @@
|
||||
"""
|
||||
Zen Coding settings
|
||||
@author Sergey Chikuyonok (serge.che@gmail.com)
|
||||
@link http://chikuyonok.ru
|
||||
"""
|
||||
zen_settings = {
|
||||
|
||||
# Variables that can be placed inside snippets or abbreviations as ${variable}
|
||||
# ${child} variable is reserved, don't use it
|
||||
'variables': {
|
||||
'lang': 'en',
|
||||
'locale': 'en-US',
|
||||
'charset': 'UTF-8',
|
||||
'profile': 'xhtml',
|
||||
|
||||
# Inner element indentation
|
||||
'indentation': '\t'
|
||||
},
|
||||
|
||||
# common settings are used for quick injection of user-defined snippets
|
||||
'common': {
|
||||
|
||||
},
|
||||
|
||||
'css': {
|
||||
'extends': 'common',
|
||||
'snippets': {
|
||||
"@i": "@import url(|);",
|
||||
"@m": "@media print {\n\t|\n}",
|
||||
"@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}",
|
||||
"!": "!important",
|
||||
"pos": "position:|;",
|
||||
"pos:s": "position:static;",
|
||||
"pos:a": "position:absolute;",
|
||||
"pos:r": "position:relative;",
|
||||
"pos:f": "position:fixed;",
|
||||
"t": "top:|;",
|
||||
"t:a": "top:auto;",
|
||||
"r": "right:|;",
|
||||
"r:a": "right:auto;",
|
||||
"b": "bottom:|;",
|
||||
"b:a": "bottom:auto;",
|
||||
"l": "left:|;",
|
||||
"l:a": "left:auto;",
|
||||
"z": "z-index:|;",
|
||||
"z:a": "z-index:auto;",
|
||||
"fl": "float:|;",
|
||||
"fl:n": "float:none;",
|
||||
"fl:l": "float:left;",
|
||||
"fl:r": "float:right;",
|
||||
"cl": "clear:|;",
|
||||
"cl:n": "clear:none;",
|
||||
"cl:l": "clear:left;",
|
||||
"cl:r": "clear:right;",
|
||||
"cl:b": "clear:both;",
|
||||
"d": "display:|;",
|
||||
"d:n": "display:none;",
|
||||
"d:b": "display:block;",
|
||||
"d:ib": "display:inline;",
|
||||
"d:li": "display:list-item;",
|
||||
"d:ri": "display:run-in;",
|
||||
"d:cp": "display:compact;",
|
||||
"d:tb": "display:table;",
|
||||
"d:itb": "display:inline-table;",
|
||||
"d:tbcp": "display:table-caption;",
|
||||
"d:tbcl": "display:table-column;",
|
||||
"d:tbclg": "display:table-column-group;",
|
||||
"d:tbhg": "display:table-header-group;",
|
||||
"d:tbfg": "display:table-footer-group;",
|
||||
"d:tbr": "display:table-row;",
|
||||
"d:tbrg": "display:table-row-group;",
|
||||
"d:tbc": "display:table-cell;",
|
||||
"d:rb": "display:ruby;",
|
||||
"d:rbb": "display:ruby-base;",
|
||||
"d:rbbg": "display:ruby-base-group;",
|
||||
"d:rbt": "display:ruby-text;",
|
||||
"d:rbtg": "display:ruby-text-group;",
|
||||
"v": "visibility:|;",
|
||||
"v:v": "visibility:visible;",
|
||||
"v:h": "visibility:hidden;",
|
||||
"v:c": "visibility:collapse;",
|
||||
"ov": "overflow:|;",
|
||||
"ov:v": "overflow:visible;",
|
||||
"ov:h": "overflow:hidden;",
|
||||
"ov:s": "overflow:scroll;",
|
||||
"ov:a": "overflow:auto;",
|
||||
"ovx": "overflow-x:|;",
|
||||
"ovx:v": "overflow-x:visible;",
|
||||
"ovx:h": "overflow-x:hidden;",
|
||||
"ovx:s": "overflow-x:scroll;",
|
||||
"ovx:a": "overflow-x:auto;",
|
||||
"ovy": "overflow-y:|;",
|
||||
"ovy:v": "overflow-y:visible;",
|
||||
"ovy:h": "overflow-y:hidden;",
|
||||
"ovy:s": "overflow-y:scroll;",
|
||||
"ovy:a": "overflow-y:auto;",
|
||||
"ovs": "overflow-style:|;",
|
||||
"ovs:a": "overflow-style:auto;",
|
||||
"ovs:s": "overflow-style:scrollbar;",
|
||||
"ovs:p": "overflow-style:panner;",
|
||||
"ovs:m": "overflow-style:move;",
|
||||
"ovs:mq": "overflow-style:marquee;",
|
||||
"zoo": "zoom:1;",
|
||||
"cp": "clip:|;",
|
||||
"cp:a": "clip:auto;",
|
||||
"cp:r": "clip:rect(|);",
|
||||
"bxz": "box-sizing:|;",
|
||||
"bxz:cb": "box-sizing:content-box;",
|
||||
"bxz:bb": "box-sizing:border-box;",
|
||||
"bxsh": "box-shadow:|;",
|
||||
"bxsh:n": "box-shadow:none;",
|
||||
"bxsh:w": "-webkit-box-shadow:0 0 0 #000;",
|
||||
"bxsh:m": "-moz-box-shadow:0 0 0 0 #000;",
|
||||
"m": "margin:|;",
|
||||
"m:a": "margin:auto;",
|
||||
"m:0": "margin:0;",
|
||||
"m:2": "margin:0 0;",
|
||||
"m:3": "margin:0 0 0;",
|
||||
"m:4": "margin:0 0 0 0;",
|
||||
"mt": "margin-top:|;",
|
||||
"mt:a": "margin-top:auto;",
|
||||
"mr": "margin-right:|;",
|
||||
"mr:a": "margin-right:auto;",
|
||||
"mb": "margin-bottom:|;",
|
||||
"mb:a": "margin-bottom:auto;",
|
||||
"ml": "margin-left:|;",
|
||||
"ml:a": "margin-left:auto;",
|
||||
"p": "padding:|;",
|
||||
"p:0": "padding:0;",
|
||||
"p:2": "padding:0 0;",
|
||||
"p:3": "padding:0 0 0;",
|
||||
"p:4": "padding:0 0 0 0;",
|
||||
"pt": "padding-top:|;",
|
||||
"pr": "padding-right:|;",
|
||||
"pb": "padding-bottom:|;",
|
||||
"pl": "padding-left:|;",
|
||||
"w": "width:|;",
|
||||
"w:a": "width:auto;",
|
||||
"h": "height:|;",
|
||||
"h:a": "height:auto;",
|
||||
"maw": "max-width:|;",
|
||||
"maw:n": "max-width:none;",
|
||||
"mah": "max-height:|;",
|
||||
"mah:n": "max-height:none;",
|
||||
"miw": "min-width:|;",
|
||||
"mih": "min-height:|;",
|
||||
"o": "outline:|;",
|
||||
"o:n": "outline:none;",
|
||||
"oo": "outline-offset:|;",
|
||||
"ow": "outline-width:|;",
|
||||
"os": "outline-style:|;",
|
||||
"oc": "outline-color:#000;",
|
||||
"oc:i": "outline-color:invert;",
|
||||
"bd": "border:|;",
|
||||
"bd+": "border:1px solid #000;",
|
||||
"bd:n": "border:none;",
|
||||
"bdbk": "border-break:|;",
|
||||
"bdbk:c": "border-break:close;",
|
||||
"bdcl": "border-collapse:|;",
|
||||
"bdcl:c": "border-collapse:collapse;",
|
||||
"bdcl:s": "border-collapse:separate;",
|
||||
"bdc": "border-color:#000;",
|
||||
"bdi": "border-image:url(|);",
|
||||
"bdi:n": "border-image:none;",
|
||||
"bdi:w": "-webkit-border-image:url(|) 0 0 0 0 stretch stretch;",
|
||||
"bdi:m": "-moz-border-image:url(|) 0 0 0 0 stretch stretch;",
|
||||
"bdti": "border-top-image:url(|);",
|
||||
"bdti:n": "border-top-image:none;",
|
||||
"bdri": "border-right-image:url(|);",
|
||||
"bdri:n": "border-right-image:none;",
|
||||
"bdbi": "border-bottom-image:url(|);",
|
||||
"bdbi:n": "border-bottom-image:none;",
|
||||
"bdli": "border-left-image:url(|);",
|
||||
"bdli:n": "border-left-image:none;",
|
||||
"bdci": "border-corner-image:url(|);",
|
||||
"bdci:n": "border-corner-image:none;",
|
||||
"bdci:c": "border-corner-image:continue;",
|
||||
"bdtli": "border-top-left-image:url(|);",
|
||||
"bdtli:n": "border-top-left-image:none;",
|
||||
"bdtli:c": "border-top-left-image:continue;",
|
||||
"bdtri": "border-top-right-image:url(|);",
|
||||
"bdtri:n": "border-top-right-image:none;",
|
||||
"bdtri:c": "border-top-right-image:continue;",
|
||||
"bdbri": "border-bottom-right-image:url(|);",
|
||||
"bdbri:n": "border-bottom-right-image:none;",
|
||||
"bdbri:c": "border-bottom-right-image:continue;",
|
||||
"bdbli": "border-bottom-left-image:url(|);",
|
||||
"bdbli:n": "border-bottom-left-image:none;",
|
||||
"bdbli:c": "border-bottom-left-image:continue;",
|
||||
"bdf": "border-fit:|;",
|
||||
"bdf:c": "border-fit:clip;",
|
||||
"bdf:r": "border-fit:repeat;",
|
||||
"bdf:sc": "border-fit:scale;",
|
||||
"bdf:st": "border-fit:stretch;",
|
||||
"bdf:ow": "border-fit:overwrite;",
|
||||
"bdf:of": "border-fit:overflow;",
|
||||
"bdf:sp": "border-fit:space;",
|
||||
"bdl": "border-length:|;",
|
||||
"bdl:a": "border-length:auto;",
|
||||
"bdsp": "border-spacing:|;",
|
||||
"bds": "border-style:|;",
|
||||
"bds:n": "border-style:none;",
|
||||
"bds:h": "border-style:hidden;",
|
||||
"bds:dt": "border-style:dotted;",
|
||||
"bds:ds": "border-style:dashed;",
|
||||
"bds:s": "border-style:solid;",
|
||||
"bds:db": "border-style:double;",
|
||||
"bds:dtds": "border-style:dot-dash;",
|
||||
"bds:dtdtds": "border-style:dot-dot-dash;",
|
||||
"bds:w": "border-style:wave;",
|
||||
"bds:g": "border-style:groove;",
|
||||
"bds:r": "border-style:ridge;",
|
||||
"bds:i": "border-style:inset;",
|
||||
"bds:o": "border-style:outset;",
|
||||
"bdw": "border-width:|;",
|
||||
"bdt": "border-top:|;",
|
||||
"bdt+": "border-top:1px solid #000;",
|
||||
"bdt:n": "border-top:none;",
|
||||
"bdtw": "border-top-width:|;",
|
||||
"bdts": "border-top-style:|;",
|
||||
"bdts:n": "border-top-style:none;",
|
||||
"bdtc": "border-top-color:#000;",
|
||||
"bdr": "border-right:|;",
|
||||
"bdr+": "border-right:1px solid #000;",
|
||||
"bdr:n": "border-right:none;",
|
||||
"bdrw": "border-right-width:|;",
|
||||
"bdrs": "border-right-style:|;",
|
||||
"bdrs:n": "border-right-style:none;",
|
||||
"bdrc": "border-right-color:#000;",
|
||||
"bdb": "border-bottom:|;",
|
||||
"bdb+": "border-bottom:1px solid #000;",
|
||||
"bdb:n": "border-bottom:none;",
|
||||
"bdbw": "border-bottom-width:|;",
|
||||
"bdbs": "border-bottom-style:|;",
|
||||
"bdbs:n": "border-bottom-style:none;",
|
||||
"bdbc": "border-bottom-color:#000;",
|
||||
"bdl": "border-left:|;",
|
||||
"bdl+": "border-left:1px solid #000;",
|
||||
"bdl:n": "border-left:none;",
|
||||
"bdlw": "border-left-width:|;",
|
||||
"bdls": "border-left-style:|;",
|
||||
"bdls:n": "border-left-style:none;",
|
||||
"bdlc": "border-left-color:#000;",
|
||||
"bdrs": "border-radius:|;",
|
||||
"bdtrrs": "border-top-right-radius:|;",
|
||||
"bdtlrs": "border-top-left-radius:|;",
|
||||
"bdbrrs": "border-bottom-right-radius:|;",
|
||||
"bdblrs": "border-bottom-left-radius:|;",
|
||||
"bg": "background:|;",
|
||||
"bg+": "background:#FFF url(|) 0 0 no-repeat;",
|
||||
"bg:n": "background:none;",
|
||||
"bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='|x.png');",
|
||||
"bgc": "background-color:#FFF;",
|
||||
"bgi": "background-image:url(|);",
|
||||
"bgi:n": "background-image:none;",
|
||||
"bgr": "background-repeat:|;",
|
||||
"bgr:n": "background-repeat:no-repeat;",
|
||||
"bgr:x": "background-repeat:repeat-x;",
|
||||
"bgr:y": "background-repeat:repeat-y;",
|
||||
"bga": "background-attachment:|;",
|
||||
"bga:f": "background-attachment:fixed;",
|
||||
"bga:s": "background-attachment:scroll;",
|
||||
"bgp": "background-position:0 0;",
|
||||
"bgpx": "background-position-x:|;",
|
||||
"bgpy": "background-position-y:|;",
|
||||
"bgbk": "background-break:|;",
|
||||
"bgbk:bb": "background-break:bounding-box;",
|
||||
"bgbk:eb": "background-break:each-box;",
|
||||
"bgbk:c": "background-break:continuous;",
|
||||
"bgcp": "background-clip:|;",
|
||||
"bgcp:bb": "background-clip:border-box;",
|
||||
"bgcp:pb": "background-clip:padding-box;",
|
||||
"bgcp:cb": "background-clip:content-box;",
|
||||
"bgcp:nc": "background-clip:no-clip;",
|
||||
"bgo": "background-origin:|;",
|
||||
"bgo:pb": "background-origin:padding-box;",
|
||||
"bgo:bb": "background-origin:border-box;",
|
||||
"bgo:cb": "background-origin:content-box;",
|
||||
"bgz": "background-size:|;",
|
||||
"bgz:a": "background-size:auto;",
|
||||
"bgz:ct": "background-size:contain;",
|
||||
"bgz:cv": "background-size:cover;",
|
||||
"c": "color:#000;",
|
||||
"tbl": "table-layout:|;",
|
||||
"tbl:a": "table-layout:auto;",
|
||||
"tbl:f": "table-layout:fixed;",
|
||||
"cps": "caption-side:|;",
|
||||
"cps:t": "caption-side:top;",
|
||||
"cps:b": "caption-side:bottom;",
|
||||
"ec": "empty-cells:|;",
|
||||
"ec:s": "empty-cells:show;",
|
||||
"ec:h": "empty-cells:hide;",
|
||||
"lis": "list-style:|;",
|
||||
"lis:n": "list-style:none;",
|
||||
"lisp": "list-style-position:|;",
|
||||
"lisp:i": "list-style-position:inside;",
|
||||
"lisp:o": "list-style-position:outside;",
|
||||
"list": "list-style-type:|;",
|
||||
"list:n": "list-style-type:none;",
|
||||
"list:d": "list-style-type:disc;",
|
||||
"list:c": "list-style-type:circle;",
|
||||
"list:s": "list-style-type:square;",
|
||||
"list:dc": "list-style-type:decimal;",
|
||||
"list:dclz": "list-style-type:decimal-leading-zero;",
|
||||
"list:lr": "list-style-type:lower-roman;",
|
||||
"list:ur": "list-style-type:upper-roman;",
|
||||
"lisi": "list-style-image:|;",
|
||||
"lisi:n": "list-style-image:none;",
|
||||
"q": "quotes:|;",
|
||||
"q:n": "quotes:none;",
|
||||
"q:ru": "quotes:'\00AB' '\00BB' '\201E' '\201C';",
|
||||
"q:en": "quotes:'\201C' '\201D' '\2018' '\2019';",
|
||||
"ct": "content:|;",
|
||||
"ct:n": "content:normal;",
|
||||
"ct:oq": "content:open-quote;",
|
||||
"ct:noq": "content:no-open-quote;",
|
||||
"ct:cq": "content:close-quote;",
|
||||
"ct:ncq": "content:no-close-quote;",
|
||||
"ct:a": "content:attr(|);",
|
||||
"ct:c": "content:counter(|);",
|
||||
"ct:cs": "content:counters(|);",
|
||||
"coi": "counter-increment:|;",
|
||||
"cor": "counter-reset:|;",
|
||||
"va": "vertical-align:|;",
|
||||
"va:sup": "vertical-align:super;",
|
||||
"va:t": "vertical-align:top;",
|
||||
"va:tt": "vertical-align:text-top;",
|
||||
"va:m": "vertical-align:middle;",
|
||||
"va:bl": "vertical-align:baseline;",
|
||||
"va:b": "vertical-align:bottom;",
|
||||
"va:tb": "vertical-align:text-bottom;",
|
||||
"va:sub": "vertical-align:sub;",
|
||||
"ta": "text-align:|;",
|
||||
"ta:l": "text-align:left;",
|
||||
"ta:c": "text-align:center;",
|
||||
"ta:r": "text-align:right;",
|
||||
"tal": "text-align-last:|;",
|
||||
"tal:a": "text-align-last:auto;",
|
||||
"tal:l": "text-align-last:left;",
|
||||
"tal:c": "text-align-last:center;",
|
||||
"tal:r": "text-align-last:right;",
|
||||
"td": "text-decoration:|;",
|
||||
"td:n": "text-decoration:none;",
|
||||
"td:u": "text-decoration:underline;",
|
||||
"td:o": "text-decoration:overline;",
|
||||
"td:l": "text-decoration:line-through;",
|
||||
"te": "text-emphasis:|;",
|
||||
"te:n": "text-emphasis:none;",
|
||||
"te:ac": "text-emphasis:accent;",
|
||||
"te:dt": "text-emphasis:dot;",
|
||||
"te:c": "text-emphasis:circle;",
|
||||
"te:ds": "text-emphasis:disc;",
|
||||
"te:b": "text-emphasis:before;",
|
||||
"te:a": "text-emphasis:after;",
|
||||
"th": "text-height:|;",
|
||||
"th:a": "text-height:auto;",
|
||||
"th:f": "text-height:font-size;",
|
||||
"th:t": "text-height:text-size;",
|
||||
"th:m": "text-height:max-size;",
|
||||
"ti": "text-indent:|;",
|
||||
"ti:-": "text-indent:-9999px;",
|
||||
"tj": "text-justify:|;",
|
||||
"tj:a": "text-justify:auto;",
|
||||
"tj:iw": "text-justify:inter-word;",
|
||||
"tj:ii": "text-justify:inter-ideograph;",
|
||||
"tj:ic": "text-justify:inter-cluster;",
|
||||
"tj:d": "text-justify:distribute;",
|
||||
"tj:k": "text-justify:kashida;",
|
||||
"tj:t": "text-justify:tibetan;",
|
||||
"to": "text-outline:|;",
|
||||
"to+": "text-outline:0 0 #000;",
|
||||
"to:n": "text-outline:none;",
|
||||
"tr": "text-replace:|;",
|
||||
"tr:n": "text-replace:none;",
|
||||
"tt": "text-transform:|;",
|
||||
"tt:n": "text-transform:none;",
|
||||
"tt:c": "text-transform:capitalize;",
|
||||
"tt:u": "text-transform:uppercase;",
|
||||
"tt:l": "text-transform:lowercase;",
|
||||
"tw": "text-wrap:|;",
|
||||
"tw:n": "text-wrap:normal;",
|
||||
"tw:no": "text-wrap:none;",
|
||||
"tw:u": "text-wrap:unrestricted;",
|
||||
"tw:s": "text-wrap:suppress;",
|
||||
"tsh": "text-shadow:|;",
|
||||
"tsh+": "text-shadow:0 0 0 #000;",
|
||||
"tsh:n": "text-shadow:none;",
|
||||
"lh": "line-height:|;",
|
||||
"whs": "white-space:|;",
|
||||
"whs:n": "white-space:normal;",
|
||||
"whs:p": "white-space:pre;",
|
||||
"whs:nw": "white-space:nowrap;",
|
||||
"whs:pw": "white-space:pre-wrap;",
|
||||
"whs:pl": "white-space:pre-line;",
|
||||
"whsc": "white-space-collapse:|;",
|
||||
"whsc:n": "white-space-collapse:normal;",
|
||||
"whsc:k": "white-space-collapse:keep-all;",
|
||||
"whsc:l": "white-space-collapse:loose;",
|
||||
"whsc:bs": "white-space-collapse:break-strict;",
|
||||
"whsc:ba": "white-space-collapse:break-all;",
|
||||
"wob": "word-break:|;",
|
||||
"wob:n": "word-break:normal;",
|
||||
"wob:k": "word-break:keep-all;",
|
||||
"wob:l": "word-break:loose;",
|
||||
"wob:bs": "word-break:break-strict;",
|
||||
"wob:ba": "word-break:break-all;",
|
||||
"wos": "word-spacing:|;",
|
||||
"wow": "word-wrap:|;",
|
||||
"wow:nm": "word-wrap:normal;",
|
||||
"wow:n": "word-wrap:none;",
|
||||
"wow:u": "word-wrap:unrestricted;",
|
||||
"wow:s": "word-wrap:suppress;",
|
||||
"lts": "letter-spacing:|;",
|
||||
"f": "font:|;",
|
||||
"f+": "font:1em Arial,sans-serif;",
|
||||
"fw": "font-weight:|;",
|
||||
"fw:n": "font-weight:normal;",
|
||||
"fw:b": "font-weight:bold;",
|
||||
"fw:br": "font-weight:bolder;",
|
||||
"fw:lr": "font-weight:lighter;",
|
||||
"fs": "font-style:|;",
|
||||
"fs:n": "font-style:normal;",
|
||||
"fs:i": "font-style:italic;",
|
||||
"fs:o": "font-style:oblique;",
|
||||
"fv": "font-variant:|;",
|
||||
"fv:n": "font-variant:normal;",
|
||||
"fv:sc": "font-variant:small-caps;",
|
||||
"fz": "font-size:|;",
|
||||
"fza": "font-size-adjust:|;",
|
||||
"fza:n": "font-size-adjust:none;",
|
||||
"ff": "font-family:|;",
|
||||
"ff:s": "font-family:serif;",
|
||||
"ff:ss": "font-family:sans-serif;",
|
||||
"ff:c": "font-family:cursive;",
|
||||
"ff:f": "font-family:fantasy;",
|
||||
"ff:m": "font-family:monospace;",
|
||||
"fef": "font-effect:|;",
|
||||
"fef:n": "font-effect:none;",
|
||||
"fef:eg": "font-effect:engrave;",
|
||||
"fef:eb": "font-effect:emboss;",
|
||||
"fef:o": "font-effect:outline;",
|
||||
"fem": "font-emphasize:|;",
|
||||
"femp": "font-emphasize-position:|;",
|
||||
"femp:b": "font-emphasize-position:before;",
|
||||
"femp:a": "font-emphasize-position:after;",
|
||||
"fems": "font-emphasize-style:|;",
|
||||
"fems:n": "font-emphasize-style:none;",
|
||||
"fems:ac": "font-emphasize-style:accent;",
|
||||
"fems:dt": "font-emphasize-style:dot;",
|
||||
"fems:c": "font-emphasize-style:circle;",
|
||||
"fems:ds": "font-emphasize-style:disc;",
|
||||
"fsm": "font-smooth:|;",
|
||||
"fsm:a": "font-smooth:auto;",
|
||||
"fsm:n": "font-smooth:never;",
|
||||
"fsm:aw": "font-smooth:always;",
|
||||
"fst": "font-stretch:|;",
|
||||
"fst:n": "font-stretch:normal;",
|
||||
"fst:uc": "font-stretch:ultra-condensed;",
|
||||
"fst:ec": "font-stretch:extra-condensed;",
|
||||
"fst:c": "font-stretch:condensed;",
|
||||
"fst:sc": "font-stretch:semi-condensed;",
|
||||
"fst:se": "font-stretch:semi-expanded;",
|
||||
"fst:e": "font-stretch:expanded;",
|
||||
"fst:ee": "font-stretch:extra-expanded;",
|
||||
"fst:ue": "font-stretch:ultra-expanded;",
|
||||
"op": "opacity:|;",
|
||||
"op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);",
|
||||
"op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",
|
||||
"rz": "resize:|;",
|
||||
"rz:n": "resize:none;",
|
||||
"rz:b": "resize:both;",
|
||||
"rz:h": "resize:horizontal;",
|
||||
"rz:v": "resize:vertical;",
|
||||
"cur": "cursor:|;",
|
||||
"cur:a": "cursor:auto;",
|
||||
"cur:d": "cursor:default;",
|
||||
"cur:c": "cursor:crosshair;",
|
||||
"cur:ha": "cursor:hand;",
|
||||
"cur:he": "cursor:help;",
|
||||
"cur:m": "cursor:move;",
|
||||
"cur:p": "cursor:pointer;",
|
||||
"cur:t": "cursor:text;",
|
||||
"pgbb": "page-break-before:|;",
|
||||
"pgbb:au": "page-break-before:auto;",
|
||||
"pgbb:al": "page-break-before:always;",
|
||||
"pgbb:l": "page-break-before:left;",
|
||||
"pgbb:r": "page-break-before:right;",
|
||||
"pgbi": "page-break-inside:|;",
|
||||
"pgbi:au": "page-break-inside:auto;",
|
||||
"pgbi:av": "page-break-inside:avoid;",
|
||||
"pgba": "page-break-after:|;",
|
||||
"pgba:au": "page-break-after:auto;",
|
||||
"pgba:al": "page-break-after:always;",
|
||||
"pgba:l": "page-break-after:left;",
|
||||
"pgba:r": "page-break-after:right;",
|
||||
"orp": "orphans:|;",
|
||||
"wid": "widows:|;"
|
||||
}
|
||||
},
|
||||
|
||||
'html': {
|
||||
'extends': 'common',
|
||||
'filters': 'html',
|
||||
'snippets': {
|
||||
'cc:ie6': '<!--[if lte IE 6]>\n\t${child}|\n<![endif]-->',
|
||||
'cc:ie': '<!--[if IE]>\n\t${child}|\n<![endif]-->',
|
||||
'cc:noie': '<!--[if !IE]><!-->\n\t${child}|\n<!--<![endif]-->',
|
||||
'html:4t': '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n' +
|
||||
'<html lang="${lang}">\n' +
|
||||
'<head>\n' +
|
||||
' <meta http-equiv="Content-Type" content="text/html;charset=${charset}">\n' +
|
||||
' <title></title>\n' +
|
||||
'</head>\n' +
|
||||
'<body>\n\t${child}|\n</body>\n' +
|
||||
'</html>',
|
||||
|
||||
'html:4s': '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n' +
|
||||
'<html lang="${lang}">\n' +
|
||||
'<head>\n' +
|
||||
' <meta http-equiv="Content-Type" content="text/html;charset=${charset}">\n' +
|
||||
' <title></title>\n' +
|
||||
'</head>\n' +
|
||||
'<body>\n\t${child}|\n</body>\n' +
|
||||
'</html>',
|
||||
|
||||
'html:xt': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n' +
|
||||
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${lang}">\n' +
|
||||
'<head>\n' +
|
||||
' <meta http-equiv="Content-Type" content="text/html;charset=${charset}" />\n' +
|
||||
' <title></title>\n' +
|
||||
'</head>\n' +
|
||||
'<body>\n\t${child}|\n</body>\n' +
|
||||
'</html>',
|
||||
|
||||
'html:xs': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' +
|
||||
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${lang}">\n' +
|
||||
'<head>\n' +
|
||||
' <meta http-equiv="Content-Type" content="text/html;charset=${charset}" />\n' +
|
||||
' <title></title>\n' +
|
||||
'</head>\n' +
|
||||
'<body>\n\t${child}|\n</body>\n' +
|
||||
'</html>',
|
||||
|
||||
'html:xxs': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n' +
|
||||
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${lang}">\n' +
|
||||
'<head>\n' +
|
||||
' <meta http-equiv="Content-Type" content="text/html;charset=${charset}" />\n' +
|
||||
' <title></title>\n' +
|
||||
'</head>\n' +
|
||||
'<body>\n\t${child}|\n</body>\n' +
|
||||
'</html>',
|
||||
|
||||
'html:5': '<!DOCTYPE HTML>\n' +
|
||||
'<html lang="${locale}">\n' +
|
||||
'<head>\n' +
|
||||
' <meta charset="${charset}">\n' +
|
||||
' <title></title>\n' +
|
||||
'</head>\n' +
|
||||
'<body>\n\t${child}|\n</body>\n' +
|
||||
'</html>'
|
||||
},
|
||||
|
||||
'abbreviations': {
|
||||
'a': '<a href=""></a>',
|
||||
'a:link': '<a href="http://|"></a>',
|
||||
'a:mail': '<a href="mailto:|"></a>',
|
||||
'abbr': '<abbr title=""></abbr>',
|
||||
'acronym': '<acronym title=""></acronym>',
|
||||
'base': '<base href="" />',
|
||||
'bdo': '<bdo dir=""></bdo>',
|
||||
'bdo:r': '<bdo dir="rtl"></bdo>',
|
||||
'bdo:l': '<bdo dir="ltr"></bdo>',
|
||||
'link:css': '<link rel="stylesheet" type="text/css" href="|style.css" media="all" />',
|
||||
'link:print': '<link rel="stylesheet" type="text/css" href="|print.css" media="print" />',
|
||||
'link:favicon': '<link rel="shortcut icon" type="image/x-icon" href="|favicon.ico" />',
|
||||
'link:touch': '<link rel="apple-touch-icon" href="|favicon.png" />',
|
||||
'link:rss': '<link rel="alternate" type="application/rss+xml" title="RSS" href="|rss.xml" />',
|
||||
'link:atom': '<link rel="alternate" type="application/atom+xml" title="Atom" href="atom.xml" />',
|
||||
'meta:utf': '<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />',
|
||||
'meta:win': '<meta http-equiv="Content-Type" content="text/html;charset=Win-1251" />',
|
||||
'meta:compat': '<meta http-equiv="X-UA-Compatible" content="IE=7" />',
|
||||
'style': '<style type="text/css"></style>',
|
||||
'script': '<script type="text/javascript"></script>',
|
||||
'script:src': '<script type="text/javascript" src=""></script>',
|
||||
'img': '<img src="" alt="" />',
|
||||
'iframe': '<iframe src="" frameborder="0"></iframe>',
|
||||
'embed': '<embed src="" type="" />',
|
||||
'object': '<object data="" type=""></object>',
|
||||
'param': '<param name="" value="" />',
|
||||
'map': '<map name=""></map>',
|
||||
'area': '<area shape="" coords="" href="" alt="" />',
|
||||
'area:d': '<area shape="default" href="" alt="" />',
|
||||
'area:c': '<area shape="circle" coords="" href="" alt="" />',
|
||||
'area:r': '<area shape="rect" coords="" href="" alt="" />',
|
||||
'area:p': '<area shape="poly" coords="" href="" alt="" />',
|
||||
'link': '<link rel="stylesheet" href="" />',
|
||||
'form': '<form action=""></form>',
|
||||
'form:get': '<form action="" method="get"></form>',
|
||||
'form:post': '<form action="" method="post"></form>',
|
||||
'label': '<label for=""></label>',
|
||||
'input': '<input type="" />',
|
||||
'input:hidden': '<input type="hidden" name="" />',
|
||||
'input:h': '<input type="hidden" name="" />',
|
||||
'input:text': '<input type="text" name="" id="" />',
|
||||
'input:t': '<input type="text" name="" id="" />',
|
||||
'input:search': '<input type="search" name="" id="" />',
|
||||
'input:email': '<input type="email" name="" id="" />',
|
||||
'input:url': '<input type="url" name="" id="" />',
|
||||
'input:password': '<input type="password" name="" id="" />',
|
||||
'input:p': '<input type="password" name="" id="" />',
|
||||
'input:datetime': '<input type="datetime" name="" id="" />',
|
||||
'input:date': '<input type="date" name="" id="" />',
|
||||
'input:datetime-local': '<input type="datetime-local" name="" id="" />',
|
||||
'input:month': '<input type="month" name="" id="" />',
|
||||
'input:week': '<input type="week" name="" id="" />',
|
||||
'input:time': '<input type="time" name="" id="" />',
|
||||
'input:number': '<input type="number" name="" id="" />',
|
||||
'input:color': '<input type="color" name="" id="" />',
|
||||
'input:checkbox': '<input type="checkbox" name="" id="" />',
|
||||
'input:c': '<input type="checkbox" name="" id="" />',
|
||||
'input:radio': '<input type="radio" name="" id="" />',
|
||||
'input:r': '<input type="radio" name="" id="" />',
|
||||
'input:range': '<input type="range" name="" id="" />',
|
||||
'input:file': '<input type="file" name="" id="" />',
|
||||
'input:f': '<input type="file" name="" id="" />',
|
||||
'input:submit': '<input type="submit" value="" />',
|
||||
'input:s': '<input type="submit" value="" />',
|
||||
'input:image': '<input type="image" src="" alt="" />',
|
||||
'input:i': '<input type="image" src="" alt="" />',
|
||||
'input:reset': '<input type="reset" value="" />',
|
||||
'input:button': '<input type="button" value="" />',
|
||||
'input:b': '<input type="button" value="" />',
|
||||
'select': '<select name="" id=""></select>',
|
||||
'option': '<option value=""></option>',
|
||||
'textarea': '<textarea name="" id="" cols="30" rows="10"></textarea>',
|
||||
'menu:context': '<menu type="context"></menu>',
|
||||
'menu:c': '<menu type="context"></menu>',
|
||||
'menu:toolbar': '<menu type="toolbar"></menu>',
|
||||
'menu:t': '<menu type="toolbar"></menu>',
|
||||
'video': '<video src=""></video>',
|
||||
'audio': '<audio src=""></audio>',
|
||||
'html:xml': '<html xmlns="http://www.w3.org/1999/xhtml"></html>',
|
||||
'bq': '<blockquote></blockquote>',
|
||||
'acr': '<acronym></acronym>',
|
||||
'fig': '<figure></figure>',
|
||||
'ifr': '<iframe></iframe>',
|
||||
'emb': '<embed></embed>',
|
||||
'obj': '<object></object>',
|
||||
'src': '<source></source>',
|
||||
'cap': '<caption></caption>',
|
||||
'colg': '<colgroup></colgroup>',
|
||||
'fst': '<fieldset></fieldset>',
|
||||
'btn': '<button></button>',
|
||||
'optg': '<optgroup></optgroup>',
|
||||
'opt': '<option></option>',
|
||||
'tarea': '<textarea></textarea>',
|
||||
'leg': '<legend></legend>',
|
||||
'sect': '<section></section>',
|
||||
'art': '<article></article>',
|
||||
'hdr': '<header></header>',
|
||||
'ftr': '<footer></footer>',
|
||||
'adr': '<address></address>',
|
||||
'dlg': '<dialog></dialog>',
|
||||
'str': '<strong></strong>',
|
||||
'prog': '<progress></progress>',
|
||||
'fset': '<fieldset></fieldset>',
|
||||
'datag': '<datagrid></datagrid>',
|
||||
'datal': '<datalist></datalist>',
|
||||
'kg': '<keygen></keygen>',
|
||||
'out': '<output></output>',
|
||||
'det': '<details></details>',
|
||||
'cmd': '<command></command>',
|
||||
|
||||
# expandos
|
||||
'ol+': 'ol>li',
|
||||
'ul+': 'ul>li',
|
||||
'dl+': 'dl>dt+dd',
|
||||
'map+': 'map>area',
|
||||
'table+': 'table>tr>td',
|
||||
'colgroup+': 'colgroup>col',
|
||||
'colg+': 'colgroup>col',
|
||||
'tr+': 'tr>td',
|
||||
'select+': 'select>option',
|
||||
'optgroup+': 'optgroup>option',
|
||||
'optg+': 'optgroup>option'
|
||||
|
||||
},
|
||||
|
||||
'element_types': {
|
||||
'empty': 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,keygen,command',
|
||||
'block_level': 'address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,link,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul,h1,h2,h3,h4,h5,h6',
|
||||
'inline_level': 'a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'
|
||||
}
|
||||
},
|
||||
|
||||
'xsl': {
|
||||
'extends': 'common,html',
|
||||
'filters': 'html, xsl',
|
||||
'abbreviations': {
|
||||
'tm': '<xsl:template match="" mode=""></xsl:template>',
|
||||
'tmatch': 'tm',
|
||||
'tn': '<xsl:template name=""></xsl:template>',
|
||||
'tname': 'tn',
|
||||
'xsl:when': '<xsl:when test=""></xsl:when>',
|
||||
'wh': 'xsl:when',
|
||||
'var': '<xsl:variable name="">|</xsl:variable>',
|
||||
'vare': '<xsl:variable name="" select=""/>',
|
||||
'if': '<xsl:if test=""></xsl:if>',
|
||||
'call': '<xsl:call-template name=""/>',
|
||||
'attr': '<xsl:attribute name=""></xsl:attribute>',
|
||||
'wp': '<xsl:with-param name="" select=""/>',
|
||||
'par': '<xsl:param name="" select=""/>',
|
||||
'val': '<xsl:value-of select=""/>',
|
||||
'co': '<xsl:copy-of select=""/>',
|
||||
'each': '<xsl:for-each select=""></xsl:for-each>',
|
||||
'ap': '<xsl:apply-templates select="" mode=""/>',
|
||||
|
||||
# expandos
|
||||
'choose+': 'xsl:choose>xsl:when+xsl:otherwise'
|
||||
}
|
||||
},
|
||||
|
||||
'haml': {
|
||||
'filters': 'haml',
|
||||
'extends': 'html'
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+17
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>kCode</string>
|
||||
<key>ordering</key>
|
||||
<array>
|
||||
<string>zen-remove-tag-3</string>
|
||||
<string>zen-balance-outward-2</string>
|
||||
<string>EF1F0ADB-1A4D-411A-9BDB-FF9837072CB2</string>
|
||||
<string>FC3E137B-6E92-40C9-BAF6-ED13182D7269</string>
|
||||
</array>
|
||||
<key>uuid</key>
|
||||
<string>3F3B4BEF-F826-47EA-8575-876F1AEBB999</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
sparkup
|
||||
Reference in New Issue
Block a user