Last active
June 10, 2021 00:23
-
-
Save aponxi/4361669 to your computer and use it in GitHub Desktop.
Sublime Text 2 Plugin Examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#https://github.com/a-sk/sublime-build-on-save/blob/master/autobuild.py | |
from os.path import splitext, basename, dirname, join | |
import sublime_plugin | |
class AutoBuild(sublime_plugin.EventListener): | |
def on_post_save(self, view): | |
file_path = view.file_name() | |
working_dir = dirname(file_path) | |
file_name, file_type = splitext(basename(file_path)) | |
redir = False | |
if file_type == '.coffee': | |
cmd = ["/usr/local/bin/coffee", "-c", file_path] | |
elif file_type == '.less': | |
output_file = join(working_dir, file_name + '.css') | |
cmd = ["/usr/local/lib/node_modules/less/bin/lessc", | |
file_path] | |
elif file_type == '.sass': | |
output_file = join(working_dir, file_name + '.css') | |
cmd = ["/usr/local/bin/sass", file_path, output_file] | |
elif file_type == '.scss': | |
output_file = join(working_dir, file_name + '.css') | |
cmd = ["/usr/local/bin/scss", file_path, output_file] | |
else: | |
return [] | |
# finally execute our command | |
view.window().run_command("exec_redir", {"cmd": cmd, "quiet": True, | |
"redir": redir}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#https://github.com/sublimator/ZenCoding/blob/master/sublimezenplugin.py | |
#coding: utf8 | |
#################################### IMPORTS ################################### | |
# Std Libs | |
import operator | |
import os | |
import sys | |
# Sublime Libs | |
import sublime | |
import sublime_plugin | |
# Zen Coding libs | |
from zencoding.parser.abbreviation import ZenInvalidAbbreviation | |
from zencoding import resources as zcr | |
# Dynamic Snippet Base Class | |
from dynamicsnippets import CommandsAsYouTypeBase | |
import zencoding | |
import zencoding.actions | |
from sublimezen import ( expand_abbr, editor, css_sorted, | |
css_property_values, multi_selectable, | |
find_css_property, find_tag_name, | |
find_attribute_name, css_prefixer, find_css_selector) | |
from zenmeta import ( CSS_PROP_VALUES, HTML_ELEMENTS_ATTRIBUTES, | |
HTML_ATTRIBUTES_VALUES, CSS_PSEUDO_CLASSES) | |
from zencoding.html_matcher import last_match | |
################################### CONSTANTS ################################## | |
PACKAGE_NAME = os.path.basename(os.getcwdu()) | |
ZEN_GRAMMAR = "Packages/%s/ZenCoding.tmLanguage" % PACKAGE_NAME | |
HTML = 'text.html - source' | |
XML = 'text.xml' | |
HTML_INSIDE_TAG_ANYWHERE = 'text.html meta.tag' | |
HTML_INSIDE_TAG = ( 'text.html meta.tag - string - ' | |
'meta.scope.between-tag-pair.html ' | |
'-punctuation.definition.tag.begin.html') | |
HTML_INSIDE_TAG_ATTRIBUTE = 'text.html meta.tag string' | |
HTML_NOT_INSIDE_TAG = 'text.html - meta.tag' | |
CSS = 'source.css, source.scss' | |
CSS_PROPERTY = 'meta.property-list.css - meta.property-value.css' | |
CSS_SELECTOR = 'meta.selector.css, source.css - meta, source.scss - meta' | |
CSS_PROPERTY_NAME = 'meta.property-list.css meta.property-name.css' | |
CSS_PREFIXER = 'meta.property-list.css, meta.selector.css' | |
CSS_VALUE = 'meta.property-list.css meta.property-value.css' | |
CSS_ENTITY_SELECTOR = 'meta.selector.css entity.other.attribute-name' | |
NO_PLUG = sublime.INHIBIT_EXPLICIT_COMPLETIONS | |
NO_BUF = sublime.INHIBIT_WORD_COMPLETIONS | |
ZEN_SCOPE = ', '.join([HTML, XML, CSS]) | |
#################################### AUTHORS ################################### | |
__version__ = '1.5.0a' | |
__zen_version__ = '0.7' | |
__authors__ = ['"Sergey Chikuyonok" <serge.che@gmail.com>' | |
'"Вадим Макеев" <pepelsbey@gmail.com>', | |
'"Nicholas Dudfield" <ndudfield@gmail.com>'] | |
################################### SETTINGS ################################### | |
zen_settings = sublime.load_settings('zen-coding.sublime-settings') | |
OPMAP = { | |
sublime.OP_EQUAL : operator.eq, | |
sublime.OP_NOT_EQUAL : operator.ne, | |
} | |
def eval_op(op, operand, operand2): | |
return OPMAP[op](operand, operand2) | |
class ZenSettings(sublime_plugin.EventListener): | |
def on_query_context(self, view, key, op, operand, match_all): | |
if key.startswith('zen_setting'): | |
return eval_op(op, operand, zen_settings.get(key.split('.')[1])) | |
##################################### TODO ##################################### | |
""" | |
Anything referencing `css_sorted` should be updated to be recalculated on zen- | |
codings.sublime-settings change) | |
Installation Docs | |
OSX | |
Windows | |
Linux | |
""" | |
#################################### LOGGING ################################### | |
def debug(f): | |
if zen_settings.get('debug'): | |
# sublime.log_commands(True) | |
frame = sys._getframe(1) | |
if 'debug' in frame.f_code.co_name : frame = sys._getframe(2) | |
line = frame.f_lineno | |
print 'debug:ZenCoding.%s:%s:' % (__name__, line), f | |
def oq_debug(f): | |
debug("on_query_completions %s" % f) | |
################################ MY ZEN SETTINGS ############################### | |
def load_settings(force_reload=False): | |
if not zcr.user_settings or force_reload: | |
my_zen_settings = zen_settings.get('my_zen_settings') | |
if my_zen_settings is not None: | |
debug('loading my_zen_settings from zen-settings.sublime-settings') | |
zcr.set_vocabulary(my_zen_settings, zcr.VOC_USER) | |
assert zcr.vocabularies[zcr.VOC_USER] is my_zen_settings | |
load_settings() | |
if int(sublime.version()) >= 2092: | |
zen_settings.clear_on_change('zen_coding') | |
zen_settings.add_on_change('zen_coding', | |
lambda: load_settings(force_reload=1)) | |
################################### ARBITRAGE ################################## | |
######################## REMOVE HTML/HTML_COMPLETIONS.PY ####################### | |
def remove_html_completions(): | |
import sublime_plugin | |
for completer in "TagCompletions", "HtmlCompletions": | |
try: | |
import html_completions | |
cm = getattr(html_completions, completer) | |
except (ImportError, AttributeError): | |
debug('Unable to find `html_completions.HtmlCompletions`') | |
continue | |
completions = sublime_plugin.all_callbacks['on_query_completions'] | |
for i, instance in enumerate (completions): | |
if isinstance(instance, cm): | |
debug('on_query_completion: removing: %s' % cm) | |
del completions[i] | |
# The funky loader | |
if debug: debug('on_query_completion: callbacks: %r' % completions) | |
sublime.set_timeout(remove_html_completions, 2000) | |
########################## DYNAMIC ZEN CODING SNIPPETS ######################### | |
class ZenAsYouType(CommandsAsYouTypeBase): | |
default_input = 'div' | |
input_message = "Enter Koan: " | |
grammar = ZEN_GRAMMAR | |
def filter_input(self, abbr): | |
try: | |
return expand_abbr(abbr, super_profile='no_check_valid') | |
except Exception: | |
"dont litter the console" | |
class WrapZenAsYouType(CommandsAsYouTypeBase): | |
default_input = 'div' | |
input_message = "Enter Haiku: " | |
grammar = ZEN_GRAMMAR | |
def run_command(self, view, cmd_input): | |
try: | |
ex = expand_abbr(cmd_input, super_profile='no_check_valid') | |
p = editor.get_profile_name() + '.no_check_valid' | |
if not ex.strip(): | |
raise ZenInvalidAbbreviation('Empty expansion %r' % ex) | |
except Exception: | |
return False | |
view.run_command ( | |
'run_zen_action', | |
dict(action="wrap_with_abbreviation", | |
abbr=cmd_input, profile_name=p)) | |
################################ RUN ZEN ACTION ################################ | |
class RunZenAction(sublime_plugin.TextCommand): | |
last_matches = [] | |
@multi_selectable | |
def run(self, view, contexter, kw): | |
matches = [] | |
for i, selection in enumerate(contexter): | |
args = kw.copy() | |
if self.last_matches and not i >= len(self.last_matches): | |
last_match.update(self.last_matches[i]) | |
zencoding.run_action(args.pop('action'), editor, **args) | |
matches.append(last_match.copy()) | |
self.last_matches = matches | |
################################# ZEN MNEMONIC ################################# | |
class ZenCssMnemonic(sublime_plugin.WindowCommand): | |
" Insert css snippets from QuickPanel" | |
def is_enabled(self, **args): | |
return len(self.window.active_view().sel()) == 1 | |
def run(self, prop_value=False): | |
window = self.window | |
view = window.active_view() | |
if prop_value: | |
pos = view.sel()[0].b | |
prop = find_css_property(window.active_view(), pos) | |
forpanel = sorted((css_property_values.get(prop) or {}).items()) | |
contents = lambda i: forpanel[i][1] | |
# TODO expand while selector matches | |
"meta.property-value.css - punctuation" | |
# Then insert snippet over top of selection | |
else: | |
forpanel = css_sorted | |
contents = lambda i: expand_abbr(forpanel[i][0]) | |
def done(i): | |
if i != -1: | |
view.run_command('insert_snippet', {'contents': contents(i)}) | |
display = [[v,k] for k,v in forpanel] | |
window.show_quick_panel(display, done) | |
################################### CONTEXTS ################################### | |
class ZenListener(sublime_plugin.EventListener): | |
def correct_syntax(self, view): | |
return view.match_selector( view.sel()[0].b, ZEN_SCOPE ) | |
def css_selectors(self, view, prefix, pos): | |
elements = [ (v, v) for v in | |
sorted(HTML_ELEMENTS_ATTRIBUTES.keys()) if v != prefix] | |
if view.syntax_name(pos).strip() in ('source.scss', 'source.css'): | |
return elements | |
else: | |
selector = find_css_selector(view, pos) | |
oq_debug('css_selectors selector: %r' % selector) | |
if ':' in selector: | |
prefix = selector.rsplit(':', 1)[-1] | |
return [ ( prefix, (':' + p), p.replace('|', '$1') ) for p in | |
CSS_PSEUDO_CLASSES if | |
not prefix or p.startswith(prefix[0].lower() ) ] | |
elif selector.startswith('.'): | |
return [] | |
# return [] | |
return [(selector, v, v) for v in | |
set(map(view.substr, [ | |
r for r in view.find_by_selector('source.css ' | |
'meta.selector.css entity.other.attribute-name.class.css') | |
if not r.contains(pos)] ))] | |
else: | |
return elements | |
def css_property_values(self, view, prefix, pos): | |
prefix = css_prefixer(view, pos) | |
prop = find_css_property(view, pos) | |
print `prop` | |
# These `values` are sourced from all the fully specified zen abbrevs | |
# `d:n` => `display:none` so `display:n{tab}` will yield `none` | |
values = css_property_values.get(prop) | |
if values and prefix and prefix in values: | |
oq_debug("zcprop:val prop: %r values: %r" % (prop, values)) | |
return [(d, '%s\t(%s)' % (v, d), v) for d,v in sorted(values.items())] | |
else: | |
# Look for values relating to that property | |
# Remove exact matches, so a \t is inserted | |
values = [v for v in CSS_PROP_VALUES.get(prop, []) if v != prefix] | |
if values: | |
debug("zenmeta:val prop: %r values: %r" % (prop, values)) | |
return [(v, v, v) for v in values] | |
# return [(v, '%s\t(%s)' % (v, v), v) for v in values] | |
def html_elements_attributes(self, view, prefix, pos): | |
tag = find_tag_name(view, pos) | |
values = HTML_ELEMENTS_ATTRIBUTES.get(tag, []) | |
return [(v, '%s\t@%s' % (v,v), '%s="$1"' % v) for v in values] | |
def html_attributes_values(self, view, prefix, pos): | |
attr = find_attribute_name(view, pos) | |
values = HTML_ATTRIBUTES_VALUES.get(attr, []) | |
return [(v, '%s\t@=%s' % (v,v), v) for v in values] | |
def on_query_completions(self, view, prefix, locations): | |
if ( not self.correct_syntax(view) or | |
zen_settings.get('disable_completions', False) ): return [] | |
black_list = zen_settings.get('completions_blacklist', []) | |
# We need to use one function rather than discrete listeners so as to | |
# avoid pollution with less specific completions. Try to return early | |
# with the most specific match possible. | |
oq_debug("prefix: %r" % prefix) | |
# A mapping of scopes, sub scopes and handlers, first matching of which | |
# is used. | |
COMPLETIONS = ( | |
(CSS_SELECTOR, self.css_selectors), | |
(CSS_VALUE, self.css_property_values), | |
(HTML_INSIDE_TAG, self.html_elements_attributes), | |
(HTML_INSIDE_TAG_ATTRIBUTE, self.html_attributes_values) ) | |
pos = view.sel()[0].b | |
# Try to find some more specific contextual abbreviation | |
for sub_selector, handler in COMPLETIONS: | |
h_name = handler.__name__ | |
if h_name in black_list: continue | |
if ( view.match_selector(pos, sub_selector) or | |
view.match_selector(pos -1, sub_selector )): | |
c = h_name, prefix | |
oq_debug('handler: %r prefix: %r' % c) | |
oq_debug('pos: %r scope: %r' % (pos, view.syntax_name(pos))) | |
completions = handler(view, prefix, pos) | |
oq_debug('completions: %r' % completions) | |
if completions: | |
if h_name == 'css_selectors': | |
return completions | |
else: | |
return completions#, # NO_BUF | NO_PLUG) | |
do_zen_expansion = True | |
html_scope_for_zen = ("text.html meta.tag " | |
"-meta.scope.between-tag-pair.html " | |
"-punctuation.definition.tag.begin.html") | |
if view.match_selector(pos, 'text.html'): | |
if view.match_selector(pos, html_scope_for_zen): | |
do_zen_expansion = False | |
if do_zen_expansion: | |
# Expand Zen expressions such as `d:n+m:a` or `div*5` | |
try: | |
abbr = zencoding.actions.basic.find_abbreviation(editor) | |
oq_debug('abbr: %r' % abbr) | |
if abbr and not view.match_selector( locations[0], | |
HTML_INSIDE_TAG ): | |
result = expand_abbr(abbr) | |
oq_debug('expand_abbr abbr: %r result: %r' % (abbr, result)) | |
if result: | |
return ( | |
[(abbr, result, result)], | |
# 0, | |
NO_BUF #| NO_PLUG | |
) | |
except ZenInvalidAbbreviation: | |
pass | |
# If it wasn't a valid Zen css snippet, or the prefix is empty '' | |
# then get warm and fuzzy with css properties. | |
# TODO, before or after this, fuzz directly against the zen snippets | |
# eg `tjd` matching `tj:d` to expand `text-justify:distribute;` | |
if ( view.match_selector(pos, CSS_PROPERTY) and | |
not 'css_properties' in black_list ): | |
# Use this to get non \w based prefixes | |
prefix = css_prefixer(view, pos) | |
properties = sorted(CSS_PROP_VALUES.keys()) | |
# 'a'.startswith('') is True! so will never get IndexError below | |
exacts = [p for p in properties if p.startswith(prefix)] | |
if exacts: properties = exacts | |
else: properties = [ p for p in properties if | |
# to allow for fuzzy, which will | |
# generally start with first letter | |
p.strip('-').startswith(prefix[0].lower()) ] | |
oq_debug('css_property exact: %r prefix: %r properties: %r' % ( | |
bool(exacts), prefix, properties )) | |
return ([ (prefix, v +'\t'+'zen:css_properties', '%s:$1;' % v) for v in properties ], | |
NO_BUF) | |
else: | |
return [] | |
@staticmethod | |
def check_context(view): | |
abbr = zencoding.actions.basic.find_abbreviation(editor) | |
if abbr: | |
try: result = expand_abbr(abbr) | |
except ZenInvalidAbbreviation: return None | |
if result: | |
return abbr, result | |
def on_query_context(self, view, key, op, operand, match_all): | |
# TODO: is_zen, honour match_all, so that the extracted prefix is | |
# the same on all sides | |
if key == 'is_zen': | |
debug('checking iz_zen context') | |
context = ZenListener.check_context(view) | |
if context is not None: | |
abbr, result = context | |
if match_all == True: | |
n = len(abbr) | |
for sel in view.sel(): | |
a = view.substr(sublime.Region(sel.b-n, sel.b)) | |
if not a == abbr: | |
return False | |
view.settings().set("zen_abbrev_cache", [abbr, result]) | |
debug('is_zen context enabled') | |
return True | |
else: | |
debug('is_zen context disabled') | |
return False | |
################################################################################ | |
class ExpandZenAbbreviationOnTab(sublime_plugin.TextCommand): | |
""" | |
This command is for when, on `<tab>`, you want to expand abbreviations in | |
contexts that on_query_completion will not, namely when the characters | |
preceding the cursor are word separators. | |
The default zen actions are not multi select aware. | |
""" | |
def run(self, edit): | |
view = self.view | |
settings = view.settings() | |
abbr, result = settings.get("zen_abbrev_cache") | |
settings.erase('zen_abbrev_cache') | |
n = len(abbr) | |
for sel in view.sel(): | |
view.erase(edit, sublime.Region(sel.b-n, sel.b)) | |
view.run_command('insert_snippet', {"contents": result}) | |
################################################################################ | |
class SetHtmlSyntaxAndInsertSkel(sublime_plugin.TextCommand): | |
def run(self, edit, doctype=None): | |
view = self.view | |
syntax = zen_settings.get( 'default_html_syntax', | |
'Packages/HTML/HTML.tmLanguage' ) | |
view.set_syntax_file(syntax) | |
view.run_command( 'insert_snippet', | |
{'contents': expand_abbr('html:%s' % doctype)} ) | |
################################################################################A |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment