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,364 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'theme/item'
4
+ require_relative 'theme/dsl_reader'
5
+ require_relative 'theme/plist_writer'
6
+
7
+ module SublimeDSL
8
+ module TextMate
9
+
10
+ class Theme
11
+
12
+ # Create from a PList file.
13
+ def self.import(file)
14
+ p = PList.import(file)
15
+ t = new(p.delete('name'))
16
+ t.basename = File.basename(file, File.extname(file))
17
+ t.author = p.delete('author')
18
+ t.uuid = p.delete('uuid')
19
+ t.comment = p.delete('comment')
20
+ t.license = p.delete('license')
21
+ p.delete('settings').each do |h|
22
+ n = h['name']
23
+ s = h['scope'] && h['scope'].strip
24
+ if n || s
25
+ e = Item.new(n, s)
26
+ h['settings'].each_pair do |prop, value|
27
+ next if value.empty?
28
+ e.send "#{prop.snake_case}=", value
29
+ end
30
+ t.add_item e
31
+ else
32
+ h['settings'].each_pair do |prop, color|
33
+ key = prop.snake_case #.to_sym
34
+ color.upcase! if color =~ /^#/
35
+ t.base_colors[key] = color
36
+ end
37
+ end
38
+ end
39
+ p.empty? or raise Error, "unknown theme keys: #{p.inspect}"
40
+ t
41
+ end
42
+
43
+ # Returns the content of this file after __END__
44
+ # (DATA is not available for required files).
45
+
46
+ def self.color_data # :nodoc:
47
+ data = ''
48
+ File.open(__FILE__) do |f|
49
+ in_data = false
50
+ f.each_line do |line|
51
+ if line =~ /^__END__/
52
+ in_data = true
53
+ elsif in_data
54
+ data << line
55
+ end
56
+ end
57
+ end
58
+ data
59
+ end
60
+
61
+ # Hash { '#xxxxxx' => color_name }, read from the
62
+ # DATA section of this file.
63
+
64
+ COLOR_NAMES_HASH = {}
65
+
66
+ color_data.each_line do |line|
67
+ line.strip!
68
+ next if line.empty? || line.start_with?('=')
69
+ name, value = line.split(/\s+/)
70
+ COLOR_NAMES_HASH["##{value}"] = name.snake_case
71
+ end
72
+
73
+ include CustomBaseName
74
+
75
+ attr_accessor :name, :author, :uuid, :comment, :license
76
+
77
+ # base color hash:
78
+ # background
79
+ # caret
80
+ # foreground
81
+ # invisibles
82
+ # line_highlight
83
+ # selection
84
+ # selection_border
85
+ # inactive_selection
86
+ #
87
+ # Monokai* also have:
88
+ # inactive_selection
89
+ # inactive_selection_foreground
90
+ # find_highlight
91
+ # find_highlight_foreground
92
+ # active_guide
93
+ # brackets_foreground
94
+ # brackets_options
95
+ # bracket_contents_foreground
96
+ # bracket_contents_options
97
+
98
+ attr_reader :base_colors
99
+
100
+ # array of defined items
101
+ attr_reader :items
102
+
103
+ def initialize(name)
104
+ @name = name
105
+ @author = nil
106
+ @uuid = nil
107
+ @comment = nil
108
+ @license = nil
109
+ @base_colors = {}
110
+ @items = []
111
+ end
112
+
113
+ def add_item(e)
114
+ warn "unnamed item in theme #{name.inspect}" unless e.name
115
+ warn "empty scope for item '#{e.name}' in theme #{name.inspect}" unless e.scope
116
+ e.name && items.any? { |el| el.name == e.name } and
117
+ warn "duplicate item name '#{e.name}' in theme #{name.inspect}"
118
+ items << e
119
+ end
120
+
121
+ def to_dsl
122
+ out = StringIO.new('', 'wb:utf-8')
123
+
124
+ args = name.to_source
125
+ args << dsl_file_arg
126
+
127
+ out.puts comment.strip.lines.map { |l| "# #{l}" }.join << "\n\n" if comment
128
+ out.puts "theme #{args} do"
129
+ out.puts
130
+ out.puts " author '#{author}'" if author
131
+ out.puts " uuid '#{uuid}'" if uuid
132
+ if license
133
+ out.puts
134
+ out.puts " license <<-TXT"
135
+ license.lines.each { |l| out.puts l.strip.empty? ? '' : l.strip.wrap.indent(4) }
136
+ out.puts " TXT"
137
+ end
138
+
139
+ out.puts
140
+ max = colors_hash.values.map(&:length).max
141
+ colors_hash.each_pair do |color, name|
142
+ out.puts " %*1$2$s = '%3$s'" % [-max, name, color]
143
+ end
144
+
145
+ out.puts
146
+ out.puts " base_colors("
147
+ base_colors.each_pair do |k,v|
148
+ out.puts " #{k}: #{colors_hash[v]},"
149
+ end
150
+ out.puts " )"
151
+
152
+ out.puts
153
+ names_hash = items.group_by(&:name)
154
+ items.each do |e|
155
+ out.puts ' # FIXME: duplicate name:' if e.name && names_hash[e.name].length > 1
156
+ out.puts e.to_dsl(colors_hash).indent(2)
157
+ end
158
+
159
+ out.puts "\nend"
160
+
161
+ out.string
162
+ end
163
+
164
+ def write(dir)
165
+ file = "#{dir}/#{basename}.tmTheme.rb"
166
+ File.open(file, 'wb:utf-8') do |f|
167
+ f.puts '# encoding: utf-8'
168
+ f.puts "\n" << to_dsl
169
+ end
170
+ end
171
+
172
+ def export(dir)
173
+ file = "#{dir}/#{basename}.tmTheme"
174
+ PListWriter.new(self).export(file)
175
+ end
176
+
177
+ # A Hash { <color value> => <color name> }.
178
+ def colors_hash
179
+ @colors_hash ||= begin
180
+ colors = base_colors.values + items.flat_map(&:colors)
181
+ colors.uniq!
182
+ h = {}
183
+ colors.each_with_index { |c, i| h[c] = COLOR_NAMES_HASH[c] || "color#{i}" }
184
+ h
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+ end
192
+
193
+ __END__
194
+
195
+ = Red
196
+
197
+ IndianRed CD5C5C
198
+ LightCoral F08080
199
+ Salmon FA8072
200
+ DarkSalmon E9967A
201
+ LightSalmon FFA07A
202
+ Crimson DC143C
203
+ Red FF0000
204
+ FireBrick B22222
205
+ DarkRed 8B0000
206
+
207
+ = Pink
208
+
209
+ Pink FFC0CB
210
+ LightPink FFB6C1
211
+ HotPink FF69B4
212
+ DeepPink FF1493
213
+ MediumVioletRed C71585
214
+ PaleVioletRed DB7093
215
+
216
+ = Orange
217
+
218
+ LightSalmon FFA07A
219
+ Coral FF7F50
220
+ Tomato FF6347
221
+ OrangeRed FF4500
222
+ DarkOrange FF8C00
223
+ Orange FFA500
224
+
225
+ = Yellow
226
+
227
+ Gold FFD700
228
+ Yellow FFFF00
229
+ LightYellow FFFFE0
230
+ LemonChiffon FFFACD
231
+ LightGoldenrodYellow FAFAD2
232
+ PapayaWhip FFEFD5
233
+ Moccasin FFE4B5
234
+ PeachPuff FFDAB9
235
+ PaleGoldenrod EEE8AA
236
+ Khaki F0E68C
237
+ DarkKhaki BDB76B
238
+
239
+ = Purple
240
+
241
+ Lavender E6E6FA
242
+ Thistle D8BFD8
243
+ Plum DDA0DD
244
+ Violet EE82EE
245
+ Orchid DA70D6
246
+ Fuchsia FF00FF
247
+ Magenta FF00FF
248
+ MediumOrchid BA55D3
249
+ MediumPurple 9370DB
250
+ BlueViolet 8A2BE2
251
+ DarkViolet 9400D3
252
+ DarkOrchid 9932CC
253
+ DarkMagenta 8B008B
254
+ Purple 800080
255
+ Indigo 4B0082
256
+ SlateBlue 6A5ACD
257
+ DarkSlateBlue 483D8B
258
+
259
+ = Green
260
+
261
+ GreenYellow ADFF2F
262
+ Chartreuse 7FFF00
263
+ LawnGreen 7CFC00
264
+ Lime 00FF00
265
+ LimeGreen 32CD32
266
+ PaleGreen 98FB98
267
+ LightGreen 90EE90
268
+ MediumSpringGreen 00FA9A
269
+ SpringGreen 00FF7F
270
+ MediumSeaGreen 3CB371
271
+ SeaGreen 2E8B57
272
+ ForestGreen 228B22
273
+ Green 008000
274
+ DarkGreen 006400
275
+ YellowGreen 9ACD32
276
+ OliveDrab 6B8E23
277
+ Olive 808000
278
+ DarkOliveGreen 556B2F
279
+ MediumAquamarine 66CDAA
280
+ DarkSeaGreen 8FBC8F
281
+ LightSeaGreen 20B2AA
282
+ DarkCyan 008B8B
283
+ Teal 008080
284
+
285
+ = Blue
286
+
287
+ Aqua 00FFFF
288
+ Cyan 00FFFF
289
+ LightCyan E0FFFF
290
+ PaleTurquoise AFEEEE
291
+ Aquamarine 7FFFD4
292
+ Turquoise 40E0D0
293
+ MediumTurquoise 48D1CC
294
+ DarkTurquoise 00CED1
295
+ CadetBlue 5F9EA0
296
+ SteelBlue 4682B4
297
+ LightSteelBlue B0C4DE
298
+ PowderBlue B0E0E6
299
+ LightBlue ADD8E6
300
+ SkyBlue 87CEEB
301
+ LightSkyBlue 87CEFA
302
+ DeepSkyBlue 00BFFF
303
+ DodgerBlue 1E90FF
304
+ CornflowerBlue 6495ED
305
+ MediumSlateBlue 7B68EE
306
+ RoyalBlue 4169E1
307
+ Blue 0000FF
308
+ MediumBlue 0000CD
309
+ DarkBlue 00008B
310
+ Navy 000080
311
+ MidnightBlue 191970
312
+
313
+ = Brown
314
+
315
+ Cornsilk FFF8DC
316
+ BlanchedAlmond FFEBCD
317
+ Bisque FFE4C4
318
+ NavajoWhite FFDEAD
319
+ Wheat F5DEB3
320
+ BurlyWood DEB887
321
+ Tan D2B48C
322
+ RosyBrown BC8F8F
323
+ SandyBrown F4A460
324
+ Goldenrod DAA520
325
+ DarkGoldenrod B8860B
326
+ Peru CD853F
327
+ Chocolate D2691E
328
+ SaddleBrown 8B4513
329
+ Sienna A0522D
330
+ Brown A52A2A
331
+ Maroon 800000
332
+
333
+ = White
334
+
335
+ White FFFFFF
336
+ Snow FFFAFA
337
+ Honeydew F0FFF0
338
+ MintCream F5FFFA
339
+ Azure F0FFFF
340
+ AliceBlue F0F8FF
341
+ GhostWhite F8F8FF
342
+ WhiteSmoke F5F5F5
343
+ Seashell FFF5EE
344
+ Beige F5F5DC
345
+ OldLace FDF5E6
346
+ FloralWhite FFFAF0
347
+ Ivory FFFFF0
348
+ AntiqueWhite FAEBD7
349
+ Linen FAF0E6
350
+ LavenderBlush FFF0F5
351
+ MistyRose FFE4E1
352
+
353
+ = Grey
354
+
355
+ Gainsboro DCDCDC
356
+ LightGrey D3D3D3
357
+ Silver C0C0C0
358
+ DarkGray A9A9A9
359
+ Gray 808080
360
+ DimGray 696969
361
+ LightSlateGray 778899
362
+ SlateGray 708090
363
+ DarkSlateGray 2F4F4F
364
+ Black 000000
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'textmate/plist'
4
+ require_relative 'textmate/custom_base_name'
5
+
6
+ require_relative 'textmate/grammar'
7
+ require_relative 'textmate/snippet'
8
+ require_relative 'textmate/preference'
9
+ require_relative 'textmate/theme'
@@ -0,0 +1,49 @@
1
+
2
+ module SublimeDSL
3
+ module Tools
4
+
5
+ ##
6
+ # A class with a minimal set of methods, used for DSL readers
7
+ # that heavily rely on #method_missing.
8
+
9
+ class BlankSlate < Object
10
+
11
+ # Methods kept:
12
+ # - public/protected from BasicObject, except operators
13
+ # - private from BasicObject
14
+ # - a few Object/Kernel methods
15
+
16
+ KEPT_METHODS = %w(
17
+ __id__
18
+ __send__
19
+ equal?
20
+ instance_eval
21
+ instance_exec
22
+
23
+ initialize
24
+ method_missing
25
+ singleton_method_added
26
+ singleton_method_removed
27
+ singleton_method_undefined
28
+
29
+ __callee__
30
+ __method__
31
+ caller
32
+ define_singleton_method
33
+ method
34
+ object_id
35
+ p
36
+ puts
37
+ raise
38
+ singleton_methods
39
+ warn
40
+ ).map(&:to_sym)
41
+
42
+ (instance_methods + private_instance_methods - KEPT_METHODS).each do |m|
43
+ undef_method m
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,74 @@
1
+
2
+ module SublimeDSL
3
+ module Tools
4
+
5
+ ##
6
+ # Tools for console interaction.
7
+
8
+ class Console
9
+
10
+ attr_reader :verbosity
11
+ attr_accessor :set_ruby_verbosity
12
+ attr_reader :in_progress
13
+
14
+ def initialize
15
+ @verbosity = 1
16
+ @set_ruby_verbosity = true
17
+ @in_progress = false
18
+ end
19
+
20
+ def error(text)
21
+ end_progress
22
+ $stderr.puts text
23
+ end
24
+
25
+ def verbosity=(value)
26
+ if set_ruby_verbosity
27
+ case value
28
+ when 0 then $VERBOSE = nil
29
+ when 1 then $VERBOSE = false
30
+ when 2 then $VERBOSE = true
31
+ end
32
+ end
33
+ @verbosity = value
34
+ end
35
+
36
+ def info(text, force = false)
37
+ return unless $stdout.tty? && (verbosity > 0 || force)
38
+ end_progress
39
+ puts text
40
+ end
41
+
42
+ def width
43
+ (ENV['COLUMNS'] || 80).to_i
44
+ end
45
+
46
+ def progress(index, count, file, force = false)
47
+ return unless $stdout.tty? && (verbosity == 2 || force)
48
+ @in_progress = true
49
+ c = count.to_s
50
+ prefix = "file %#{c.length}d/#{c}: " % index
51
+ line = "#{prefix}#{file}"
52
+ padding = width - line.length - 1
53
+ if padding < 0
54
+ file = file[(-padding) .. -1]
55
+ file[0..2] = "..."
56
+ line = "#{prefix}#{file}"
57
+ else
58
+ line << ' ' * padding
59
+ end
60
+ print "#{line}\r"
61
+ $stdout.flush
62
+ end
63
+
64
+ private
65
+
66
+ def end_progress
67
+ puts() if in_progress
68
+ @in_progress = false
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,152 @@
1
+
2
+ module SublimeDSL
3
+ module Tools
4
+
5
+ ##
6
+ # Helper methods to include in DSL blocks as needed.
7
+
8
+ module Helpers
9
+
10
+ # Return an optimized regexp string matching a word in +words+. Example:
11
+ # include SublimeDSL::Tools::Helpers
12
+ # list = %w(abs addr addrlong airy allcomb allperm anyalnum anyalpha anycntrl)
13
+ # optimized_re(list)
14
+ # => 'a(bs|ddr(long)?|iry|ll(comb|perm)|ny(al(num|pha)|cntrl))'
15
+
16
+ def optimized_re(words)
17
+ root = Node.new('')
18
+ root.add_children words
19
+ root.reduce!
20
+ root.to_re
21
+ end
22
+
23
+ module_function :optimized_re
24
+
25
+ end
26
+
27
+ ##
28
+ # A node in the optimization of a Regexp matching a list of keywords.
29
+
30
+ class Node
31
+
32
+ # The start string.
33
+ attr_reader :start
34
+
35
+ # The child nodes.
36
+ attr_reader :children
37
+
38
+ # Creates a new node for the +start+ string, with an empty list of children.
39
+ def initialize(start)
40
+ @start = start
41
+ @children = []
42
+ end
43
+
44
+ # The node representing the end of a string: an empty string with no child.
45
+ NULL = self.new('')
46
+
47
+ # Adds +words+ to the children of the current node:
48
+ # the children of the current node are the unique initial letters of words,
49
+ # and the following letters are recurisvely added as grandchildren:
50
+ # x = Node.new('')
51
+ # x.add_children %w(a abc abd abde)
52
+ # => tree:
53
+ # ""
54
+ # "a"
55
+ # ""
56
+ # "b"
57
+ # "c"
58
+ # ""
59
+ # "d"
60
+ # ""
61
+ # "e"
62
+ # ""
63
+
64
+ def add_children(words)
65
+ words.group_by { |w| w[0] }.each_pair do |letter, list|
66
+ if letter.nil?
67
+ self.children << Node::NULL
68
+ else
69
+ n = Node.new(letter.dup)
70
+ self.children << n
71
+ n.add_children list.map { |w| w[1..-1] }
72
+ end
73
+ end
74
+ end
75
+
76
+ # Reduce the current node:
77
+ # - first reduce all children
78
+ # - then if there is only one +child+ left:
79
+ # - append the child#start value to self#start
80
+ # - replace the children of +self+ by the children of +child+
81
+ # Example:
82
+ # x = Node.new('')
83
+ # x.add_children %w(abs addr addrlong airy allcomb allperm anyalnum anyalpha anycntrl)
84
+ # x.reduce!
85
+ # => tree:
86
+ # "a"
87
+ # "bs"
88
+ # "ddr"
89
+ # ""
90
+ # "long"
91
+ # "iry"
92
+ # "ll"
93
+ # "comb"
94
+ # "perm"
95
+ # "ny"
96
+ # "al"
97
+ # "num"
98
+ # "pha"
99
+ # "cntrl"
100
+
101
+ def reduce!
102
+ children.each(&:reduce!)
103
+ if children.length == 1
104
+ child = children.first
105
+ @start << child.start
106
+ @children = child.children
107
+ end
108
+ end
109
+
110
+ # Converts the current node to a regular expression string:
111
+ # x = Node.new('')
112
+ # x.add_children %w(abs addr addrlong airy allcomb allperm anyalnum anyalpha anycntrl)
113
+ # x.reduce!
114
+ # x.to_re
115
+ # => 'a(bs|ddr(long)?|iry|ll(comb|perm)|ny(al(num|pha)|cntrl))'
116
+
117
+ def to_re
118
+ t, list = children.partition { |c| c == NULL }
119
+ t = t.first
120
+ start + (
121
+ case list.length
122
+ when 0
123
+ ''
124
+ when 1
125
+ r = list.first.to_re
126
+ r.length == 1 ? r : '(' + r + ')'
127
+ else
128
+ r = list.map(&:to_re)
129
+ if r.all? { |s| s.length == 1 }
130
+ '[' + r.join + ']'
131
+ else
132
+ '(' + r.join('|') + ')'
133
+ end
134
+ end
135
+ ) + (
136
+ ( t ? '?' : '' )
137
+ )
138
+ end
139
+
140
+ # The display tree for the current node.
141
+
142
+ def display_tree(indent = '')
143
+ return indent + start.inspect if children.empty?
144
+ [ indent + start.inspect,
145
+ children.map { |c| c.display_tree(indent+' ') },
146
+ ].join("\n")
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+ end