-
Notifications
You must be signed in to change notification settings - Fork 4
/
extract_snippets.py
executable file
·152 lines (113 loc) · 5.05 KB
/
extract_snippets.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
################################################################################
# Std Libs
import textwrap
import re
import json
import bisect
import uuid
import pprint
# Sublime Libs
import sublime
import sublime_plugin
# Sublime Default libs
from Default.indentation import normed_indentation_pt
# Edit Preferences imports
from .scheduler import yields_from, input_panel
from .helpers import inversion_stream, invert_regions, get_setting
################################### CONSTANTS ##################################
EXTRACTED_SNIPPETS_SETTING = 'extracted-snippets.sublime-settings'
TAB_STOP_RE = r"(?<!\\)\$(%s)|\$\{(%s)(?::|/)"
TAB_STOP = re.compile(TAB_STOP_RE % ('\\d+', '\\d+'))
################################### COMMANDS ###################################
class ExtractSnippet(sublime_plugin.TextCommand):
def is_enabled(self, args=[]):
return bool( (self.view.sel() or
self.view.get_regions('auto_select')) )
@yields_from
def run(self, edit):
view = self.view
window = view.window()
view.run_command('auto_select', dict(cmd='merge'))
settings = sublime.load_settings(EXTRACTED_SNIPPETS_SETTING)
contents = extract_snippet(view, edit)
trigger = "${1:%s}" % (re.findall('\w+', contents) + [''])[0]
scope = scope_as_snippet(view)
the_snippets = settings.get('extracted_snippets')
the_snippets.append( dict(
scope = (yield from input_panel('Enter Scope', scope)),
trigger = (yield from input_panel('Enter Trigger', trigger)),
uuid = uuid.uuid1().hex,
contents = contents ))
settings.set("extracted_snippets", the_snippets)
sublime.save_settings(EXTRACTED_SNIPPETS_SETTING)
class IncrementTabstops(sublime_plugin.TextCommand):
def run(self, edit, args=[]):
view = self.view
for sel in view.sel():
selection = view.substr(sel)
view.replace (
edit, sel, replace_highest(increment_tabstops(selection)))
################################### LISTENERS ##################################
class ExtractedSnippetsCompletions(sublime_plugin.EventListener):
def on_query_completions(self, view, prefix, locations, flags = 0):
if not prefix or not get_setting('extracted_snippets_completions'):
return []
completions = [
("%(trigger)s\t(SnippetCompletions)" % s, s['contents']) for
s in load_snippets() if 'scope' not in s or any(
view.match_selector(p, s['scope']) for p in locations) ]
if view.match_selector(locations[0], 'source.js'):
extra = view.extract_completions(prefix, locations[0])
completions.extend(("%s\t(buffer)" % e, e) for e in extra)
return (completions, flags )
############################ EXTRACT SNIPPET HELPERS ###########################
def shares_extents(r1, r2):
return {r1.a, r1.b} & {r2.a, r2.b}
def extract_snippet(view, edit):
# Reset start end_points
span = view.sel()[0].cover(view.sel()[-1])
tab_stops = [s for s in view.sel() if not
( shares_extents(s, span) and s.empty() ) ]
snippet = [ normed_indentation_pt(view, span, non_space=True) *
' ']
tab_stop_map = {}
i = 0
if tab_stops:
all_regions = [ sublime.Region(r.begin(), r.end(), 666) for r in
invert_regions(regions=tab_stops, spanning=span) ]
else:
all_regions = [sublime.Region(span.a, span.b, 666)]
for region in tab_stops: bisect.insort(all_regions, region)
for region in all_regions:
text = (view.substr(region)
.replace('\\', '\\\\')
.replace('$', '\\$'))
i+=1
if region.xpos != 666:
tab_stop_index = tab_stop_map.get(text, i)
if tab_stop_index == i and text: tab_stop_map[text] = i
text = '${%s:%s}' % (tab_stop_index, text.replace('}', '\\}'))
snippet.append(text)
dedented = textwrap.dedent(''.join(snippet)).lstrip()
assert dedented, ("Issues \n\n%s" % pprint.pformat(locals()))
return dedented
def scope_as_snippet(view):
scope = view.scope.split()
return '${2:%s}${1: %s}' % (scope[0], ' '.join(scope[1:]))
def load_snippets():
settings = sublime.load_settings(EXTRACTED_SNIPPETS_SETTING)
return settings.get("extracted_snippets", [])
########################### INCREMENT TAB STOP HELPES ##########################
def inc_stop(m):
return re.sub('\d+', lambda d: str(int(d.group()) + 1), m)
def increment_tabstops(s):
return TAB_STOP.sub(lambda m: inc_stop(m.group()), s)
def zero_stop(s, replace):
return re.sub(replace, '0', s)
def replace_highest(s):
h = str(max(int(max(g)) for g in TAB_STOP.findall(s)))
return re.sub (
TAB_STOP_RE % (h,h),
lambda m: '%s' % zero_stop(m.group(), h),
s )
################################################################################