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,138 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module SublimeText
|
5
|
+
class Package
|
6
|
+
|
7
|
+
##
|
8
|
+
# An export operation with its options.
|
9
|
+
|
10
|
+
class Exporter
|
11
|
+
|
12
|
+
attr_reader :package
|
13
|
+
attr_accessor :root, :backup, :cleanup
|
14
|
+
|
15
|
+
def initialize(package = nil)
|
16
|
+
@package = package
|
17
|
+
@root = SublimeText.packages_dir
|
18
|
+
@backup = :always
|
19
|
+
@cleanup = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def export(package = nil)
|
23
|
+
@package = package if package
|
24
|
+
validate
|
25
|
+
perform_backup
|
26
|
+
Console.info "exporting package to #{dir}"
|
27
|
+
Dir.mktmpdir('sublime_dsl_') do |tmpdir|
|
28
|
+
tmpdir = tmpdir.encode('utf-8') # for ruby 2.0
|
29
|
+
export_package tmpdir
|
30
|
+
copy_package tmpdir, dir
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def dir
|
35
|
+
"#{root}/#{@package.name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def keyboard=(name)
|
39
|
+
@keyboard_name = name
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate
|
43
|
+
File.directory?(root) or raise OptionError, "no such directory: #{root}"
|
44
|
+
@backup = backup.to_sym
|
45
|
+
unless [:never, :always, :once].include?(backup)
|
46
|
+
warn "invalid backup option #{backup.inspect}, 'always' assumed"
|
47
|
+
@backup = :always
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def perform_backup
|
52
|
+
# no backup needed if not asked or no file to backup
|
53
|
+
return unless backup != :never && Dir["#{dir}/*"].length > 0
|
54
|
+
# don't do it if only one backup is requested, and there is already one
|
55
|
+
name = Tools.filename(package.name)
|
56
|
+
return if backup == :once && Dir["#{root}/#{name}.*.zip"].length > 0
|
57
|
+
# backup
|
58
|
+
time_stamp = Tools.filename(Time.now.strftime("%Y-%m-%d %H:%M:%S"))
|
59
|
+
backup_file = "#{root}/#{name}.#{time_stamp}.zip"
|
60
|
+
Tools.zip dir, backup_file
|
61
|
+
Console.info "#{dir} backed up as #{backup_file}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def export_package(dir)
|
65
|
+
p = package
|
66
|
+
p.themes.each { |t| t.export dir }
|
67
|
+
p.grammars.each { |g| g.export dir }
|
68
|
+
p.preferences.each { |pr| pr.export dir }
|
69
|
+
p.snippets.each { |s| s.export dir }
|
70
|
+
p.setting_sets.each { |s| s.export dir }
|
71
|
+
p.macros.each { |m| m.export dir }
|
72
|
+
p.command_sets.each { |c| c.export dir }
|
73
|
+
p.menus.each { |m| m.export dir }
|
74
|
+
p.mousemaps.each { |m| m.export dir }
|
75
|
+
p.keymaps.each { |k| k.export dir }
|
76
|
+
p.other_files.each { |f| copy_file f, dir }
|
77
|
+
end
|
78
|
+
|
79
|
+
def copy_package(from, to)
|
80
|
+
|
81
|
+
# remember the files originally there
|
82
|
+
if File.directory?(to)
|
83
|
+
old_files = Dir["#{to}/**/*"]
|
84
|
+
else
|
85
|
+
Dir.mkdir to
|
86
|
+
old_files = []
|
87
|
+
end
|
88
|
+
|
89
|
+
# copy from the temporary directory
|
90
|
+
new_files = []
|
91
|
+
Dir["#{from}/**/*"].each do |fromfile|
|
92
|
+
tofile = fromfile.dup
|
93
|
+
tofile[from] = to
|
94
|
+
new_files << tofile
|
95
|
+
if File.directory?(fromfile)
|
96
|
+
Dir.mkdir tofile unless File.directory?(tofile)
|
97
|
+
else
|
98
|
+
FileUtils.cp fromfile, tofile
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
return unless cleanup
|
103
|
+
|
104
|
+
# on Windows, do not delete files that had just case changes
|
105
|
+
if Tools.os == :Windows
|
106
|
+
old_files = old_files.map(&:downcase)
|
107
|
+
new_files = new_files.map(&:downcase)
|
108
|
+
end
|
109
|
+
|
110
|
+
# delete files/directories that disappeared
|
111
|
+
(old_files - new_files).each do |f|
|
112
|
+
next unless File.exist?(f) # removing directories also remove files
|
113
|
+
if File.directory?(f)
|
114
|
+
FileUtils.rm_r [f]
|
115
|
+
else
|
116
|
+
FileUtils.rm f
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def copy_file(infile, outdir)
|
123
|
+
if File.directory?(infile)
|
124
|
+
outpath = "#{outdir}/#{File.basename(infile)}"
|
125
|
+
Dir.mkdir outpath unless File.directory?(outpath)
|
126
|
+
Dir["#{infile}/*"].each { |f| copy_file f, outpath }
|
127
|
+
else
|
128
|
+
# Console.progress ...
|
129
|
+
FileUtils.cp infile, outdir
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module SublimeText
|
5
|
+
class Package
|
6
|
+
|
7
|
+
##
|
8
|
+
# An import operation with its options.
|
9
|
+
|
10
|
+
class Importer
|
11
|
+
|
12
|
+
attr_reader :package_name
|
13
|
+
attr_accessor :root, :alt_root, :include, :exclude
|
14
|
+
|
15
|
+
def initialize(name)
|
16
|
+
@package_name = name
|
17
|
+
@root = SublimeText.packages_dir
|
18
|
+
@alt_root = nil
|
19
|
+
@include = nil
|
20
|
+
@exclude = '*.cache;*.pyc'
|
21
|
+
end
|
22
|
+
|
23
|
+
def dir
|
24
|
+
"#{root}/#{package_name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def package
|
28
|
+
@package ||= import
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate
|
34
|
+
File.directory?(dir) or raise OptionError, "no such directory: #{dir}"
|
35
|
+
alt_root.nil? || File.directory?(alt_root) or
|
36
|
+
raise OptionError, "no such directory: #{alt_root}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def files
|
40
|
+
@files ||= begin
|
41
|
+
files = Dir["#{dir}/*"]
|
42
|
+
|
43
|
+
if self.include
|
44
|
+
specs = self.include.split(';')
|
45
|
+
files.select! do |f|
|
46
|
+
f = File.basename(f)
|
47
|
+
specs.any? { |s| File.fnmatch(s, f) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if self.exclude
|
52
|
+
specs = self.exclude.split(';')
|
53
|
+
files.reject! do |f|
|
54
|
+
f = File.basename(f)
|
55
|
+
specs.any? { |s| File.fnmatch(s, f) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
files
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def import
|
64
|
+
validate
|
65
|
+
|
66
|
+
if files.empty?
|
67
|
+
Console.info "#{dir}: nothing to import"
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
Console.info "importing package from #{dir}"
|
72
|
+
package = Package.new(package_name)
|
73
|
+
files.each.with_index(1) do |file, index|
|
74
|
+
file = selected_file(file)
|
75
|
+
Console.progress index, files.length, file
|
76
|
+
begin
|
77
|
+
case File.extname(file).downcase
|
78
|
+
when '.tmtheme'
|
79
|
+
package.themes << TextMate::Theme.import(file)
|
80
|
+
when '.tmlanguage'
|
81
|
+
package.grammars << TextMate::Grammar.import(file)
|
82
|
+
when '.tmpreferences'
|
83
|
+
package.preferences << TextMate::Preference.import(file)
|
84
|
+
when '.tmsnippet', '.sublime-snippet'
|
85
|
+
package.snippets << TextMate::Snippet.import(file)
|
86
|
+
when '.sublime-settings'
|
87
|
+
package.setting_sets << SettingSet.import(file)
|
88
|
+
when '.sublime-macro'
|
89
|
+
package.macros << Macro.import(file)
|
90
|
+
when '.sublime-commands'
|
91
|
+
package.command_sets << CommandSet.import(file)
|
92
|
+
when '.sublime-menu'
|
93
|
+
package.menus << Menu.import(file)
|
94
|
+
when '.sublime-mousemap'
|
95
|
+
package.mousemaps << MouseMap.import(file)
|
96
|
+
when '.sublime-keymap'
|
97
|
+
package.keymaps << KeyMap.import(file)
|
98
|
+
|
99
|
+
when '.sublime-build'
|
100
|
+
# not worth it
|
101
|
+
package.other_files << file
|
102
|
+
when '.sublime-completions'
|
103
|
+
# only HTML & PHP, not surviving in ST 3
|
104
|
+
package.other_files << file
|
105
|
+
else
|
106
|
+
package.other_files << file
|
107
|
+
end
|
108
|
+
rescue => ex
|
109
|
+
Console.error "error when importing #{file}:"
|
110
|
+
raise ex
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
package
|
115
|
+
end
|
116
|
+
|
117
|
+
def selected_file(file)
|
118
|
+
return file unless alt_root
|
119
|
+
alt_file = file.sub(root, alt_root)
|
120
|
+
File.exist?(alt_file) ? alt_file : file
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module SublimeText
|
5
|
+
class Package
|
6
|
+
|
7
|
+
##
|
8
|
+
# A DSL read operation with its options.
|
9
|
+
|
10
|
+
class Reader
|
11
|
+
|
12
|
+
attr_reader :package_name
|
13
|
+
attr_accessor :root, :alt_root, :include, :exclude
|
14
|
+
|
15
|
+
def initialize(name)
|
16
|
+
@package_name = name
|
17
|
+
@root = '.'
|
18
|
+
@alt_root = nil
|
19
|
+
@include = nil
|
20
|
+
@exclude = '*.keyboard.rb'
|
21
|
+
end
|
22
|
+
|
23
|
+
def dir
|
24
|
+
"#{root}/#{package_name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def package
|
28
|
+
@package ||= read
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate
|
34
|
+
File.directory?(dir) or raise OptionError, "no such directory: #{dir}"
|
35
|
+
alt_root.nil? || File.directory?(alt_root) or
|
36
|
+
raise OptionError, "no such directory: #{alt_root}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def files
|
40
|
+
@files ||= begin
|
41
|
+
files = Dir["#{dir}/*"]
|
42
|
+
|
43
|
+
if self.include
|
44
|
+
specs = self.include.split(';')
|
45
|
+
files.select! do |f|
|
46
|
+
f = File.basename(f)
|
47
|
+
specs.any? { |s| File.fnmatch(s, f) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if self.exclude
|
52
|
+
specs = self.exclude.split(';')
|
53
|
+
files.reject! do |f|
|
54
|
+
f = File.basename(f)
|
55
|
+
specs.any? { |s| File.fnmatch(s, f) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
files
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def read
|
64
|
+
validate
|
65
|
+
|
66
|
+
if files.empty?
|
67
|
+
Console.info "#{dir}: nothing to read"
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
Console.info "processing DSL files in #{dir}"
|
72
|
+
package = Package.new(package_name)
|
73
|
+
|
74
|
+
files.each.with_index(1) do |file, index|
|
75
|
+
file = selected_file(file)
|
76
|
+
Console.progress index, files.length, file
|
77
|
+
begin
|
78
|
+
if File.basename(file) =~ /\.rb$/
|
79
|
+
DSLReader.new file, package
|
80
|
+
else
|
81
|
+
package.other_files << file
|
82
|
+
end
|
83
|
+
rescue => ex
|
84
|
+
Console.error "error when reading #{file}:"
|
85
|
+
raise ex
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
package
|
90
|
+
end
|
91
|
+
|
92
|
+
def selected_file(file)
|
93
|
+
return file unless alt_root
|
94
|
+
alt_file = file.sub(root, alt_root)
|
95
|
+
File.exist?(alt_file) ? alt_file : file
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module SublimeText
|
5
|
+
class Package
|
6
|
+
|
7
|
+
##
|
8
|
+
# A DSL writing operation with its options.
|
9
|
+
|
10
|
+
class Writer
|
11
|
+
|
12
|
+
attr_reader :package
|
13
|
+
attr_accessor :root, :backup, :copy_other_files
|
14
|
+
|
15
|
+
def initialize(package = nil)
|
16
|
+
@package = package
|
17
|
+
@root = '.'
|
18
|
+
@backup = :once
|
19
|
+
@copy_other_files = true
|
20
|
+
@keyboard_name = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def write(package = nil)
|
24
|
+
@package = package if package
|
25
|
+
validate
|
26
|
+
perform_backup
|
27
|
+
FileUtils.rm_r Dir["#{dir}/*"] if File.directory?(dir)
|
28
|
+
Console.info "writing package DSL to #{dir}"
|
29
|
+
write_package
|
30
|
+
end
|
31
|
+
|
32
|
+
def dir
|
33
|
+
"#{root}/#{@package.name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def keyboard=(name)
|
37
|
+
@keyboard_name = name
|
38
|
+
end
|
39
|
+
|
40
|
+
def keyboard
|
41
|
+
@keyboard ||=
|
42
|
+
if @keyboard_name.nil? || @keyboard_name == Keyboard.sublime.name
|
43
|
+
Keyboard.sublime
|
44
|
+
else
|
45
|
+
Keyboard.get(@keyboard_name, root)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate
|
50
|
+
File.directory?(root) or raise OptionError, "no such directory: #{root}"
|
51
|
+
@backup = backup.to_sym
|
52
|
+
unless [:never, :always, :once].include?(backup)
|
53
|
+
warn "invalid backup option #{backup.inspect}, 'always' assumed"
|
54
|
+
@backup = :always
|
55
|
+
end
|
56
|
+
keyboard
|
57
|
+
end
|
58
|
+
|
59
|
+
def perform_backup
|
60
|
+
# no backup needed if not asked or no file to backup
|
61
|
+
return unless backup != :never && Dir["#{dir}/*"].length > 0
|
62
|
+
# don't do it if only one backup is requested, and there is already one
|
63
|
+
name = Tools.filename(package.name)
|
64
|
+
return if backup == :once && Dir["#{root}/#{name}.*.zip"].length > 0
|
65
|
+
# backup
|
66
|
+
time_stamp = Tools.filename(Time.now.strftime("%Y-%m-%d %H:%M:%S"))
|
67
|
+
backup_file = "#{root}/#{name}.#{time_stamp}.zip"
|
68
|
+
Tools.zip dir, backup_file
|
69
|
+
Console.info "#{dir} backed up as #{backup_file}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_package
|
73
|
+
Dir.mkdir dir unless File.directory?(dir)
|
74
|
+
p = package
|
75
|
+
p.themes.each { |t| t.write dir }
|
76
|
+
p.grammars.each { |g| g.write dir }
|
77
|
+
write_all p.preferences, "#{dir}/preferences.rb"
|
78
|
+
TextMate::Snippet::DSLWriter.new(p.snippets).write "#{dir}/snippets.rb"
|
79
|
+
write_all p.setting_sets, "#{dir}/settings.rb"
|
80
|
+
write_all p.macros.sort_by(&:name), "#{dir}/macros.rb"
|
81
|
+
write_all p.command_sets, "#{dir}/commands.rb"
|
82
|
+
p.menus.each { |m| m.write dir }
|
83
|
+
p.mousemaps.each { |m| m.write dir }
|
84
|
+
p.keymaps.each { |k| k.for_keyboard(keyboard).write dir }
|
85
|
+
return unless copy_other_files
|
86
|
+
p.other_files.each { |f| copy_file f, dir }
|
87
|
+
end
|
88
|
+
|
89
|
+
def copy_file(infile, outdir)
|
90
|
+
if File.directory?(infile)
|
91
|
+
outpath = "#{outdir}/#{File.basename(infile)}"
|
92
|
+
Dir.mkdir outpath unless File.directory?(outpath)
|
93
|
+
Dir["#{infile}/*"].each { |f| copy_file f, outpath }
|
94
|
+
else
|
95
|
+
# Console.progress ...
|
96
|
+
FileUtils.cp infile, outdir
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def write_all(collection, file)
|
101
|
+
return if collection.empty?
|
102
|
+
File.open(file, 'wb:utf-8') do |f|
|
103
|
+
f.puts '# encoding: utf-8'
|
104
|
+
collection.each { |e| f.puts "\n#{e.to_dsl}" }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative 'package/importer'
|
4
|
+
require_relative 'package/writer'
|
5
|
+
require_relative 'package/reader'
|
6
|
+
require_relative 'package/exporter'
|
7
|
+
require_relative 'package/dsl_reader'
|
8
|
+
|
9
|
+
module SublimeDSL
|
10
|
+
module SublimeText
|
11
|
+
|
12
|
+
class Package
|
13
|
+
|
14
|
+
# Create a new Package from a Sublime Text 'Packages' subdirectory.
|
15
|
+
#
|
16
|
+
# The options can be any Importer option.
|
17
|
+
|
18
|
+
def self.import(name, options = {})
|
19
|
+
importer = Importer.new(name)
|
20
|
+
process_options importer, options
|
21
|
+
|
22
|
+
importer.package
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a new Package from a DSL directory.
|
26
|
+
#
|
27
|
+
# The options can be any Reader option.
|
28
|
+
|
29
|
+
def self.read(name, options = {})
|
30
|
+
reader = Reader.new(name)
|
31
|
+
process_options reader, options
|
32
|
+
|
33
|
+
reader.package
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.process_options(object, options)
|
37
|
+
options.each_pair do |k,v|
|
38
|
+
m = "#{k}="
|
39
|
+
raise OptionError, "invalid option '#{k}'" unless object.respond_to? m
|
40
|
+
object.send m, v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :name
|
45
|
+
|
46
|
+
attr_reader :themes
|
47
|
+
attr_reader :grammars
|
48
|
+
attr_reader :preferences
|
49
|
+
attr_reader :snippets
|
50
|
+
attr_reader :setting_sets
|
51
|
+
attr_reader :macros
|
52
|
+
attr_reader :command_sets
|
53
|
+
attr_reader :menus
|
54
|
+
attr_reader :mousemaps
|
55
|
+
attr_reader :keymaps
|
56
|
+
|
57
|
+
attr_reader :other_files
|
58
|
+
|
59
|
+
def initialize(name)
|
60
|
+
@name = name
|
61
|
+
@themes = []
|
62
|
+
@grammars = []
|
63
|
+
@preferences = []
|
64
|
+
@snippets = []
|
65
|
+
@setting_sets = []
|
66
|
+
@macros = []
|
67
|
+
@command_sets = []
|
68
|
+
@menus = []
|
69
|
+
@mousemaps = []
|
70
|
+
@keymaps = []
|
71
|
+
|
72
|
+
@other_files = []
|
73
|
+
end
|
74
|
+
|
75
|
+
# Writes the package to DSL files.
|
76
|
+
# The options can be any Writer option.
|
77
|
+
|
78
|
+
def write(options = {})
|
79
|
+
writer = Writer.new(self)
|
80
|
+
self.class.process_options writer, options
|
81
|
+
writer.write
|
82
|
+
end
|
83
|
+
|
84
|
+
# Exports the package.
|
85
|
+
# The options can be any Exporter option.
|
86
|
+
|
87
|
+
def export(options = {})
|
88
|
+
exporter = Exporter.new(self)
|
89
|
+
self.class.process_options exporter, options
|
90
|
+
exporter.export
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module SublimeText
|
5
|
+
|
6
|
+
class SettingSet
|
7
|
+
|
8
|
+
def self.import(file)
|
9
|
+
name = File.basename(file, File.extname(file))
|
10
|
+
set = new(name)
|
11
|
+
json = File.read(file, encoding: 'utf-8')
|
12
|
+
h = json.empty? ? {} : JSON[json]
|
13
|
+
begin
|
14
|
+
h.each_pair { |key, value| set.settings << Setting.new(key, value) }
|
15
|
+
rescue => ex
|
16
|
+
puts "file: #{file}"
|
17
|
+
raise ex
|
18
|
+
end
|
19
|
+
|
20
|
+
set
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :name, :settings
|
24
|
+
|
25
|
+
def initialize(name)
|
26
|
+
@name = name
|
27
|
+
@settings = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_dsl
|
31
|
+
dsl = "settings #{name.to_source} do\n"
|
32
|
+
dsl << "\n" unless settings.empty?
|
33
|
+
settings.each { |s| dsl << " #{s.to_dsl}\n" }
|
34
|
+
dsl << "\nend"
|
35
|
+
end
|
36
|
+
|
37
|
+
def export(dir)
|
38
|
+
file = "#{dir}/#{name}.sublime-settings"
|
39
|
+
File.open(file, 'wb:utf-8') { |f| f.write to_json }
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json
|
43
|
+
h = Hash[settings.map { |s| [s.key, s.value] }]
|
44
|
+
JSON.pretty_generate(h)
|
45
|
+
end
|
46
|
+
|
47
|
+
class Setting
|
48
|
+
|
49
|
+
attr_reader :key, :value
|
50
|
+
|
51
|
+
def initialize(key, value)
|
52
|
+
@key = key
|
53
|
+
@value = value
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_dsl
|
57
|
+
"#{key} #{display(value)}"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def display(v)
|
63
|
+
case v
|
64
|
+
when String
|
65
|
+
v.to_source
|
66
|
+
when TrueClass, FalseClass, Numeric
|
67
|
+
v.to_s
|
68
|
+
when Array
|
69
|
+
display_array(v)
|
70
|
+
when Hash
|
71
|
+
v.empty? ? 'Hash.new' : v.inspect[1..-2] # remove braces
|
72
|
+
when NilClass
|
73
|
+
'nil'
|
74
|
+
else
|
75
|
+
raise Error, "value: #{v.inspect} for #{name.inspect}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def display_array(a)
|
80
|
+
if a.empty?
|
81
|
+
'[]'
|
82
|
+
elsif a.all? { |e| e.is_a?(String) && e =~ /\A(\S+)\z/ }
|
83
|
+
"%w(#{a.join(' ')})"
|
84
|
+
else
|
85
|
+
a.inspect
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class DSLReader < Tools::BlankSlate
|
92
|
+
|
93
|
+
def initialize(file = nil)
|
94
|
+
@setting_sets = []
|
95
|
+
@current_set = nil
|
96
|
+
instance_eval File.read(file, encoding: 'utf-8'), file if file
|
97
|
+
end
|
98
|
+
|
99
|
+
def _setting_sets
|
100
|
+
@setting_sets
|
101
|
+
end
|
102
|
+
|
103
|
+
def method_missing(sym, *args, &block)
|
104
|
+
@current_set or raise Error, "'#{sym}' is invalid outside of a 'settings' block"
|
105
|
+
args.empty? and raise Error, "no value for setting '#{sym}'"
|
106
|
+
args.length > 1 and raise Error, "more than one value for setting '#{sym}'"
|
107
|
+
@current_set.settings << Setting.new(sym.to_s, args.first)
|
108
|
+
end
|
109
|
+
|
110
|
+
def settings(name, &block)
|
111
|
+
@current_set and raise Error, "'settings' blocks cannot be nested"
|
112
|
+
@current_set = SettingSet.new(name)
|
113
|
+
instance_eval(&block)
|
114
|
+
@setting_sets << @current_set
|
115
|
+
@current_set = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|