sublime_dsl 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +136 -0
  3. data/Rakefile +248 -0
  4. data/SYNTAX.md +927 -0
  5. data/bin/subdsl +4 -0
  6. data/lib/sublime_dsl/cli/export.rb +134 -0
  7. data/lib/sublime_dsl/cli/import.rb +143 -0
  8. data/lib/sublime_dsl/cli.rb +125 -0
  9. data/lib/sublime_dsl/core_ext/enumerable.rb +24 -0
  10. data/lib/sublime_dsl/core_ext/string.rb +129 -0
  11. data/lib/sublime_dsl/core_ext.rb +4 -0
  12. data/lib/sublime_dsl/sublime_text/command.rb +157 -0
  13. data/lib/sublime_dsl/sublime_text/command_set.rb +112 -0
  14. data/lib/sublime_dsl/sublime_text/keyboard.rb +659 -0
  15. data/lib/sublime_dsl/sublime_text/keymap/dsl_reader.rb +194 -0
  16. data/lib/sublime_dsl/sublime_text/keymap.rb +385 -0
  17. data/lib/sublime_dsl/sublime_text/macro.rb +91 -0
  18. data/lib/sublime_dsl/sublime_text/menu.rb +237 -0
  19. data/lib/sublime_dsl/sublime_text/mouse.rb +149 -0
  20. data/lib/sublime_dsl/sublime_text/mousemap.rb +185 -0
  21. data/lib/sublime_dsl/sublime_text/package/dsl_reader.rb +91 -0
  22. data/lib/sublime_dsl/sublime_text/package/exporter.rb +138 -0
  23. data/lib/sublime_dsl/sublime_text/package/importer.rb +127 -0
  24. data/lib/sublime_dsl/sublime_text/package/reader.rb +102 -0
  25. data/lib/sublime_dsl/sublime_text/package/writer.rb +112 -0
  26. data/lib/sublime_dsl/sublime_text/package.rb +96 -0
  27. data/lib/sublime_dsl/sublime_text/setting_set.rb +123 -0
  28. data/lib/sublime_dsl/sublime_text.rb +48 -0
  29. data/lib/sublime_dsl/textmate/custom_base_name.rb +45 -0
  30. data/lib/sublime_dsl/textmate/grammar/dsl_reader.rb +383 -0
  31. data/lib/sublime_dsl/textmate/grammar/dsl_writer.rb +178 -0
  32. data/lib/sublime_dsl/textmate/grammar/plist_reader.rb +163 -0
  33. data/lib/sublime_dsl/textmate/grammar/plist_writer.rb +153 -0
  34. data/lib/sublime_dsl/textmate/grammar.rb +252 -0
  35. data/lib/sublime_dsl/textmate/plist.rb +141 -0
  36. data/lib/sublime_dsl/textmate/preference.rb +301 -0
  37. data/lib/sublime_dsl/textmate/snippet.rb +437 -0
  38. data/lib/sublime_dsl/textmate/theme/dsl_reader.rb +87 -0
  39. data/lib/sublime_dsl/textmate/theme/item.rb +74 -0
  40. data/lib/sublime_dsl/textmate/theme/plist_writer.rb +53 -0
  41. data/lib/sublime_dsl/textmate/theme.rb +364 -0
  42. data/lib/sublime_dsl/textmate.rb +9 -0
  43. data/lib/sublime_dsl/tools/blank_slate.rb +49 -0
  44. data/lib/sublime_dsl/tools/console.rb +74 -0
  45. data/lib/sublime_dsl/tools/helpers.rb +152 -0
  46. data/lib/sublime_dsl/tools/regexp_wannabe.rb +154 -0
  47. data/lib/sublime_dsl/tools/stable_inspect.rb +20 -0
  48. data/lib/sublime_dsl/tools/value_equality.rb +37 -0
  49. data/lib/sublime_dsl/tools/xml.rb +66 -0
  50. data/lib/sublime_dsl/tools.rb +66 -0
  51. data/lib/sublime_dsl.rb +23 -0
  52. 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