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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5b577ed966febadd2751d13f7d5b16f8912d5306
|
4
|
+
data.tar.gz: b3f7a8b90827cffb1cdd42e68d38bd648ec67c37
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d0ac519cf9c60fb106de04a6d72b894b63174d96cd3c176ed62f17a87a60a2832aba0ec80b4425496c97b7df3a7858d983444deb9e7631e975900df81e4560c4
|
7
|
+
data.tar.gz: f593fa22c1652903ba2b5c136d479ebd05178259b1e22906c9c37e976a78faf7715bb582261f9229c27039708cb1c6d17285bac0da2b298b87e32172244a0f63
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# Sublime DSL
|
2
|
+
|
3
|
+
Configuration of Sublime Text using Ruby DSLs.
|
4
|
+
|
5
|
+
## Motivation
|
6
|
+
|
7
|
+
The configuration of Sublime Text uses horrible PList files (TextMate legacy),
|
8
|
+
and verbose JSON files. I first used TextMate to develop grammars, but even there,
|
9
|
+
typing grammars or specifying themes was a pain.
|
10
|
+
|
11
|
+
So I had the idea to create a Ruby DSL for TextMate grammars, themes, etc.
|
12
|
+
Once you have it, you wonder how you could do without it: all DSL statements
|
13
|
+
are regular method calls, so you can use the power of Ruby instead of copy/paste,
|
14
|
+
defining and calling your own methods.
|
15
|
+
|
16
|
+
Refer to SYNTAX.md for more information on the DSLs.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
```
|
21
|
+
$ gem install sublime_dsl
|
22
|
+
```
|
23
|
+
|
24
|
+
## Synopsis
|
25
|
+
|
26
|
+
```
|
27
|
+
$ subdsl import Ruby
|
28
|
+
```
|
29
|
+
|
30
|
+
This converts the Ruby package (from your ST packages directory) to a set of DSL files
|
31
|
+
inside ./Ruby. You can then review and modify the files. Have a look at
|
32
|
+
test/DSL.custom/Ruby.tmLanguage.rb for an example of refactoring (here-docs).
|
33
|
+
Have a look at test/DSL.custom/SAS for a package created from
|
34
|
+
scratch directly with the DSLs.
|
35
|
+
|
36
|
+
```
|
37
|
+
$ subdsl export Ruby
|
38
|
+
```
|
39
|
+
|
40
|
+
This converts the Ruby package in ./Ruby and places the files in the
|
41
|
+
ST Ruby packages directory (the previous content as backed up into a
|
42
|
+
zip archive).
|
43
|
+
|
44
|
+
Type `subdsl help` for more info on the command.
|
45
|
+
|
46
|
+
## Import: Converting to DSL
|
47
|
+
|
48
|
+
When importing a ST package, some files are grouped into one DSL file:
|
49
|
+
|
50
|
+
- all macros (*.sublime-macro) are grouped into macros.rb
|
51
|
+
- all snippets (*.sublime-snippet, *.tmSnippet) are grouped into snippets.rb
|
52
|
+
- all commands (*.sublime-commands) are grouped into commands.rb
|
53
|
+
- all preferences (*.tmPreferences) are grouped into preferences.rb
|
54
|
+
- all settings (*.sublime-settings) are grouped into settings.rb
|
55
|
+
|
56
|
+
Other files generate one DSL file:
|
57
|
+
|
58
|
+
- *.tmLanguage => *.tmLanguage.rb
|
59
|
+
- *.sublime-menu => *.menu.rb
|
60
|
+
- *.sublime-keymap => *.keymap.rb
|
61
|
+
- *.sublime-mousemap => *.mousemap.rb
|
62
|
+
|
63
|
+
Finally, files with an extension not listed above (e.g., *.py) are copied "as is".
|
64
|
+
In particular, there is no DSL (yet?) for *.sublime-build and *.sublime-completions.
|
65
|
+
|
66
|
+
## Export: Converting from DSL
|
67
|
+
|
68
|
+
The export processes all *.rb files located in the DSL directory, and copies
|
69
|
+
other files "as is". Therefore, the organization of DSL files is free: a whole
|
70
|
+
package can be specified into one source file, or into several source files.
|
71
|
+
|
72
|
+
## Tests
|
73
|
+
|
74
|
+
I don't believe in unit tests. At least not at this stage, where I can change my mind
|
75
|
+
and rework the API extensively. I do believe into extensive integration tests, so the
|
76
|
+
tests are done as follows:
|
77
|
+
|
78
|
+
- test/Packages.original and test/Packages.textmate contain the packages coming
|
79
|
+
from Sublime Text 2 and TextMate, respectively.
|
80
|
+
|
81
|
+
- They were converted into DSL equivalents in test/DSL.original and test/DSL.textmate.
|
82
|
+
|
83
|
+
- Then I tried to convert them back to PList/JSON packages into
|
84
|
+
test/Packages.original-round-trip and test/Packages.textmate-round-trip.
|
85
|
+
|
86
|
+
- Everytime there was an error in the DSL (for instance because of illegal regular expressions),
|
87
|
+
I created a fixed DSL file in test/DSL.original-fixes or test/DSL.textmate-fixes, that is read
|
88
|
+
instead of the one in test/DSL.original or test/DSL.textmate, to allow round-tripping.
|
89
|
+
|
90
|
+
- I then compared the result of the round-trip to the original, and checked it was OK.
|
91
|
+
|
92
|
+
- I then fixed some warnings (FIXME comments in the grammar DSL files) and placed the result
|
93
|
+
in test/DSL.modified, exported them in test/Packages.modified, and checked the result.
|
94
|
+
|
95
|
+
- I also created some tests using my French keyboard definition file, generating DSL in
|
96
|
+
test/DSL.original.kb-fr, and exporting them to test/Packages.original.kb-fr-round-trip.
|
97
|
+
|
98
|
+
- Finally, I placed some custom DSL files in test/DSL.custom, with the export in
|
99
|
+
test/Packages.custom.
|
100
|
+
|
101
|
+
So testing is simple and brute force: run `rake test`, and check that nothing changes,
|
102
|
+
or that the changes are expected.
|
103
|
+
|
104
|
+
## Contributing
|
105
|
+
|
106
|
+
I welcome (constructive) criticism and ideas. This was an experience for me to create Ruby DSLs,
|
107
|
+
and retrospectively, I'm even more impressed by Ruby. Many thanks to Matz for creating a language
|
108
|
+
that makes programming a pleasure.
|
109
|
+
|
110
|
+
The documentation is sparse, to say the least. Feel free to ask for clarifications:
|
111
|
+
a heavy use of `method_missing` does not create an easy to understand piece of software.
|
112
|
+
|
113
|
+
## License
|
114
|
+
|
115
|
+
(The MIT License)
|
116
|
+
|
117
|
+
Copyright (c) 2014 Thierry Lambert
|
118
|
+
|
119
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
120
|
+
a copy of this software and associated documentation files (the
|
121
|
+
'Software'), to deal in the Software without restriction, including
|
122
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
123
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
124
|
+
permit persons to whom the Software is furnished to do so, subject to
|
125
|
+
the following conditions:
|
126
|
+
|
127
|
+
The above copyright notice and this permission notice shall be
|
128
|
+
included in all copies or substantial portions of the Software.
|
129
|
+
|
130
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
131
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
132
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
133
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
134
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
135
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
136
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$VERBOSE = true
|
3
|
+
$LOAD_PATH.unshift File.expand_path('./lib')
|
4
|
+
require 'sublime_dsl'
|
5
|
+
|
6
|
+
include SublimeDSL
|
7
|
+
Console.set_ruby_verbosity = false
|
8
|
+
|
9
|
+
undef rule if self.respond_to? :rule
|
10
|
+
|
11
|
+
desc 'run a command'
|
12
|
+
task 'cmd', 'arguments' do |t, args|
|
13
|
+
cmd = Dir['bin/*'].first
|
14
|
+
cmd << ' ' << args.arguments if args.arguments
|
15
|
+
puts cmd
|
16
|
+
system "ruby -w -Ilib #{cmd}"
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'build & install gem'
|
20
|
+
task 'gem' do
|
21
|
+
system 'gem build sublime_dsl.gemspec'
|
22
|
+
system 'gem install sublime_dsl --local'
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'create RDoc documentation'
|
26
|
+
task 'rdoc' do
|
27
|
+
require 'rdoc/rdoc'
|
28
|
+
# require 'rdoc/generator/babel'
|
29
|
+
FileUtils.rm_r 'doc' if File.directory?('doc')
|
30
|
+
rdoc = RDoc::RDoc.new
|
31
|
+
args = %w(--force-update --force-output --all) # --hyperlink-all
|
32
|
+
args << '-f' << 'babel'
|
33
|
+
args << '-c' << 'utf-8'
|
34
|
+
args << '--see-standard-ancestors'
|
35
|
+
args << '-o' << 'doc'
|
36
|
+
args << '-t' << 'Sublime DSL'
|
37
|
+
args.concat %w(README.md SYNTAX.md lib)
|
38
|
+
rdoc.document(args)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'run all tests'
|
42
|
+
task 'test' do
|
43
|
+
Rake::Task['test:import'].invoke
|
44
|
+
Rake::Task['test:export'].invoke
|
45
|
+
Rake::Task['test:compare'].invoke
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace 'test' do
|
49
|
+
|
50
|
+
desc 'import all packages to DSL'
|
51
|
+
task 'import', 'profile' do |t, args|
|
52
|
+
|
53
|
+
if args.profile
|
54
|
+
profiling('import', 'original') { import_packages 'original' }
|
55
|
+
profiling('import', 'keyboard') { import_packages 'original', true }
|
56
|
+
profiling('import', 'textmate') { import_packages 'textmate' }
|
57
|
+
else
|
58
|
+
import_packages 'original'
|
59
|
+
import_packages 'original', true
|
60
|
+
import_packages 'textmate'
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'import with custom keyboards'
|
66
|
+
task 'import_keyboards' do
|
67
|
+
import_packages 'original', true
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'export all DSL to packages'
|
71
|
+
task 'export', 'profile' do |t, args|
|
72
|
+
if args.profile then
|
73
|
+
profiling('export', 'original') { export_packages 'original' }
|
74
|
+
profiling('export', 'keyboard') { export_packages 'original.kb-fr' }
|
75
|
+
profiling('export', 'modified') { export_packages 'modified' }
|
76
|
+
profiling('export', 'textmate') { export_packages 'textmate' }
|
77
|
+
profiling('export', 'custom') { export_packages 'custom' }
|
78
|
+
else
|
79
|
+
export_packages 'original'
|
80
|
+
export_packages 'original.kb-fr'
|
81
|
+
export_packages 'modified'
|
82
|
+
export_packages 'textmate'
|
83
|
+
export_packages 'custom'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
desc 'export custom DSL packages'
|
88
|
+
task 'export_custom' do
|
89
|
+
export_packages 'custom'
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'compare JSON after round-trip'
|
93
|
+
task 'compare' do
|
94
|
+
compare 'original'
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def sublime_dir
|
100
|
+
SublimeText.packages_dir
|
101
|
+
end
|
102
|
+
|
103
|
+
def capturing_stderr(file)
|
104
|
+
prev_stderr, $stderr = $stderr, File.open(file, 'wb:utf-8')
|
105
|
+
yield
|
106
|
+
$stderr.close
|
107
|
+
ensure
|
108
|
+
$stderr = prev_stderr
|
109
|
+
end
|
110
|
+
|
111
|
+
def profiling(task, subtask)
|
112
|
+
require 'ruby-prof'
|
113
|
+
result = RubyProf.profile { yield }
|
114
|
+
profile_report "#{task}-#{subtask}", result
|
115
|
+
end
|
116
|
+
|
117
|
+
def profile_report(file, result)
|
118
|
+
printer = RubyProf::CallStackPrinter.new(result)
|
119
|
+
Dir.mkdir 'prof' unless File.directory?('prof')
|
120
|
+
File.open("prof/#{file}.html", 'wb') do |f|
|
121
|
+
printer.print f, title: file, application: 'Sublime DSL'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def import_packages(suffix, custom_keyboard = false)
|
126
|
+
inroot = "test/Packages.#{suffix}"
|
127
|
+
altroot = "test/Packages.#{suffix}-fixes"
|
128
|
+
altroot = nil unless File.directory?(altroot)
|
129
|
+
import_options = { root: inroot, alt_root: altroot }
|
130
|
+
write_options = {}
|
131
|
+
if custom_keyboard
|
132
|
+
suffix << '.kb-fr'
|
133
|
+
import_options[:include] = '*.sublime-keymap'
|
134
|
+
write_options[:keyboard] = 'AZERTY-fr-FR-v6 (Windows 7)'
|
135
|
+
end
|
136
|
+
outroot = "test/DSL.#{suffix}"
|
137
|
+
write_options.merge! root: outroot, backup: :never
|
138
|
+
log = "test/#{suffix}.import.log"
|
139
|
+
Console.verbosity = 0
|
140
|
+
Console.info "importing #{inroot}", true
|
141
|
+
capturing_stderr(log) do
|
142
|
+
dirs = Dir[inroot + '/*']
|
143
|
+
dirs.each.with_index(1) do |indir, index|
|
144
|
+
Console.progress index, dirs.length, indir, true
|
145
|
+
name = File.basename(indir)
|
146
|
+
p = SublimeText::Package.import(name, import_options)
|
147
|
+
p.write write_options if p
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def export_packages(suffix)
|
153
|
+
inroot = "test/DSL.#{suffix}"
|
154
|
+
altroot = "test/DSL.#{suffix}-fixes"
|
155
|
+
outroot = "test/Packages.#{suffix}"
|
156
|
+
outroot << '-round-trip' unless suffix =~ /custom|modified/
|
157
|
+
read_options = { root: inroot }
|
158
|
+
read_options[:alt_root] = altroot if File.directory?(altroot)
|
159
|
+
export_options = { root: outroot, backup: :never, cleanup: true }
|
160
|
+
log = "test/#{suffix}.export.log"
|
161
|
+
Console.verbosity = 0
|
162
|
+
Console.info "exporting #{inroot}", true
|
163
|
+
capturing_stderr(log) do
|
164
|
+
dirs = Dir[inroot + '/*']
|
165
|
+
dirs.each.with_index(1) do |indir, index|
|
166
|
+
Console.progress index, dirs.length, indir, true
|
167
|
+
name = File.basename(indir)
|
168
|
+
p = SublimeText::Package.read(name, read_options)
|
169
|
+
p.export export_options if p
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def compare(suffix)
|
175
|
+
extensions = 'macro,commands,menu,keymap,mousemap,settings,build,completions'
|
176
|
+
inroot = "test/Packages.#{suffix}"
|
177
|
+
altroot = "test/Packages.#{suffix}-fixes"
|
178
|
+
outroot = "test/Packages.#{suffix}-round-trip"
|
179
|
+
Dir[inroot + "/**/*.sublime-{#{extensions}}"].each do |infile|
|
180
|
+
indir = File.dirname(infile)
|
181
|
+
altdir = altroot + '/' << File.basename(indir)
|
182
|
+
altfile = altdir + '/' << File.basename(infile)
|
183
|
+
infile = altfile if File.exist?(altfile)
|
184
|
+
outdir = outroot + '/' << File.basename(indir)
|
185
|
+
outfile = outdir + '/' << File.basename(infile)
|
186
|
+
if File.exist?(outfile)
|
187
|
+
compare_json infile, outfile
|
188
|
+
else
|
189
|
+
# puts outfile
|
190
|
+
# puts ' no found'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def compare_json(infile, outfile)
|
196
|
+
in_text = File.read(infile, encoding: 'utf-8')
|
197
|
+
out_text = File.read(outfile, encoding: 'utf-8')
|
198
|
+
out_json = JSON[out_text]
|
199
|
+
if in_text.empty?
|
200
|
+
puts "*** mismatch: #{infile}" unless out_json.empty?
|
201
|
+
return
|
202
|
+
end
|
203
|
+
in_json = JSON[in_text]
|
204
|
+
normalize in_json
|
205
|
+
normalize out_json
|
206
|
+
return if in_json == out_json
|
207
|
+
puts "*** mismatch: #{infile}"
|
208
|
+
file = infile.gsub('/', '_')
|
209
|
+
File.open("#{file}.in", 'wb:utf-8') { |f| f.write JSON.pretty_generate(in_json) }
|
210
|
+
File.open("#{file}.out", 'wb:utf-8') { |f| f.write JSON.pretty_generate(out_json) }
|
211
|
+
end
|
212
|
+
|
213
|
+
def normalize(json)
|
214
|
+
case json
|
215
|
+
when Hash
|
216
|
+
h = {}
|
217
|
+
json.keys.sort.each do |k|
|
218
|
+
v = json[k]
|
219
|
+
normalize v
|
220
|
+
if k == 'mnemonic'
|
221
|
+
v = v.upcase
|
222
|
+
elsif k == 'keys' && v.is_a?(Array)
|
223
|
+
v = v.map do |spec|
|
224
|
+
*mods, key = spec.split(/\+(?!$)/)
|
225
|
+
if key == '+' || key == 'plus'
|
226
|
+
mods << 'shift'
|
227
|
+
key = '='
|
228
|
+
elsif key =~ /^(forward_slash|backquote|equals|minus)$/
|
229
|
+
map = { 'forward_slash' => '/', 'backquote' => '`', 'equals' => '=', 'minus' => '-' }
|
230
|
+
key = map[$1]
|
231
|
+
end
|
232
|
+
(mods.sort << key).join('+')
|
233
|
+
end
|
234
|
+
end
|
235
|
+
h[k] = v unless
|
236
|
+
(k == 'modifiers' && v.empty?) ||
|
237
|
+
(k == 'match_all' && v == false)
|
238
|
+
end
|
239
|
+
json.clear
|
240
|
+
json.merge! h
|
241
|
+
when Array
|
242
|
+
if json.all? { |e| e.is_a?(String) }
|
243
|
+
json.sort!
|
244
|
+
else
|
245
|
+
json.each { |e| normalize e }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|