sublime_dsl 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +136 -0
- data/Rakefile +248 -0
- data/SYNTAX.md +927 -0
- data/bin/subdsl +4 -0
- data/lib/sublime_dsl/cli/export.rb +134 -0
- data/lib/sublime_dsl/cli/import.rb +143 -0
- data/lib/sublime_dsl/cli.rb +125 -0
- data/lib/sublime_dsl/core_ext/enumerable.rb +24 -0
- data/lib/sublime_dsl/core_ext/string.rb +129 -0
- data/lib/sublime_dsl/core_ext.rb +4 -0
- data/lib/sublime_dsl/sublime_text/command.rb +157 -0
- data/lib/sublime_dsl/sublime_text/command_set.rb +112 -0
- data/lib/sublime_dsl/sublime_text/keyboard.rb +659 -0
- data/lib/sublime_dsl/sublime_text/keymap/dsl_reader.rb +194 -0
- data/lib/sublime_dsl/sublime_text/keymap.rb +385 -0
- data/lib/sublime_dsl/sublime_text/macro.rb +91 -0
- data/lib/sublime_dsl/sublime_text/menu.rb +237 -0
- data/lib/sublime_dsl/sublime_text/mouse.rb +149 -0
- data/lib/sublime_dsl/sublime_text/mousemap.rb +185 -0
- data/lib/sublime_dsl/sublime_text/package/dsl_reader.rb +91 -0
- data/lib/sublime_dsl/sublime_text/package/exporter.rb +138 -0
- data/lib/sublime_dsl/sublime_text/package/importer.rb +127 -0
- data/lib/sublime_dsl/sublime_text/package/reader.rb +102 -0
- data/lib/sublime_dsl/sublime_text/package/writer.rb +112 -0
- data/lib/sublime_dsl/sublime_text/package.rb +96 -0
- data/lib/sublime_dsl/sublime_text/setting_set.rb +123 -0
- data/lib/sublime_dsl/sublime_text.rb +48 -0
- data/lib/sublime_dsl/textmate/custom_base_name.rb +45 -0
- data/lib/sublime_dsl/textmate/grammar/dsl_reader.rb +383 -0
- data/lib/sublime_dsl/textmate/grammar/dsl_writer.rb +178 -0
- data/lib/sublime_dsl/textmate/grammar/plist_reader.rb +163 -0
- data/lib/sublime_dsl/textmate/grammar/plist_writer.rb +153 -0
- data/lib/sublime_dsl/textmate/grammar.rb +252 -0
- data/lib/sublime_dsl/textmate/plist.rb +141 -0
- data/lib/sublime_dsl/textmate/preference.rb +301 -0
- data/lib/sublime_dsl/textmate/snippet.rb +437 -0
- data/lib/sublime_dsl/textmate/theme/dsl_reader.rb +87 -0
- data/lib/sublime_dsl/textmate/theme/item.rb +74 -0
- data/lib/sublime_dsl/textmate/theme/plist_writer.rb +53 -0
- data/lib/sublime_dsl/textmate/theme.rb +364 -0
- data/lib/sublime_dsl/textmate.rb +9 -0
- data/lib/sublime_dsl/tools/blank_slate.rb +49 -0
- data/lib/sublime_dsl/tools/console.rb +74 -0
- data/lib/sublime_dsl/tools/helpers.rb +152 -0
- data/lib/sublime_dsl/tools/regexp_wannabe.rb +154 -0
- data/lib/sublime_dsl/tools/stable_inspect.rb +20 -0
- data/lib/sublime_dsl/tools/value_equality.rb +37 -0
- data/lib/sublime_dsl/tools/xml.rb +66 -0
- data/lib/sublime_dsl/tools.rb +66 -0
- data/lib/sublime_dsl.rb +23 -0
- metadata +145 -0
@@ -0,0 +1,301 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module TextMate
|
5
|
+
|
6
|
+
##
|
7
|
+
# A named set of preferences.
|
8
|
+
|
9
|
+
class Preference
|
10
|
+
|
11
|
+
def self.import(file)
|
12
|
+
root = PList.import(file)
|
13
|
+
pref = new.tap do |p|
|
14
|
+
p.name = root.delete('name')
|
15
|
+
p.scope = root.delete('scope')
|
16
|
+
p.settings = root.delete('settings')
|
17
|
+
p.uuid = root.delete('uuid')
|
18
|
+
p.bundle_uuid = root.delete('bundleUUID')
|
19
|
+
end
|
20
|
+
root.empty? or raise Error, "unexpected keys: #{root.keys.inspect}"
|
21
|
+
base = File.basename(file, File.extname(file))
|
22
|
+
pref.name ||= base
|
23
|
+
pref.basename = base
|
24
|
+
pref.settings.each_pair do |key, value|
|
25
|
+
case key
|
26
|
+
when /showInSymbolList|disableDefaultCompletion|spellChecking/
|
27
|
+
pref.settings[key] = value.to_s == '1'
|
28
|
+
when /completion(s|Command)/i
|
29
|
+
# leave as is
|
30
|
+
when /pattern|completion|folding(Start|Stop)Marker|foldingIndentedBlock(Start|Ignore)/i
|
31
|
+
pref.settings[key] = Tools::RegexpWannabe.new(value)
|
32
|
+
when /symbolTransformation|completionCommand/
|
33
|
+
pref.settings[key] = value.strip.lines.map(&:strip).join("\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
pref
|
38
|
+
end
|
39
|
+
|
40
|
+
include CustomBaseName
|
41
|
+
|
42
|
+
attr_accessor :name
|
43
|
+
attr_accessor :scope
|
44
|
+
attr_accessor :settings
|
45
|
+
attr_accessor :uuid
|
46
|
+
attr_accessor :bundle_uuid
|
47
|
+
|
48
|
+
def initialize
|
49
|
+
@settings = {}
|
50
|
+
end
|
51
|
+
|
52
|
+
alias to_s name
|
53
|
+
|
54
|
+
def to_dsl
|
55
|
+
out = StringIO.new('', 'wb:utf-8')
|
56
|
+
out.puts "# FIXME: no scope" unless scope
|
57
|
+
args = "#{name.to_source} => #{scope ? scope.to_source : 'nil'}"
|
58
|
+
args << dsl_file_arg
|
59
|
+
out.puts "preferences #{args} do"
|
60
|
+
settings.each_pair do |key, value|
|
61
|
+
if value.is_a?(Array)
|
62
|
+
case key
|
63
|
+
when 'completions'
|
64
|
+
out.puts ' completions %w('
|
65
|
+
out.puts "#{value.join(' ').wrap.indent(4)}"
|
66
|
+
out.puts ' )'
|
67
|
+
when 'shellVariables'
|
68
|
+
value.each do |h|
|
69
|
+
out.puts " shell_variable #{h['name'].inspect_sq}, #{h['value'].inspect_sq}"
|
70
|
+
end
|
71
|
+
when 'smartTypingPairs', 'highlightPairs'
|
72
|
+
output_pairs key, value, out
|
73
|
+
else
|
74
|
+
raise Error, "unexpected Array setting: #{key}"
|
75
|
+
end
|
76
|
+
elsif value.is_a?(Hash)
|
77
|
+
key == 'indentedSoftWrap' or raise Error, "unexpected Hash setting: #{key}"
|
78
|
+
value.keys.sort == %w(format match) or
|
79
|
+
raise Error, "unexpected argument for indentedSoftWrap: #{value.inspect}"
|
80
|
+
m = Tools::RegexpWannabe.new(value['match'])
|
81
|
+
out.print m.fixme_comments
|
82
|
+
out.puts ' indented_soft_wrap match: ' << m.inspect << ', format: ' << value['format'].to_source
|
83
|
+
elsif value.is_a?(Tools::RegexpWannabe)
|
84
|
+
out.print value.fixme_comments
|
85
|
+
out.puts ' ' << key.snake_case << ' ' << value.inspect(true)
|
86
|
+
elsif key == 'comment'
|
87
|
+
value.each_line { |l| out.puts ' # ' + l.rstrip }
|
88
|
+
elsif key =~ /symbolTransformation|completionCommand/ && value.include?("\n")
|
89
|
+
out.puts ' ' << key.snake_case << " <<-'TXT'\n#{value.indent(4)}\n TXT"
|
90
|
+
else
|
91
|
+
out.puts ' ' << key.snake_case << ' ' << (value.is_a?(String) ? value.to_source(true) : value.inspect)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
out.puts " uuid #{uuid.inspect_dq}" if uuid
|
95
|
+
out.puts " bundle_uuid #{bundle_uuid.inspect_dq}" if bundle_uuid
|
96
|
+
out.puts 'end'
|
97
|
+
out.string
|
98
|
+
end
|
99
|
+
|
100
|
+
def export(dir)
|
101
|
+
file = "#{dir}/#{basename}.tmPreferences"
|
102
|
+
PListWriter.new(self).export(file)
|
103
|
+
end
|
104
|
+
|
105
|
+
def output_pairs(name, array, out)
|
106
|
+
simple, complex = array.partition { |o,c| o.length == 1 && c.length == 1 }
|
107
|
+
unless simple.empty? && !complex.empty?
|
108
|
+
simple = simple.map { |o,c| o+c }.join(' ')
|
109
|
+
out.puts " #{name.snake_case} #{simple.inspect_dq}"
|
110
|
+
end
|
111
|
+
return if complex.empty?
|
112
|
+
name = name[0..-2] # remove the 's'
|
113
|
+
complex.each do |o,c|
|
114
|
+
o =~ %r'^/(.+)/$' or
|
115
|
+
raise Error, "unexpected value for a #{name}: #{o.inspect}"
|
116
|
+
o = Tools::RegexpWannabe.new($1)
|
117
|
+
c =~ %r'^/(.+)/$' or
|
118
|
+
raise Error, "unexpected value for a #{name}: #{c.inspect}"
|
119
|
+
c = Tools::RegexpWannabe.new($1)
|
120
|
+
out.print o.fixme_comments
|
121
|
+
out.print c.fixme_comments
|
122
|
+
out.puts " #{name.snake_case} #{o.inspect(true)}, #{c.inspect}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
class DSLReader
|
128
|
+
|
129
|
+
def initialize(file = nil)
|
130
|
+
@preferences = []
|
131
|
+
@current_pref = nil
|
132
|
+
instance_eval File.read(file, encoding: 'utf-8'), file if file
|
133
|
+
end
|
134
|
+
|
135
|
+
def _preferences
|
136
|
+
@preferences
|
137
|
+
end
|
138
|
+
|
139
|
+
def method_missing(sym, *args, &block)
|
140
|
+
if @current_pref
|
141
|
+
store_setting sym.to_s, args
|
142
|
+
else
|
143
|
+
raise Error, "'#{sym}': only 'preferences' blocks are allowed"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def preferences(options = {}, &block)
|
148
|
+
@current_pref and raise Error, "preferences blocks cannot be nested"
|
149
|
+
file = options.delete(:file)
|
150
|
+
options.length == 0 and raise Error, 'missing name & scope'
|
151
|
+
name = options.keys.first
|
152
|
+
scope = options.delete(name)
|
153
|
+
options.length == 0 or
|
154
|
+
warn "extraneous 'preferences' arguments ignored: #{options.inspect}"
|
155
|
+
@current_pref = Preference.new.tap do |p|
|
156
|
+
p.name = name
|
157
|
+
p.scope = scope
|
158
|
+
p.basename = file
|
159
|
+
end
|
160
|
+
instance_eval(&block)
|
161
|
+
@preferences << @current_pref
|
162
|
+
@current_pref = nil
|
163
|
+
end
|
164
|
+
|
165
|
+
def shell_variable(name, value)
|
166
|
+
ensure_context __method__
|
167
|
+
array = @current_pref.settings['shellVariables'] ||= []
|
168
|
+
array << { name: name, value: value }
|
169
|
+
end
|
170
|
+
|
171
|
+
def smart_typing_pairs(string)
|
172
|
+
ensure_context __method__
|
173
|
+
store_pairs 'smartTypingPairs', string
|
174
|
+
end
|
175
|
+
|
176
|
+
def highlight_pairs(string)
|
177
|
+
ensure_context __method__
|
178
|
+
store_pairs 'highlightPairs', string
|
179
|
+
end
|
180
|
+
|
181
|
+
def smart_typing_pair(open, close)
|
182
|
+
ensure_context __method__
|
183
|
+
store_pair 'smartTypingPairs', open, close
|
184
|
+
end
|
185
|
+
|
186
|
+
def highlight_pair(open, close)
|
187
|
+
ensure_context __method__
|
188
|
+
store_pair 'highlightPairs', open, close
|
189
|
+
end
|
190
|
+
|
191
|
+
def indented_soft_wrap(options = {})
|
192
|
+
ensure_context __method__
|
193
|
+
match = options.delete(:match)
|
194
|
+
format = options.delete(:format)
|
195
|
+
match && format or raise Error, "#{__method__} requires 'match' and 'format'"
|
196
|
+
options.empty? or warn "#{__method__} options ignored: #{options.inspect}"
|
197
|
+
@current_pref.settings['indentedSoftWrap'] =
|
198
|
+
{ match: _re(match.source), format: format }
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def store_pairs(property, string)
|
204
|
+
array = @current_pref.settings[property] ||= []
|
205
|
+
string.split(/\s+/).each do |pair|
|
206
|
+
pair.length == 2 or raise Error, "invalid pair '#{pair}': only 2 characters allowed"
|
207
|
+
array << pair.split(//)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def store_pair(property, open, close)
|
212
|
+
array = @current_pref.settings[property] ||= []
|
213
|
+
array << [_re(open.source), _re(close.source)]
|
214
|
+
end
|
215
|
+
|
216
|
+
def store_setting(name, args)
|
217
|
+
case name
|
218
|
+
when 'uuid', 'bundle_uuid'
|
219
|
+
@current_pref.send "#{name}=", args.first
|
220
|
+
else
|
221
|
+
prop = name.camel_case
|
222
|
+
arg = args.first
|
223
|
+
if prop =~ /symbolTransformation|completionCommand/
|
224
|
+
arg = arg.lines.map(&:strip).join("\n")
|
225
|
+
else
|
226
|
+
arg = _re(arg.source) if arg.is_a?(Regexp)
|
227
|
+
end
|
228
|
+
@current_pref.settings[prop] = arg
|
229
|
+
end
|
230
|
+
# raise Error, "'#{name}' is not a valid preference"
|
231
|
+
end
|
232
|
+
|
233
|
+
def ensure_context(name)
|
234
|
+
@current_pref or raise Error, "#{name} is invalid outside a 'preferences' block"
|
235
|
+
end
|
236
|
+
|
237
|
+
def _re(s)
|
238
|
+
Tools::RegexpWannabe.new(s)
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
class PListWriter
|
245
|
+
|
246
|
+
attr_reader :preference
|
247
|
+
attr_reader :root
|
248
|
+
|
249
|
+
def initialize(preference)
|
250
|
+
@preference = preference
|
251
|
+
@root = {}
|
252
|
+
convert
|
253
|
+
end
|
254
|
+
|
255
|
+
def export(file)
|
256
|
+
PList.export(root, file)
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
def convert
|
262
|
+
root['name'] = preference.name
|
263
|
+
root['scope'] = preference.scope if preference.scope
|
264
|
+
root['uuid'] = preference.uuid if preference.uuid
|
265
|
+
root['bundleUUID'] = preference.bundle_uuid if preference.bundle_uuid
|
266
|
+
root['settings'] = convert_hash(preference.settings)
|
267
|
+
end
|
268
|
+
|
269
|
+
def convert_object(object)
|
270
|
+
k = object.class.name.split('::').last.snake_case
|
271
|
+
send "convert_#{k}", object
|
272
|
+
end
|
273
|
+
|
274
|
+
def convert_array(list)
|
275
|
+
list.map { |o| convert_object o }
|
276
|
+
end
|
277
|
+
|
278
|
+
def convert_hash(h)
|
279
|
+
out = {}
|
280
|
+
h.each_pair { |k,v| out[k] = convert_object(v) }
|
281
|
+
out
|
282
|
+
end
|
283
|
+
|
284
|
+
def convert_regexp_wannabe(re)
|
285
|
+
re.to_s
|
286
|
+
end
|
287
|
+
|
288
|
+
def convert_string(v) v end
|
289
|
+
def convert_fixnum(v) v end
|
290
|
+
def convert_nil_class(v) v end
|
291
|
+
|
292
|
+
# TODO: booleans as 1/0 or true/false?
|
293
|
+
def convert_true_class(v) 1 end
|
294
|
+
def convert_false_class(v) 0 end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
end
|