sublime_dsl 0.1.1
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.
- 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
|