712 lines
19 KiB
Python
712 lines
19 KiB
Python
import sublime
|
|
import sublime_plugin
|
|
|
|
import re
|
|
import imp
|
|
import json
|
|
import sys
|
|
import os.path
|
|
import traceback
|
|
|
|
BASE_PATH = os.path.abspath(os.path.dirname(__file__))
|
|
PACKAGES_PATH = sublime.packages_path() or os.path.dirname(BASE_PATH)
|
|
# EMMET_GRAMMAR = os.path.join(BASE_PATH, 'Emmet.tmLanguage')
|
|
EMMET_GRAMMAR = 'Packages/%s/Emmet.tmLanguage' % os.path.basename(BASE_PATH)
|
|
sys.path += [BASE_PATH] + [os.path.join(BASE_PATH, f) for f in ['emmet_completions', 'emmet']]
|
|
|
|
|
|
# Make sure all dependencies are reloaded on upgrade
|
|
if 'emmet.reloader' in sys.modules:
|
|
imp.reload(sys.modules['emmet.reloader'])
|
|
import emmet.reloader
|
|
|
|
# import completions as cmpl
|
|
import emmet.pyv8loader as pyv8loader
|
|
import emmet_completions as cmpl
|
|
from emmet_completions.meta import HTML_ELEMENTS_ATTRIBUTES, HTML_ATTRIBUTES_VALUES
|
|
from emmet.context import Context
|
|
from emmet.context import js_file_reader as _js_file_reader
|
|
from emmet.pyv8loader import LoaderDelegate
|
|
|
|
__version__ = '1.1'
|
|
__core_version__ = '1.0'
|
|
__authors__ = ['"Sergey Chikuyonok" <serge.che@gmail.com>'
|
|
'"Nicholas Dudfield" <ndudfield@gmail.com>']
|
|
|
|
is_python3 = sys.version_info[0] > 2
|
|
|
|
# JS context
|
|
ctx = None
|
|
# Emmet Settings
|
|
settings = None
|
|
|
|
# Default ST settings
|
|
user_settings = None
|
|
|
|
def is_st3():
|
|
return sublime.version()[0] == '3'
|
|
|
|
def js_file_reader(file_path, use_unicode=True):
|
|
if hasattr(sublime, 'load_resource'):
|
|
rel_path = file_path
|
|
for prefix in [sublime.packages_path(), sublime.installed_packages_path()]:
|
|
if rel_path.startswith(prefix):
|
|
rel_path = os.path.join('Packages', rel_path[len(prefix) + 1:])
|
|
break
|
|
|
|
rel_path = rel_path.replace('.sublime-package', '')
|
|
# for Windows we have to replace slashes
|
|
rel_path = rel_path.replace('\\', '/')
|
|
return sublime.load_resource(rel_path)
|
|
|
|
return _js_file_reader(file_path, use_unicode)
|
|
|
|
def init():
|
|
"Init Emmet plugin"
|
|
# load settings
|
|
globals()['user_settings'] = sublime.load_settings('Preferences.sublime-settings')
|
|
globals()['settings'] = sublime.load_settings('Emmet.sublime-settings')
|
|
settings.add_on_change('extensions_path', update_settings)
|
|
|
|
# setup environment for PyV8 loading
|
|
pyv8_paths = [
|
|
os.path.join(PACKAGES_PATH, 'PyV8'),
|
|
os.path.join(PACKAGES_PATH, 'PyV8', pyv8loader.get_arch()),
|
|
os.path.join(PACKAGES_PATH, 'PyV8', 'pyv8-%s' % pyv8loader.get_arch())
|
|
]
|
|
|
|
sys.path += pyv8_paths
|
|
|
|
# unpack recently loaded binary, is exists
|
|
for p in pyv8_paths:
|
|
pyv8loader.unpack_pyv8(p)
|
|
|
|
# provide some contributions to JS
|
|
contrib = {
|
|
'sublime': sublime,
|
|
'sublimeReplaceSubstring': replace_substring,
|
|
'sublimeGetOption': settings.get
|
|
}
|
|
|
|
# create JS environment
|
|
delegate = SublimeLoaderDelegate()
|
|
globals()['ctx'] = Context(
|
|
files=['../editor.js'],
|
|
ext_path=settings.get('extensions_path', None),
|
|
contrib=contrib,
|
|
logger=delegate.log,
|
|
reader=js_file_reader
|
|
)
|
|
|
|
update_settings()
|
|
|
|
pyv8loader.load(pyv8_paths[1], delegate)
|
|
|
|
if settings.get('remove_html_completions', False):
|
|
sublime.set_timeout(cmpl.remove_html_completions, 2000)
|
|
|
|
class SublimeLoaderDelegate(LoaderDelegate):
|
|
def __init__(self, settings=None):
|
|
|
|
if settings is None:
|
|
settings = {}
|
|
for k in ['http_proxy', 'https_proxy', 'timeout']:
|
|
if user_settings.has(k):
|
|
settings[k] = user_settings.get(k, None)
|
|
|
|
LoaderDelegate.__init__(self, settings)
|
|
self.state = None
|
|
self.message = 'Loading PyV8 binary, please wait'
|
|
self.i = 0
|
|
self.addend = 1
|
|
self.size = 8
|
|
|
|
def on_start(self, *args, **kwargs):
|
|
self.state = 'loading'
|
|
|
|
def on_progress(self, *args, **kwargs):
|
|
if kwargs['progress'].is_background:
|
|
return
|
|
|
|
before = self.i % self.size
|
|
after = (self.size - 1) - before
|
|
msg = '%s [%s=%s]' % (self.message, ' ' * before, ' ' * after)
|
|
if not after:
|
|
self.addend = -1
|
|
if not before:
|
|
self.addend = 1
|
|
self.i += self.addend
|
|
|
|
sublime.set_timeout(lambda: sublime.status_message(msg), 0)
|
|
|
|
def on_complete(self, *args, **kwargs):
|
|
self.state = 'complete'
|
|
|
|
if kwargs['progress'].is_background:
|
|
return
|
|
|
|
sublime.set_timeout(lambda: sublime.status_message('PyV8 binary successfully loaded'), 0)
|
|
|
|
def on_error(self, exit_code=-1, thread=None):
|
|
self.state = 'error'
|
|
sublime.set_timeout(lambda: show_pyv8_error(exit_code), 0)
|
|
|
|
def setting(self, name, default=None):
|
|
"Returns specified setting name"
|
|
return self.settings.get(name, default)
|
|
|
|
def log(self, message):
|
|
print('Emmet: %s' % message)
|
|
|
|
def show_pyv8_error(exit_code):
|
|
if 'PyV8' not in sys.modules:
|
|
sublime.error_message('Error while loading PyV8 binary: exit code %s \nTry to manually install PyV8 from\nhttps://github.com/emmetio/pyv8-binaries' % exit_code)
|
|
|
|
def active_view():
|
|
return sublime.active_window().active_view()
|
|
|
|
def check_context(verbose=False):
|
|
"Checks if JS context is completely available"
|
|
if not ctx.js():
|
|
if verbose:
|
|
sublime.message_dialog('Please wait a bit while PyV8 binary is being downloaded')
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def replace_substring(start, end, value, no_indent=False):
|
|
view = active_view()
|
|
|
|
view.sel().clear()
|
|
view.sel().add(sublime.Region(start, end or start))
|
|
|
|
if not is_python3:
|
|
value = value.decode('utf-8')
|
|
|
|
# XXX a bit naive indentation control. It handles most common
|
|
# `no_indent` usages like replacing CSS rule content, but may not
|
|
# produce expected result in all possible situations
|
|
|
|
if no_indent:
|
|
line = view.substr(view.line(view.sel()[0]))
|
|
value = unindent_text(value, get_line_padding(line))
|
|
|
|
view.run_command('insert_snippet', {'contents': value})
|
|
|
|
def unindent_text(text, pad):
|
|
"""
|
|
Removes padding at the beginning of each text's line
|
|
@type text: str
|
|
@type pad: str
|
|
"""
|
|
lines = text.splitlines()
|
|
|
|
for i,line in enumerate(lines):
|
|
if line.startswith(pad):
|
|
lines[i] = line[len(pad):]
|
|
|
|
return '\n'.join(lines)
|
|
|
|
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 update_settings():
|
|
ctx.set_ext_path(settings.get('extensions_path', None))
|
|
|
|
keys = ['snippets', 'preferences', 'syntaxProfiles', 'profiles']
|
|
payload = {}
|
|
for k in keys:
|
|
data = settings.get(k, None)
|
|
if data:
|
|
payload[k] = data
|
|
|
|
ctx.reset()
|
|
ctx.load_user_data(json.dumps(payload))
|
|
ctx.js()
|
|
|
|
def get_scope(view, pt=-1):
|
|
if pt == -1:
|
|
# use current caret position
|
|
pt = view.sel()[0].begin()
|
|
|
|
if hasattr(view, 'scope_name'):
|
|
return view.scope_name(pt)
|
|
|
|
return view.syntax_name(pt)
|
|
|
|
def should_perform_action(name, view=None):
|
|
if not view:
|
|
view = active_view()
|
|
|
|
# fallback to old check
|
|
if not view.settings().get('enable_emmet_keymap', True):
|
|
return False
|
|
|
|
disabled_actions = settings.get('disabled_keymap_actions', '')
|
|
|
|
if not disabled_actions: # no disabled actions
|
|
return True
|
|
|
|
if disabled_actions == 'all': # disable all actions
|
|
return False
|
|
|
|
return name not in re.split(r'\s*,\s*', disabled_actions.strip())
|
|
|
|
def should_handle_tab_key(syntax=None):
|
|
view = active_view()
|
|
scopes = settings.get('disabled_single_snippet_for_scopes', None)
|
|
cur_scope = get_scope(view)
|
|
|
|
if sublime.score_selector(cur_scope, 'source.css'):
|
|
return True
|
|
|
|
if not scopes or not sublime.score_selector(cur_scope, scopes):
|
|
return True
|
|
|
|
abbr = ctx.js().locals.pyExtractAbbreviation()
|
|
|
|
disabled_snippets = settings.get('disabled_single_snippets', '').split()
|
|
if disabled_snippets and abbr in disabled_snippets:
|
|
return False
|
|
|
|
if not re.match(r'^[\w\:%]+$', abbr):
|
|
# it's a complex expression
|
|
return True
|
|
|
|
if re.match(r'^(lorem|lipsum)([a-z]{2})?\d*$', abbr):
|
|
# hardcoded Lorem Ipsum generator
|
|
return True
|
|
|
|
# detect inline CSS
|
|
if syntax is None:
|
|
syntax = ctx.js().locals.pyGetSyntax();
|
|
|
|
if syntax == 'css':
|
|
return True
|
|
|
|
known_tags = settings.get('known_html_tags', '').split()
|
|
if abbr in known_tags or ctx.js().locals.pyHasSnippet(abbr):
|
|
return True
|
|
|
|
return False
|
|
|
|
def log(message):
|
|
if settings.get('debug', False):
|
|
print('Emmet: %s' % message)
|
|
|
|
class RunEmmetAction(sublime_plugin.TextCommand):
|
|
def run(self, edit, action=None, **kw):
|
|
run_action(lambda i, sel: ctx.js().locals.pyRunAction(action))
|
|
# ctx.js().locals.pyRunAction(action)
|
|
|
|
class ActionContextHandler(sublime_plugin.EventListener):
|
|
def on_query_context(self, view, key, op, operand, match_all):
|
|
if not key.startswith('emmet_action_enabled.'):
|
|
return None
|
|
|
|
prefix, name = key.split('.')
|
|
return should_perform_action(name, view)
|
|
|
|
def get_edit(view, edit_token=None):
|
|
edit = None
|
|
try:
|
|
edit = view.begin_edit()
|
|
except:
|
|
pass
|
|
|
|
if not edit and edit_token:
|
|
try:
|
|
edit = view.begin_edit(edit_token, 'Emmet')
|
|
except Exception as e:
|
|
pass
|
|
|
|
return edit
|
|
|
|
def run_action(action, view=None):
|
|
if not check_context(True):
|
|
return
|
|
|
|
"Runs Emmet action in multiselection mode"
|
|
if not view:
|
|
view = active_view()
|
|
|
|
region_key = '__emmet__'
|
|
sels = list(view.sel())
|
|
r = ctx.js().locals.pyRunAction
|
|
result = False
|
|
|
|
# edit = get_edit(view, edit_token)
|
|
max_sel_ix = len(sels) - 1
|
|
|
|
try:
|
|
for i, sel in enumerate(reversed(sels)):
|
|
view.sel().clear()
|
|
view.sel().add(sel)
|
|
# run action
|
|
# result = r(name) or result
|
|
result = action(max_sel_ix - i, sel) or result
|
|
|
|
# remember resulting selections
|
|
view.add_regions(region_key,
|
|
(view.get_regions(region_key) + list(view.sel())) , '')
|
|
except Exception as e:
|
|
view.erase_regions(region_key)
|
|
print(traceback.format_exc())
|
|
return
|
|
|
|
|
|
# output all saved regions as selection
|
|
view.sel().clear()
|
|
for sel in view.get_regions(region_key):
|
|
view.sel().add(sel)
|
|
|
|
view.erase_regions(region_key)
|
|
|
|
# if edit:
|
|
# view.end_edit(edit)
|
|
return result
|
|
|
|
class TabAndCompletionsHandler():
|
|
def correct_syntax(self, view, syntax='html'):
|
|
return syntax == 'html' and view.match_selector( view.sel()[0].b, cmpl.EMMET_SCOPE )
|
|
|
|
def completion_handler(self, view):
|
|
"Returns completions handler fo current caret position"
|
|
black_list = settings.get('completions_blacklist', [])
|
|
|
|
# A mapping of scopes, sub scopes and handlers, first matching of which
|
|
# is used.
|
|
COMPLETIONS = (
|
|
(cmpl.HTML_INSIDE_TAG, self.html_elements_attributes),
|
|
(cmpl.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 not black_list or h_name in black_list: continue
|
|
if (view.match_selector(pos, sub_selector) or
|
|
view.match_selector(pos - 1, sub_selector)):
|
|
return handler
|
|
|
|
return None
|
|
|
|
def html_elements_attributes(self, view, prefix, pos):
|
|
tag = cmpl.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 = cmpl.find_attribute_name(view, pos)
|
|
values = HTML_ATTRIBUTES_VALUES.get(attr, [])
|
|
return [(v, '%s\t@=%s' % (v,v), v) for v in values]
|
|
|
|
def expand_by_tab(self, view):
|
|
if not check_context():
|
|
return False;
|
|
|
|
syntax = ctx.js().locals.pyGetSyntax();
|
|
if not should_handle_tab_key(syntax):
|
|
return False
|
|
|
|
# we need to filter out attribute completions if
|
|
# 'disable_completions' option is not active
|
|
if (not settings.get('disable_completions', False) and
|
|
self.correct_syntax(view, syntax) and
|
|
self.completion_handler(view)):
|
|
return None
|
|
|
|
caret_pos = view.sel()[0].begin()
|
|
cur_scope = get_scope(view)
|
|
|
|
# let's see if Tab key expander should be disabled for current scope
|
|
banned_scopes = settings.get('disable_tab_abbreviations_for_scopes', '')
|
|
if banned_scopes and view.score_selector(caret_pos, banned_scopes):
|
|
return None
|
|
|
|
# Sometimes ST2 matcher may incorrectly filter scope context,
|
|
# check it against special regexp
|
|
banned_regexp = settings.get('disable_tab_abbreviations_for_regexp', None)
|
|
if banned_regexp and re.search(banned_regexp, cur_scope):
|
|
return None
|
|
|
|
return run_action(lambda i, sel: ctx.js().locals.pyRunAction('expand_abbreviation'))
|
|
|
|
# view.run_command('run_emmet_action',
|
|
# {'action':'expand_abbreviation'})
|
|
|
|
class ExpandAbbreviationByTab(sublime_plugin.TextCommand):
|
|
def run(self, edit, **kw):
|
|
if settings.get('use_old_tab_handler', False):
|
|
return
|
|
|
|
view = active_view()
|
|
h = TabAndCompletionsHandler()
|
|
if not h.expand_by_tab(view):
|
|
# try to mimic default Tab behaviour of Sublime Text
|
|
view.run_command('insert_best_completion', {
|
|
'default': '\t',
|
|
'exact': user_settings.get('tab_completion', True)
|
|
})
|
|
|
|
|
|
class TabExpandHandler(sublime_plugin.EventListener):
|
|
def on_query_context(self, view, key, op, operand, match_all):
|
|
if key != 'is_abbreviation':
|
|
return None
|
|
|
|
if settings.get('use_old_tab_handler', False):
|
|
h = TabAndCompletionsHandler()
|
|
return h.expand_by_tab(view)
|
|
|
|
return check_context()
|
|
|
|
def on_query_completions(self, view, prefix, locations):
|
|
h = TabAndCompletionsHandler()
|
|
if view.match_selector(locations[0], settings.get('css_completions_scope', '')) and check_context():
|
|
l = []
|
|
if settings.get('show_css_completions', False):
|
|
completions = ctx.js().locals.pyGetCSSCompletions()
|
|
if completions:
|
|
for p in completions:
|
|
l.append(('%s\t%s' % (p['k'], p['label']), p['v']))
|
|
|
|
if not l:
|
|
return []
|
|
|
|
return (l, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS)
|
|
|
|
if not h.correct_syntax(view) or settings.get('disable_completions', False):
|
|
return []
|
|
|
|
handler = h.completion_handler(view)
|
|
if handler:
|
|
pos = view.sel()[0].b
|
|
completions = handler(view, prefix, pos)
|
|
return completions
|
|
|
|
return []
|
|
|
|
|
|
class CommandsAsYouTypeBase(sublime_plugin.TextCommand):
|
|
input_message = "Enter Input"
|
|
default_input = ""
|
|
process_panel_input = lambda s, i: i.title()
|
|
|
|
# Note that this must be of form `Packages/$Package/Emmet.tmLanguage` on ST3
|
|
# NOT an absolute path!
|
|
panel_grammar = EMMET_GRAMMAR
|
|
|
|
def is_enabled(self):
|
|
return True
|
|
|
|
def run_command(self, edit, view, processed_input):
|
|
if '\n' in processed_input:
|
|
for sel in view.sel():
|
|
trailing = sublime.Region(sel.end(), view.line(sel).end())
|
|
if view.substr(trailing).isspace():
|
|
view.erase(edit, trailing)
|
|
|
|
if not is_python3:
|
|
processed_input = processed_input.decode('utf-8')
|
|
view.run_command('insert_snippet', { 'contents': processed_input })
|
|
|
|
def on_panel_change(self, abbr):
|
|
if not abbr and self.erase:
|
|
self.undo()
|
|
self.erase = False
|
|
return
|
|
|
|
def inner_insert():
|
|
self.view.run_command(self.name(), dict(panel_input=abbr))
|
|
# self.view.run_command('hide_auto_complete')
|
|
|
|
self.undo()
|
|
sublime.set_timeout(inner_insert, 0)
|
|
|
|
def undo(self):
|
|
if self.erase:
|
|
sublime.set_timeout(lambda: self.view.run_command('undo'), 0)
|
|
|
|
def remember_sels(self, view):
|
|
self._sels = list(view.sel())
|
|
self._sel_items = []
|
|
|
|
for sel in self._sels:
|
|
# selection should be unindented in order to get desired result
|
|
line = view.substr(view.line(sel))
|
|
s = view.substr(sel)
|
|
self._sel_items.append(unindent_text(s, get_line_padding(line)))
|
|
|
|
def on_panel_done(self, abbr):
|
|
pass
|
|
|
|
def run(self, edit, panel_input=None, **kwargs):
|
|
|
|
if panel_input is None:
|
|
self.setup(edit, self.view, **kwargs)
|
|
self.erase = False
|
|
|
|
panel = self.view.window().show_input_panel (
|
|
self.input_message,
|
|
self.default_input,
|
|
self.on_panel_done, # on_done
|
|
self.on_panel_change, # on_change
|
|
self.undo) # on_cancel
|
|
|
|
panel.sel().clear()
|
|
panel.sel().add(sublime.Region(0, panel.size()))
|
|
|
|
if self.panel_grammar:
|
|
panel.set_syntax_file(self.panel_grammar)
|
|
panel_setting = panel.settings().set
|
|
|
|
panel_setting('line_numbers', False)
|
|
panel_setting('gutter', False)
|
|
panel_setting('auto_complete', False)
|
|
panel_setting('tab_completion', False)
|
|
else:
|
|
self.run_on_input(edit, self.view, panel_input)
|
|
|
|
def setup(self, edit, view, **kwargs):
|
|
pass
|
|
|
|
def run_on_input(self, edit, view, panel_input):
|
|
view = self.view
|
|
cmd_input = self.process_panel_input(panel_input) or ''
|
|
try:
|
|
self.erase = self.run_command(edit, view, cmd_input) is not False
|
|
except:
|
|
pass
|
|
|
|
class WrapAsYouType(CommandsAsYouTypeBase):
|
|
default_input = 'div'
|
|
_prev_output = ''
|
|
input_message = "Enter Wrap Abbreviation: "
|
|
|
|
def setup(self, edit, view, **kwargs):
|
|
self._prev_output = ''
|
|
|
|
if len(view.sel()) == 1:
|
|
# capture wrapping context (parent HTML element)
|
|
# if there is only one selection
|
|
r = ctx.js().locals.pyCaptureWrappingRange()
|
|
if r:
|
|
view.sel().clear()
|
|
view.sel().add(sublime.Region(r[0], r[1]))
|
|
view.show(view.sel())
|
|
|
|
self.remember_sels(view)
|
|
|
|
# override method to correctly wrap abbreviations
|
|
def run_on_input(self, edit, view, abbr):
|
|
# def _real_insert(self, abbr):
|
|
# view = self.view
|
|
# self.edit = get_edit(view, self.edit_token)
|
|
|
|
self.erase = True
|
|
|
|
# restore selections
|
|
view.sel().clear()
|
|
for sel in self._sels:
|
|
view.sel().add(sel)
|
|
|
|
def ins(i, sel):
|
|
try:
|
|
self._prev_output = ctx.js().locals.pyWrapAsYouType(abbr, self._sel_items[i])
|
|
# self.run_command(view, output)
|
|
except Exception:
|
|
"dont litter the console"
|
|
|
|
self.run_command(edit, view, self._prev_output)
|
|
|
|
run_action(ins, view)
|
|
# if self.edit:
|
|
# view.end_edit(self.edit)
|
|
|
|
class ExpandAsYouType(WrapAsYouType):
|
|
default_input = 'div'
|
|
input_message = "Enter Abbreviation: "
|
|
|
|
def setup(self, edit, view, **kwargs):
|
|
# adjust selection to non-space bounds
|
|
sels = []
|
|
for s in view.sel():
|
|
text = view.substr(s)
|
|
a = s.a + len(text) - len(text.lstrip())
|
|
b = s.b - len(text) + len(text.rstrip())
|
|
|
|
sels.append(sublime.Region(a, b))
|
|
|
|
view.sel().clear()
|
|
for s in sels:
|
|
view.sel().add(s)
|
|
|
|
self.remember_sels(active_view())
|
|
|
|
|
|
class EnterKeyHandler(sublime_plugin.EventListener):
|
|
def on_query_context(self, view, key, op, operand, match_all):
|
|
if key != 'clear_fields_on_enter_key':
|
|
return None
|
|
|
|
if settings.get('clear_fields_on_enter_key', False):
|
|
view.run_command('clear_fields')
|
|
|
|
return True
|
|
|
|
|
|
class RenameTag(sublime_plugin.TextCommand):
|
|
def run(self, edit, **kw):
|
|
if not check_context(True):
|
|
return
|
|
|
|
view = active_view()
|
|
sels = list(view.sel())
|
|
sel_cleared = False
|
|
for s in sels:
|
|
ranges = ctx.js().locals.pyGetTagNameRanges(s.begin())
|
|
if ranges:
|
|
if not sel_cleared:
|
|
view.sel().clear()
|
|
sel_cleared = True
|
|
|
|
for r in ranges:
|
|
view.sel().add(sublime.Region(r[0], r[1]))
|
|
view.show(view.sel())
|
|
|
|
class EmmetInsertAttribute(sublime_plugin.TextCommand):
|
|
def run(self, edit, attribute=None, **kw):
|
|
if not attribute:
|
|
return
|
|
|
|
view = active_view()
|
|
prefix = ''
|
|
if view.sel():
|
|
sel = view.sel()[0]
|
|
if not view.substr(sublime.Region(sel.begin() - 1, sel.begin())).isspace():
|
|
prefix = ' '
|
|
|
|
view.run_command('insert_snippet', {'contents': '%s%s="$1"' % (prefix, attribute)})
|
|
|
|
class EmmetResetContext(sublime_plugin.TextCommand):
|
|
def run(self, edit, **kw):
|
|
update_settings()
|
|
|
|
def plugin_loaded():
|
|
sublime.set_timeout(init, 200)
|
|
|
|
##################
|
|
# Init plugin
|
|
if not is_python3:
|
|
init()
|
|
|