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
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
|