atomizr 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/atomizr +201 -0
  3. data/lib/atomizr.rb +465 -0
  4. metadata +89 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a56751156f8d212eee3aac8f422169a4d2658a27
4
+ data.tar.gz: '09b2ee2faa2025a92ffb7f40a586e416604d189a'
5
+ SHA512:
6
+ metadata.gz: 58a3918e0dcb3d1eb27060690fdcb1dbc99a5fbdb0450585c1a5fa4ff8ab1e35e41379f423a14fa83e0a668656f0e1697c642c22c93fe245ceb09e9de64e390c
7
+ data.tar.gz: 67a1ebee2b3a2117d4c2e0113e0a366153eea4aaa9a58f310ea9f712efa73602ecbc5543363387910e4053f95c1fa2bd34a02f30ee6eca0ea447e9d9207cc04b
data/bin/atomizr ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "atomizr"
4
+ require "optparse"
5
+
6
+ # default options
7
+ $input_counter = 0
8
+ $output_counter = 0
9
+ $folder = "_output"
10
+ $silent = false
11
+
12
+ $scope = nil
13
+ $merge = false
14
+ $split = false
15
+ $dupes = true
16
+ $is_tm = false
17
+ $no_tabstops = false
18
+ $no_comment = false
19
+ $no_validation = false
20
+ $delete_input = false
21
+
22
+ args = ARGV.count
23
+
24
+ # parse arguments
25
+ ARGV.options do |opts|
26
+ opts.banner = "\nUsage: atomizr [options]"
27
+
28
+ opts.on("-h", "--help", "prints this help") do
29
+ Atomizr.info()
30
+ puts opts
31
+ exit
32
+ end
33
+
34
+ opts.on("-i", "--input=<file>", Array, "Input file(s)") {
35
+ |input| $input = input
36
+ }
37
+
38
+ opts.on("-o", "--output=<file>", String, "Output file") {
39
+ |output| $output = output
40
+ }
41
+
42
+ opts.on("-s", "--scope=<scope>", String, "overwrite scope") {
43
+ |val| $scope = val
44
+ }
45
+
46
+ opts.on("-S", "--split", "split result into multiple files") {
47
+ if $merge != true
48
+ $split = true
49
+ else
50
+ abort("Error: You can't split AND merge")
51
+ end
52
+ }
53
+
54
+ opts.on("-M", "--merge", "merge results into single file") {
55
+ if $split != true
56
+ $merge = true
57
+ else
58
+ abort("Error: You can't merge AND split")
59
+ end
60
+ }
61
+
62
+ opts.on("-$", "--skip-tabstops", "skip trailing tab-stops") {
63
+ $no_tabstops = true
64
+ }
65
+
66
+ opts.on("-C", "--skip-comments", "skip generator comments") {
67
+ $no_comment = true
68
+ }
69
+
70
+ opts.on("-D", "--skip-duplicates", "skip duplicate triggers") {
71
+ $dupes = false
72
+ }
73
+
74
+ opts.on("-V", "--skip-validation", "skip file validation") {
75
+ $no_validation = true
76
+ }
77
+
78
+ opts.on("-F", "--to-folder=<target>", String, "specify target subfolder") do |folder|
79
+ $folder = folder
80
+ end
81
+
82
+ opts.on("-X", "--delete-input", "delete input file(s) on completion") {
83
+ $delete_input = true
84
+ }
85
+
86
+ opts.on("-T", "--is-textmate", "interprete snippet as TextMate") {
87
+ $is_tm = true
88
+ }
89
+
90
+ opts.on("-Z", "--silent", "run silently") {
91
+ $silent = true
92
+ }
93
+
94
+ opts.on("-v", "--version", "show version") {
95
+ Atomizr.version() unless $silent == true
96
+ exit
97
+ }
98
+
99
+ opts.parse!
100
+ end
101
+
102
+ # let's go
103
+ Atomizr.info() unless $silent == true
104
+
105
+ # error handling
106
+ if args < 1
107
+ abort("\nError: no arguments passed") unless $silent == true
108
+ elsif $input == nil
109
+ abort("\nError: no input argument passed") unless $silent == true
110
+ elsif $output == nil
111
+ abort("\nError: no output argument passed") unless $silent == true
112
+ end
113
+
114
+ # Create output directory, if necessary
115
+ Atomizr.mkdir("#{$folder}")
116
+
117
+ $input.each do |input|
118
+
119
+ if (input.end_with? ".sublime-completions") || (input.end_with? ".json")
120
+
121
+ Atomizr.read_file(input, "json")
122
+ delete_file(input) if $delete_input == true
123
+
124
+ elsif (input.end_with? ".sublime-snippet") || (input.end_with? ".tmSnippet")|| (input.end_with? ".xml")
125
+
126
+ Atomizr.read_file(input, "xml")
127
+ delete_file(input) if $delete_input == true
128
+
129
+ elsif File.directory?(input)
130
+
131
+ puts "\n$ which apm"
132
+
133
+ unless system("which apm")
134
+ abort("Unknown command 'apm'. Please run \"Install Shell Commands\" from the Atom menu.") unless $silent == true
135
+ end
136
+
137
+ unless input.end_with?("/")
138
+ input = input + "/"
139
+ end
140
+
141
+ @tm = Hash.new
142
+ @tm['Preferences'] = Dir.glob("#{input}**/*.tmPreferences")
143
+ @tm['Snippets'] = Dir.glob("#{input}**/*.tmSnippet")
144
+ @tm['Syntaxes'] = Dir.glob("#{input}**/*.tmLanguage")
145
+
146
+ abort if @tm.empty?
147
+
148
+ @tm.each do |item|
149
+
150
+ next if item[1].empty?
151
+
152
+ puts "\nCollecting #{item[0].downcase}:" unless $silent == true
153
+
154
+ item[1].each do |file|
155
+ puts " + #{file}" unless $silent == true
156
+
157
+ @base = File.basename(file)
158
+ @parent = "#{$folder}/#{$output}"
159
+ @target = @parent+'/.tmp/'+item[0]+'/'
160
+
161
+ mkdir(@target)
162
+ copy_file(file, @target+@base, false)
163
+ end
164
+
165
+ end
166
+
167
+ puts "\nConverting collection with apm" unless $silent == true
168
+ system "apm init --package #{@parent} --convert #{@parent}/.tmp"
169
+ FileUtils.rm_rf("#{@parent}/.tmp")
170
+
171
+ @st = Hash.new
172
+ @st['Snippets'] = Dir.glob("#{input}**/*.sublime-snippet")
173
+ @st['Completions'] = Dir.glob("#{input}**/*.sublime-completions")
174
+
175
+ abort if @st.empty?
176
+
177
+ @st.each do |st|
178
+ st[1].each do |item|
179
+ next if item.empty?
180
+ system "#{__FILE__} -i '#{item}' -o cson -Z -F #{@parent}/snippets"
181
+ end
182
+ end
183
+
184
+ delete_file(input) if $delete_input == true
185
+ puts "\nCompleted."
186
+ exit
187
+
188
+ else
189
+ puts "\nError: invalid input '#{input}'" unless $silent == true
190
+ end
191
+
192
+ end
193
+
194
+ # Game Over
195
+ if $input_counter == 0
196
+ puts "\nNo files converted" unless $silent == true
197
+ elsif $input_counter == 1
198
+ puts "\nAtomized #{$input_counter} file, created #{$output_counter}" unless $silent == true
199
+ else
200
+ puts "\nAtomized #{$input_counter} files, created #{$output_counter}" unless $silent == true
201
+ end
data/lib/atomizr.rb ADDED
@@ -0,0 +1,465 @@
1
+ require "json"
2
+ require "nokogiri"
3
+
4
+ class Atomizr
5
+
6
+ @name = "Atomizr"
7
+ @version = "0.18.0"
8
+ @author = "Jan T. Sott"
9
+ @homepage = "https://github.com/idleberg/atomizr.rb"
10
+
11
+ # Arrays of filters to replace characters in strings
12
+ @filename_filter = [
13
+ [/[\x00\/\\:\*\?\"\$<>\|]/, '_'],
14
+ ["\t", "-"]
15
+ ]
16
+ @title_filter = [
17
+ [/\x27/, "\\\\'"], # single-quote
18
+ [/\x22/, "\\\""], # double-quote
19
+ [/\x5C/, "\\\\"], # backslash
20
+ ]
21
+ @prefix_filter = [
22
+ [/[\x00\x22\x27\/\\:\*\?\"\'\$<>\{\}\|]/, '']
23
+ ]
24
+ @body_filter = [
25
+ [/\x27/, "\\\\'"], # single-quote
26
+ [/\x22/, "\\\""], # double-quote
27
+ [/\x5C/, "\\\\"], # backslash
28
+ ]
29
+ @scope_filter = [
30
+ # https://gist.github.com/idleberg/fca633438329cc5ae317
31
+ [',', ''],
32
+ [/\.?source\.c\+\+/, '.source.cpp'],
33
+ [/\.?source\.java-props/, '.source.java-properties'],
34
+ [/\.?source\.objc\+\+/, '.source.objcpp'],
35
+ [/\.?source\.php/, '.text.html.php'],
36
+ [/\.?source\.scss/, '.source.css.scss'],
37
+ [/\.?source\.todo/, '.text.todo'],
38
+ [/\.?text\.html\.markdown/, '.source.gfm']
39
+ ]
40
+
41
+ def self.info
42
+ puts "\n#{@name}, version #{@version}\nThe MIT License\nCopyright (c) 2015, 2016 #{@author}"
43
+ end
44
+
45
+ def self.version
46
+ puts "#{@version}"
47
+ end
48
+
49
+ def self.read_file(input, type)
50
+ if $merge == true
51
+ init_hashes()
52
+ end
53
+
54
+ Dir.glob(input) do |item|
55
+
56
+ if $merge == false
57
+ init_hashes()
58
+ end
59
+
60
+ @data = send("read_#{type}", item)
61
+
62
+ if $merge == false
63
+ write_data(item)
64
+ end
65
+ end
66
+
67
+ if $merge == true
68
+ write_data($output)
69
+ end
70
+ end
71
+
72
+
73
+ def self.read_xml(item)
74
+
75
+ puts "\nReading snippet file '#{item}'" unless $silent == true
76
+
77
+ # read file, parse data
78
+ file = File.read(item)
79
+
80
+ # validate file
81
+ if (valid_xml?(file) == false) && ($no_validation == false)
82
+ abort("\nError: Invalid XML file '#{item}'") unless $silent == true
83
+ end
84
+
85
+ data = Nokogiri::XML(file)
86
+
87
+ if (item.end_with? ".tmSnippet") || ($is_tm == true)
88
+ @data['completions'] = read_textmate_xml(item, data)
89
+ else
90
+ @data['completions'] = read_sublime_xml(item, data)
91
+ end
92
+
93
+ $input_counter += 1
94
+
95
+ return @data
96
+ end
97
+
98
+ def self.read_sublime_xml(item, data)
99
+
100
+ # get scope
101
+ @data['scope'] = get_scope( data.xpath("//scope")[0].text.strip )
102
+
103
+ trigger = data.xpath("//tabTrigger")[0].text.strip
104
+
105
+ if data.xpath("//description").empty?
106
+ description = trigger
107
+ else
108
+ description = data.xpath("//description")[0].text.strip
109
+ end
110
+
111
+ data.xpath("//content").each do |node|
112
+
113
+ title = filter_str(trigger, @title_filter)
114
+ prefix = filter_str(trigger, @prefix_filter)
115
+ body = filter_str(node.text.strip, @body_filter)
116
+
117
+ if @completions.has_key?(title)
118
+ if $dupes == false
119
+ puts " !! Duplicate trigger #{title.dump} in #{item}" unless $silent == true
120
+ else
121
+ abort("\nError: duplicate trigger '#{title.dump}' in #{item}. Triggers must be unique.")
122
+ end
123
+ end
124
+
125
+ @completions[description] = {
126
+ :prefix => prefix,
127
+ :body => body
128
+ }
129
+ end
130
+
131
+ return @completions
132
+ end
133
+
134
+ def self.read_textmate_xml(item, data)
135
+
136
+ data.xpath('//dict').each do | node |
137
+ node.element_children.map(&:content).each_slice(2) do | k, v |
138
+ case k
139
+ when 'scope'
140
+ @data['scope'] = get_scope(v.to_s)
141
+ when 'name'
142
+ @title = filter_str(v.to_s, @title_filter)
143
+ when 'tabTrigger'
144
+ @prefix = filter_str(v.to_s, @prefix_filter)
145
+ when 'content'
146
+ @body = filter_str(v.to_s.strip, @body_filter)
147
+ else
148
+ next
149
+ end
150
+ end
151
+ end
152
+
153
+ @completions[@title] = {
154
+ :prefix => @prefix,
155
+ :body => @body
156
+ }
157
+
158
+ return @completions
159
+ end
160
+
161
+ def self.read_json(item)
162
+
163
+ puts "\nReading completion file '#{item}'" unless $silent == true
164
+
165
+ # read file
166
+ file = File.read(item)
167
+
168
+ # validate file
169
+ if (valid_json?(file) == false) && $no_validation == false
170
+ abort("\nError: Invalid JSON file '#{item}'") unless $silent == true
171
+ else
172
+ data = JSON.load(file)
173
+ end
174
+
175
+ # get scope
176
+ @data['scope'] = get_scope( data["scope"] )
177
+
178
+ data["completions"].each do |line|
179
+ trigger = line["trigger"]
180
+
181
+ # Next if JSON contains non-standard keys
182
+ if trigger == nil
183
+ puts " >> Ignoring line #{line}" unless $silent == true
184
+ next
185
+ end
186
+
187
+ contents = line["contents"]
188
+
189
+ title = filter_str(trigger, @title_filter)
190
+ prefix = filter_str(trigger, @prefix_filter)
191
+ body = filter_str(contents, @body_filter)
192
+
193
+ if @completions.has_key?(title)
194
+ if $dupes == false
195
+ puts " !! Duplicate trigger #{title.dump} in #{item}" unless $silent == true
196
+ else
197
+ abort("\nError: duplicate trigger '#{title.dump}' in #{item}. Triggers must be unique.") unless $silent == true
198
+ end
199
+ end
200
+
201
+ @completions[title] = {
202
+ :prefix => prefix,
203
+ :body => body
204
+ }
205
+ end
206
+
207
+ @data['completions'] = @completions
208
+
209
+ $input_counter += 1
210
+ return @data
211
+ end
212
+
213
+ # via https://gist.github.com/ascendbruce/7070951
214
+ def self.valid_json?(json)
215
+ JSON.parse(json)
216
+ true
217
+ rescue
218
+ false
219
+ end
220
+
221
+ def self.valid_xml?(xml)
222
+ Nokogiri::XML(xml) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
223
+ true
224
+ rescue
225
+ false
226
+ end
227
+
228
+ def self.write_data(item)
229
+ if $output == "json"
230
+ file = get_outname('json', item)
231
+ write_json(@data, file, $split)
232
+ else
233
+ file = get_outname('cson', item)
234
+ write_cson(@data, file, $split)
235
+ end
236
+ end
237
+
238
+ def self.write_json(data, file, many = false)
239
+
240
+ if many == false
241
+
242
+ if $no_comment == true
243
+ json = {
244
+ data['scope'] => data['completions']
245
+ }
246
+ else
247
+ json = {
248
+ :generator => "Atomizr v#{@version} - #{@homepage}",
249
+ data['scope'] => data['completions']
250
+ }
251
+ end
252
+
253
+ puts "Writing '#{file}'" unless $silent == true
254
+ File.open("#{$folder}/#{file}","w") do |f|
255
+ f.write(JSON.pretty_generate(json))
256
+ end
257
+ $output_counter += 1
258
+
259
+ elsif many == true
260
+
261
+ scope = data['scope']
262
+
263
+ data['completions'].each do |item|
264
+
265
+ file = filter_str(item[1][:prefix], @filename_filter)
266
+
267
+ json = {
268
+ data['scope'] => {
269
+ item[0] => {
270
+ 'prefix' => item[1][:prefix],
271
+ 'body' => item[1][:body]
272
+ }
273
+ }
274
+ }
275
+
276
+ puts "Writing '#{file}.json'" unless $silent == true
277
+ File.open("#{$folder}/#{file}.json","w") do |f|
278
+ f.write(JSON.pretty_generate(json))
279
+ end
280
+ $output_counter += 1
281
+ end
282
+ end
283
+ end
284
+
285
+ def self.write_cson(data, item, many = false)
286
+
287
+ if many == false
288
+
289
+ if $no_comment == true
290
+ comment = ""
291
+ else
292
+ comment = "# Generated with #{@name} v#{@version} - #{@homepage}\n"
293
+ end
294
+
295
+ cson = comment
296
+ cson += "'"+data['scope']+"':\n"
297
+
298
+ data['completions'].each do |item|
299
+
300
+ title = item[0]
301
+ prefix = item[1][:prefix]
302
+ body = item[1][:body]
303
+
304
+ if $no_tabstops == false
305
+ body = add_trailing_tabstop(body)
306
+ end
307
+
308
+ cson += " '"+title+"':\n"
309
+ cson += " 'prefix': '"+prefix+"'\n"
310
+ if body.lines.count <= 1
311
+ cson += " 'body': '"+body+"'\n"
312
+ else
313
+ cson += " 'body': \"\"\"\n"
314
+ body.each_line do |line|
315
+ cson += " "+line
316
+ end
317
+ cson +="\n \"\"\"\n"
318
+ end
319
+ end
320
+
321
+ if File.directory?($input[0])
322
+ mkdir("#{$folder}/#{$output}/snippets")
323
+ file = $output + '/snippets/' + item + '.cson'
324
+ else
325
+ file = get_outname('cson', item)
326
+ end
327
+
328
+ puts "Writing '#{file}'" unless $silent == true
329
+ File.open("#{$folder}/#{file}","w") do |f|
330
+ f.write(cson)
331
+ end
332
+ $output_counter += 1
333
+
334
+ elsif many == true
335
+
336
+ scope = data['scope']
337
+
338
+ data['completions'].each do |item|
339
+
340
+ cson = "'"+scope+"':\n"
341
+ title = item[0]
342
+ prefix = item[1][:prefix]
343
+ body = item[1][:body]
344
+
345
+ file = filter_str(prefix, @filename_filter)
346
+
347
+ cson += " '"+title+"':\n"
348
+ cson += " 'prefix': '"+prefix+"'\n"
349
+ if body.lines.count <= 1
350
+ cson += " 'body': '"+body+"'\n"
351
+ else
352
+ cson += " 'body': \"\"\"\n"
353
+ body.each_line do |line|
354
+ cson += " "+line
355
+ end
356
+ cson +="\n \"\"\"\n"
357
+ end
358
+
359
+ puts "Writing '#{file}.cson'" unless $silent == true
360
+ File.open("#{$folder}/#{file}.cson","w") do |f|
361
+ f.write(cson)
362
+ end
363
+ $output_counter += 1
364
+ end
365
+ end
366
+ end
367
+
368
+ def self.delete_file(input)
369
+
370
+ if File.directory?(input)
371
+ puts "\nDeleting '#{input[0..-2]}'" unless $silent == true
372
+ FileUtils.rm_rf(input)
373
+ else
374
+ Dir.glob(input) do |item|
375
+ puts "Deleting '#{item}'" unless $silent == true
376
+ File.delete(item)
377
+ end
378
+ end
379
+
380
+ end
381
+
382
+ def self.get_outname(type, item)
383
+ if $output == type
384
+
385
+ if $input[0].include?("*")
386
+ file = item
387
+ else
388
+ file = $input[0]
389
+ end
390
+ output = File.basename(file, ".*")+"."+type
391
+ else
392
+ output = $output
393
+ end
394
+
395
+ return output
396
+ end
397
+
398
+ def self.get_scope(scope)
399
+
400
+ if $scope == nil
401
+ scope = fix_scope(filter_str(scope, @scope_filter))
402
+ puts "Using default scope '"+scope+"'" unless $silent == true
403
+ else
404
+ scope = fix_scope(filter_str($scope, @scope_filter))
405
+ puts "Override scope using '"+scope+"'" unless $silent == true
406
+ end
407
+
408
+ return scope
409
+ end
410
+
411
+ def self.add_trailing_tabstop(input)
412
+ unless input.match(/\$\d+$/) == nil
413
+ # nothing to do here
414
+ return input
415
+ end
416
+
417
+ stops = input.scan(/\${?(\d+)/)
418
+ if stops.length > 0
419
+ highest = stops.sort.flatten.last.to_i + 1
420
+ return "#{input}$#{highest}"
421
+ end
422
+
423
+ return "#{input}$1"
424
+ end
425
+
426
+ # prepend dot to scope
427
+ def self.fix_scope(scope)
428
+ if scope[0] != "."
429
+ scope = "."+ scope
430
+ end
431
+
432
+ return scope
433
+ end
434
+
435
+ def self.filter_str(input, filter)
436
+
437
+ if filter.any?
438
+ filter.each do |needle, replacement|
439
+ input = input.to_s.gsub(needle, replacement)
440
+ end
441
+ end
442
+
443
+ return input
444
+ end
445
+
446
+ def self.init_hashes()
447
+ @data = Hash.new
448
+ @completions = Hash.new
449
+ end
450
+
451
+ def self.mkdir(folder)
452
+ if !Dir.exists?(folder)
453
+ FileUtils.mkdir_p(folder)
454
+ end
455
+ end
456
+
457
+ def self.copy_file(src, dest, del = false)
458
+ File.write(dest, File.read(src))
459
+
460
+ if del == true
461
+ File.delete(src)
462
+ end
463
+ end
464
+
465
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: atomizr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.18.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan T. Sott
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.12.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.12.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.7.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.7.2
55
+ description: A command-line tool to convert Sublime Text snippets and completions,
56
+ as well as and TextMate snippets into Atom snippets
57
+ email:
58
+ executables:
59
+ - atomizr
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - lib/atomizr.rb
64
+ - bin/atomizr
65
+ homepage: https://github.com/idleberg/atomizr.rb
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.0.14.1
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Converts Sublime Text snippets and completions into Atom format
89
+ test_files: []