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,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative 'sublime_text/command'
|
4
|
+
require_relative 'sublime_text/command_set'
|
5
|
+
require_relative 'sublime_text/keyboard'
|
6
|
+
require_relative 'sublime_text/keymap'
|
7
|
+
require_relative 'sublime_text/macro'
|
8
|
+
require_relative 'sublime_text/menu'
|
9
|
+
require_relative 'sublime_text/mouse'
|
10
|
+
require_relative 'sublime_text/mousemap'
|
11
|
+
require_relative 'sublime_text/package'
|
12
|
+
require_relative 'sublime_text/setting_set'
|
13
|
+
|
14
|
+
module SublimeDSL
|
15
|
+
module SublimeText
|
16
|
+
|
17
|
+
# Path to the Packages directory.
|
18
|
+
|
19
|
+
def self.packages_dir
|
20
|
+
@packages_dir ||=
|
21
|
+
case Tools.os
|
22
|
+
when :Windows
|
23
|
+
ENV['APPDATA'].gsub('\\', '/') << '/Sublime Text 2/Packages'
|
24
|
+
when :OSX
|
25
|
+
"~/Library/Application Support/Sublime Text 2/Packages"
|
26
|
+
when :Linux
|
27
|
+
"~/.config/sublime-text-2/Packages"
|
28
|
+
else
|
29
|
+
raise NotImplementedError,
|
30
|
+
"don't know the location of Sublime Text packages on #{os}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Order a series of config files like ST does:
|
35
|
+
# - Default/* first,
|
36
|
+
# - then packages alphabetically,
|
37
|
+
# - then User/Default*,
|
38
|
+
# - then User/* alphabetically.
|
39
|
+
|
40
|
+
def self.order_config(files)
|
41
|
+
default, other = files.partition { |f| f.start_with?('Default') }
|
42
|
+
user, other = other.partition { |f| f.start_with?('User') }
|
43
|
+
user_default, user_other = user.partition { |f| f.start_with?('User/Default') }
|
44
|
+
default.sort + other.sort + user_default.sort + user_other.sort
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SublimeDSL
|
2
|
+
module TextMate
|
3
|
+
|
4
|
+
##
|
5
|
+
# Mix-in to define a custom file base name.
|
6
|
+
#
|
7
|
+
# The includer has a #name method.
|
8
|
+
|
9
|
+
module CustomBaseName
|
10
|
+
|
11
|
+
|
12
|
+
# Returns #custom_basename if defined, otherwise Tools.filename(name).
|
13
|
+
def basename
|
14
|
+
custom_basename || Tools.filename(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets #custom_basename to +base+.
|
18
|
+
def basename=(base)
|
19
|
+
@basename = base
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the custom base name, or +nil+ if none.
|
23
|
+
def custom_basename
|
24
|
+
# avoid warning on uninitialized instance variable
|
25
|
+
if defined?(@basename)
|
26
|
+
@basename
|
27
|
+
else
|
28
|
+
@basename = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns ", file: '<basename>'" if #basename different from
|
33
|
+
# Tools.filename(name). Otherwise sets it to +nil+
|
34
|
+
def dsl_file_arg
|
35
|
+
if basename != Tools.filename(name)
|
36
|
+
", file: #{custom_basename.to_source}"
|
37
|
+
else
|
38
|
+
''
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,383 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module TextMate
|
5
|
+
class Grammar
|
6
|
+
|
7
|
+
class DSLReader
|
8
|
+
|
9
|
+
def initialize(file = nil)
|
10
|
+
@grammars = []
|
11
|
+
@context_stack = []
|
12
|
+
instance_eval File.read(file, encoding: 'utf-8'), file if file
|
13
|
+
end
|
14
|
+
|
15
|
+
def _grammars
|
16
|
+
@grammars
|
17
|
+
end
|
18
|
+
|
19
|
+
def _context
|
20
|
+
@context_stack.last
|
21
|
+
end
|
22
|
+
|
23
|
+
def _grammar
|
24
|
+
@context_stack.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(sym, *args, &block)
|
28
|
+
raise Error, "'#{sym}' is not a valid grammar DSL statement"
|
29
|
+
end
|
30
|
+
|
31
|
+
def language(options = {}, &block)
|
32
|
+
_context and raise Error, "'language' blocks cannot be nested"
|
33
|
+
base = options.delete(:file)
|
34
|
+
options.length == 0 and raise Error, 'missing name & scope'
|
35
|
+
name = options.keys.first
|
36
|
+
scope = options.delete(name)
|
37
|
+
options.empty? or warn "invalid options: #{options.inspect}"
|
38
|
+
g = Grammar.new(name, scope)
|
39
|
+
g.basename = base
|
40
|
+
@context_stack.push g
|
41
|
+
instance_eval(&block)
|
42
|
+
@context_stack.pop
|
43
|
+
g.complete!
|
44
|
+
@grammars << g
|
45
|
+
end
|
46
|
+
|
47
|
+
def file_types(array)
|
48
|
+
ensure_writer
|
49
|
+
_grammar.file_types = array
|
50
|
+
end
|
51
|
+
|
52
|
+
def first_line_match(re)
|
53
|
+
ensure_writer
|
54
|
+
_grammar.first_line_match = Tools::RegexpWannabe.new(re.source)
|
55
|
+
end
|
56
|
+
|
57
|
+
def folding_start_marker(re)
|
58
|
+
ensure_writer
|
59
|
+
_grammar.folding_start_marker = Tools::RegexpWannabe.new(re.source)
|
60
|
+
end
|
61
|
+
|
62
|
+
def folding_stop_marker(re)
|
63
|
+
ensure_writer
|
64
|
+
_grammar.folding_stop_marker = Tools::RegexpWannabe.new(re.source)
|
65
|
+
end
|
66
|
+
|
67
|
+
def key_equivalent(str)
|
68
|
+
ensure_writer
|
69
|
+
_grammar.key_equivalent = str
|
70
|
+
end
|
71
|
+
|
72
|
+
def uuid(str)
|
73
|
+
ensure_writer
|
74
|
+
_grammar.uuid = str
|
75
|
+
end
|
76
|
+
|
77
|
+
def bundle_uuid(str)
|
78
|
+
ensure_writer
|
79
|
+
_grammar.bundle_uuid = str
|
80
|
+
end
|
81
|
+
|
82
|
+
def include(name_or_module)
|
83
|
+
if name_or_module.is_a?(Module)
|
84
|
+
self.class.send :include, name_or_module
|
85
|
+
else
|
86
|
+
ensure_reader :patterns
|
87
|
+
_context.patterns << Include.new(name_or_module)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def fragment(name)
|
92
|
+
ensure_reader :fragments
|
93
|
+
f = Fragment.new(name.to_s) # FIXME pas la peine de sortir des :sym pour faire du to_s!
|
94
|
+
_grammar.fragments << f
|
95
|
+
@context_stack.push f
|
96
|
+
yield self # block.call
|
97
|
+
@context_stack.pop
|
98
|
+
end
|
99
|
+
|
100
|
+
def rule(scope = nil)
|
101
|
+
ensure_reader :patterns
|
102
|
+
b = RuleBuilder.new(scope)
|
103
|
+
@context_stack.push b
|
104
|
+
yield self # block.call
|
105
|
+
@context_stack.pop
|
106
|
+
b.rule.complete! _grammar
|
107
|
+
_context.patterns << b.rule
|
108
|
+
end
|
109
|
+
|
110
|
+
def match(re, captures = {})
|
111
|
+
ensure_reader
|
112
|
+
_context.match re, captures
|
113
|
+
end
|
114
|
+
|
115
|
+
def from(re, captures = {})
|
116
|
+
ensure_reader
|
117
|
+
_context.from re, captures
|
118
|
+
end
|
119
|
+
|
120
|
+
def to(re, captures = {})
|
121
|
+
ensure_reader
|
122
|
+
_context.to re, captures
|
123
|
+
end
|
124
|
+
|
125
|
+
def both(captures)
|
126
|
+
ensure_reader
|
127
|
+
_context.both captures
|
128
|
+
end
|
129
|
+
|
130
|
+
def content_scope(scope)
|
131
|
+
ensure_writer
|
132
|
+
_context.content_scope = scope
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_last(value)
|
136
|
+
ensure_writer
|
137
|
+
_context.to_last = value
|
138
|
+
end
|
139
|
+
|
140
|
+
def disabled(value)
|
141
|
+
ensure_writer
|
142
|
+
_context.disabled = value
|
143
|
+
end
|
144
|
+
|
145
|
+
def ensure_reader(method = nil)
|
146
|
+
name = caller[0].gsub(/.*`(.*)'$/, '\1')
|
147
|
+
method ||= name
|
148
|
+
_context.respond_to? method or raise invalid_context(name)
|
149
|
+
end
|
150
|
+
|
151
|
+
def ensure_writer(method = nil)
|
152
|
+
name = caller[0].gsub(/.*`(.*)'$/, '\1')
|
153
|
+
method ||= name + '='
|
154
|
+
_context.respond_to? method or raise invalid_context(name)
|
155
|
+
end
|
156
|
+
|
157
|
+
def invalid_context(name)
|
158
|
+
if _context
|
159
|
+
c = _context.class.name.split('::').last.snake_case
|
160
|
+
c = 'rule' if c == 'rule_builder'
|
161
|
+
"#{name} is invalid inside '#{c}'"
|
162
|
+
else
|
163
|
+
"#{name} is invalid outside 'language'"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
class RuleBuilder
|
170
|
+
|
171
|
+
attr_accessor :state
|
172
|
+
|
173
|
+
def initialize(scope)
|
174
|
+
@state = State.new(self)
|
175
|
+
@state.scope = scope
|
176
|
+
end
|
177
|
+
|
178
|
+
def disabled=(value)
|
179
|
+
state.disabled = value
|
180
|
+
end
|
181
|
+
|
182
|
+
def match(re, captures)
|
183
|
+
state.match re, captures
|
184
|
+
end
|
185
|
+
|
186
|
+
def from(re, captures)
|
187
|
+
state.from re, captures
|
188
|
+
end
|
189
|
+
|
190
|
+
def to(re, captures)
|
191
|
+
state.to re, captures
|
192
|
+
end
|
193
|
+
|
194
|
+
def both(captures)
|
195
|
+
state.both captures
|
196
|
+
end
|
197
|
+
|
198
|
+
def content_scope=(scope)
|
199
|
+
state.content_scope = scope
|
200
|
+
end
|
201
|
+
|
202
|
+
def to_last=(value)
|
203
|
+
state.to_last = value
|
204
|
+
end
|
205
|
+
|
206
|
+
def patterns
|
207
|
+
state.patterns
|
208
|
+
end
|
209
|
+
|
210
|
+
def rule
|
211
|
+
state.rule
|
212
|
+
end
|
213
|
+
|
214
|
+
class State
|
215
|
+
|
216
|
+
attr_accessor :builder
|
217
|
+
attr_accessor :scope, :comment, :disabled
|
218
|
+
|
219
|
+
def initialize(builder)
|
220
|
+
@scope = @comment = @disabled = nil
|
221
|
+
@builder = builder
|
222
|
+
@patterns = []
|
223
|
+
end
|
224
|
+
|
225
|
+
def match(re, captures)
|
226
|
+
@patterns.empty? or raise Error, "a 'match' rule cannot contain 'include' or 'rule'"
|
227
|
+
s = state_for(MatchState)
|
228
|
+
s.match re, captures
|
229
|
+
end
|
230
|
+
|
231
|
+
def from(re, captures)
|
232
|
+
s = state_for(BeginEndState)
|
233
|
+
s.from re, captures
|
234
|
+
end
|
235
|
+
|
236
|
+
def to(re, captures)
|
237
|
+
s = state_for(BeginEndState)
|
238
|
+
s.to re, captures
|
239
|
+
end
|
240
|
+
|
241
|
+
def both(captures)
|
242
|
+
s = state_for(BeginEndState)
|
243
|
+
s.both captures
|
244
|
+
end
|
245
|
+
|
246
|
+
def content_scope=(scope)
|
247
|
+
s = state_for(BeginEndState)
|
248
|
+
s.content_scope = scope
|
249
|
+
end
|
250
|
+
|
251
|
+
def to_last=(value)
|
252
|
+
s = state_for(BeginEndState)
|
253
|
+
s.to_last = value
|
254
|
+
end
|
255
|
+
|
256
|
+
def patterns
|
257
|
+
@patterns
|
258
|
+
end
|
259
|
+
|
260
|
+
def rule
|
261
|
+
r = NoMatchRule.new
|
262
|
+
init r
|
263
|
+
r.patterns.concat @patterns
|
264
|
+
r
|
265
|
+
end
|
266
|
+
|
267
|
+
private
|
268
|
+
|
269
|
+
def state_for(klass)
|
270
|
+
s = klass.new(builder)
|
271
|
+
s.scope = scope
|
272
|
+
s.comment = comment
|
273
|
+
s.disabled = disabled
|
274
|
+
s.patterns.concat @patterns unless @patterns.empty?
|
275
|
+
@builder.state = s
|
276
|
+
end
|
277
|
+
|
278
|
+
def init(rule)
|
279
|
+
rule.scope = @scope
|
280
|
+
rule.comment = @comment
|
281
|
+
rule.disabled = @disabled
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
class MatchState < State
|
287
|
+
|
288
|
+
def match(re, captures)
|
289
|
+
@match = Match.new(Tools::RegexpWannabe.new(re.source))
|
290
|
+
@match.captures.merge! captures
|
291
|
+
end
|
292
|
+
|
293
|
+
def from(re, captures)
|
294
|
+
raise Error, "'from' is invalid with 'match'"
|
295
|
+
end
|
296
|
+
|
297
|
+
def to(re, captures)
|
298
|
+
raise Error, "'to' is invalid with 'match'"
|
299
|
+
end
|
300
|
+
|
301
|
+
def both(captures)
|
302
|
+
raise Error, "'both' is invalid with 'match'"
|
303
|
+
end
|
304
|
+
|
305
|
+
def content_scope=(scope)
|
306
|
+
raise Error, "'content_scope' is invalid with 'match'"
|
307
|
+
end
|
308
|
+
|
309
|
+
def to_last=(value)
|
310
|
+
raise Error, "'to_last' is invalid with 'match'"
|
311
|
+
end
|
312
|
+
|
313
|
+
def patterns
|
314
|
+
raise Error, "'patterns' is invalid with 'match'"
|
315
|
+
end
|
316
|
+
|
317
|
+
def rule
|
318
|
+
r = MatchRule.new
|
319
|
+
init r
|
320
|
+
r.match = @match
|
321
|
+
r
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
class BeginEndState < State
|
327
|
+
|
328
|
+
def initialize(builder)
|
329
|
+
super builder
|
330
|
+
@from = @to = nil
|
331
|
+
@both = {}
|
332
|
+
@content_scope = nil
|
333
|
+
@to_last = nil
|
334
|
+
end
|
335
|
+
|
336
|
+
def match(re, captures)
|
337
|
+
raise Error, "'match' is invalid with 'from' or 'to'"
|
338
|
+
end
|
339
|
+
|
340
|
+
def from(re, captures)
|
341
|
+
@from and raise Error, "'from' called twice"
|
342
|
+
@from = Match.new(Tools::RegexpWannabe.new(re.source))
|
343
|
+
@from.captures.merge! captures
|
344
|
+
end
|
345
|
+
|
346
|
+
def to(re, captures)
|
347
|
+
@to and raise Error, "'to' called twice"
|
348
|
+
@to = Match.new(Tools::RegexpWannabe.new(re.source, @from))
|
349
|
+
@to.captures.merge! captures
|
350
|
+
end
|
351
|
+
|
352
|
+
def both(captures)
|
353
|
+
@both.empty? or raise Error, "'both' already specified"
|
354
|
+
@both.merge! captures
|
355
|
+
end
|
356
|
+
|
357
|
+
def content_scope=(scope)
|
358
|
+
@content_scope = scope
|
359
|
+
end
|
360
|
+
|
361
|
+
def to_last=(value)
|
362
|
+
@to_last = value
|
363
|
+
end
|
364
|
+
|
365
|
+
def rule
|
366
|
+
r = BeginEndRule.new
|
367
|
+
init r
|
368
|
+
r.content_scope = @content_scope
|
369
|
+
r.from = @from
|
370
|
+
r.to = @to
|
371
|
+
r.to_last = @to_last
|
372
|
+
r.captures.merge! @both
|
373
|
+
r.patterns.concat @patterns
|
374
|
+
r
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SublimeDSL
|
4
|
+
module TextMate
|
5
|
+
class Grammar
|
6
|
+
|
7
|
+
##
|
8
|
+
# Creates the DSL for a grammar.
|
9
|
+
|
10
|
+
class DSLWriter
|
11
|
+
|
12
|
+
attr_reader :grammar
|
13
|
+
attr_reader :io
|
14
|
+
|
15
|
+
def initialize(grammar)
|
16
|
+
@grammar = grammar
|
17
|
+
@io = StringIO.new('', 'wb:utf-8')
|
18
|
+
output_grammar
|
19
|
+
end
|
20
|
+
|
21
|
+
def dsl
|
22
|
+
io.string
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def output_grammar
|
28
|
+
|
29
|
+
args = "#{grammar.name.to_source} => #{grammar.scope.to_source}"
|
30
|
+
args << grammar.dsl_file_arg
|
31
|
+
|
32
|
+
# grammar start
|
33
|
+
io.puts "language #{args} do"
|
34
|
+
indent = ' '
|
35
|
+
|
36
|
+
# comment
|
37
|
+
output_comment grammar.comment, indent
|
38
|
+
|
39
|
+
# file types
|
40
|
+
if grammar.file_types && !grammar.file_types.empty?
|
41
|
+
if grammar.file_types.any? { |t| t =~ /\s/ }
|
42
|
+
array = grammar.file_types.map { |t| "'" << t << "'" }.join(', ')
|
43
|
+
io.puts indent + "file_types [#{array}]"
|
44
|
+
else
|
45
|
+
list = grammar.file_types.join(' ')
|
46
|
+
if list.length <= 80
|
47
|
+
io.puts indent + "file_types %w(#{list})"
|
48
|
+
else
|
49
|
+
io.puts indent + "file_types %w("
|
50
|
+
io.puts list.wrap.indent(indent.length + 2)
|
51
|
+
io.puts indent + ")"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
io.puts indent + '# FIXME: no file types'
|
56
|
+
end
|
57
|
+
|
58
|
+
# regexps
|
59
|
+
%w(firstLineMatch foldingStartMarker foldingStopMarker).each do |att|
|
60
|
+
met = att.snake_case
|
61
|
+
re = grammar.send(met)
|
62
|
+
next unless re
|
63
|
+
io.print re.fixme_comments(indent)
|
64
|
+
io.puts indent + met + ' ' + re.inspect(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
# TextMate stuff
|
68
|
+
%w(keyEquivalent uuid bundleUUID).each do |att|
|
69
|
+
met = att.snake_case
|
70
|
+
str = grammar.send(met)
|
71
|
+
next unless str
|
72
|
+
io.puts indent + met + ' ' + str.inspect + ' # TextMate only'
|
73
|
+
end
|
74
|
+
|
75
|
+
# patterns
|
76
|
+
io.puts
|
77
|
+
output_array grammar.patterns, indent
|
78
|
+
|
79
|
+
# repository
|
80
|
+
grammar.fragments.each { |f| output_fragment f, indent }
|
81
|
+
|
82
|
+
# grammar end
|
83
|
+
io.puts 'end'
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def output_object(object, indent)
|
88
|
+
k = object.class.name.split('::').last.snake_case
|
89
|
+
send "output_#{k}", object, indent
|
90
|
+
end
|
91
|
+
|
92
|
+
def output_array(list, indent)
|
93
|
+
list.each { |o| output_object o, indent }
|
94
|
+
end
|
95
|
+
|
96
|
+
def output_fragment(f, indent)
|
97
|
+
io.puts
|
98
|
+
io.puts indent + '# FIXME: this fragment is never referenced' unless f.used
|
99
|
+
io.puts "#{indent}fragment #{f.name.to_sym.inspect} do"
|
100
|
+
i = indent + ' '
|
101
|
+
output_comment f.comment, i
|
102
|
+
output_array f.patterns, i
|
103
|
+
io.puts indent + 'end'
|
104
|
+
end
|
105
|
+
|
106
|
+
def output_include(inc, indent)
|
107
|
+
output_comment inc.comment, indent
|
108
|
+
io.puts indent + 'include ' + inc.fragment_name.inspect
|
109
|
+
end
|
110
|
+
|
111
|
+
def output_no_match_rule(rule, indent)
|
112
|
+
io.puts indent + '# FIXME: no "match" nor "begin/end"' unless !rule.patterns.empty? &&
|
113
|
+
(rule.scope && rule.scope.start_with?('meta.') || !rule.disabled.nil?)
|
114
|
+
output_rule_start rule, indent
|
115
|
+
output_array rule.patterns, indent + ' '
|
116
|
+
io.puts indent + 'end'
|
117
|
+
end
|
118
|
+
|
119
|
+
def output_match_rule(rule, indent)
|
120
|
+
output_rule_start rule, indent
|
121
|
+
output_match 'match', rule.match, indent + ' '
|
122
|
+
io.puts indent + 'end'
|
123
|
+
end
|
124
|
+
|
125
|
+
def output_begin_end_rule(rule, indent)
|
126
|
+
output_rule_start rule, indent
|
127
|
+
i = indent + ' '
|
128
|
+
io.puts i + 'content_scope ' + rule.content_scope.inspect if rule.content_scope
|
129
|
+
output_match 'from', rule.from, i
|
130
|
+
output_match 'to', rule.to, i
|
131
|
+
unless rule.captures.empty?
|
132
|
+
io.print i + 'both '
|
133
|
+
output_captures rule.captures, i + ' '
|
134
|
+
end
|
135
|
+
output_boolean 'to_last', rule.to_last, i
|
136
|
+
output_array rule.patterns, i
|
137
|
+
io.puts indent + 'end'
|
138
|
+
end
|
139
|
+
|
140
|
+
def output_rule_start(rule, indent)
|
141
|
+
io.puts indent + (rule.scope ? "rule '#{rule.scope}' do" : 'rule do')
|
142
|
+
indent += ' '
|
143
|
+
output_comment rule.comment, indent
|
144
|
+
output_boolean 'disabled', rule.disabled, indent
|
145
|
+
end
|
146
|
+
|
147
|
+
def output_match(name, match, indent)
|
148
|
+
io.print match.regexp.fixme_comments(indent)
|
149
|
+
io.print indent + name + ' ' + match.regexp.inspect(true)
|
150
|
+
if match.captures.empty?
|
151
|
+
io.puts
|
152
|
+
else
|
153
|
+
io.print ",\n" << indent + ' '
|
154
|
+
output_captures match.captures, indent + ' '
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def output_captures(captures, indent)
|
159
|
+
lines = []
|
160
|
+
captures.each_pair do |number, scope|
|
161
|
+
lines << number.to_s + " => '" + scope + "'"
|
162
|
+
end
|
163
|
+
io.puts lines.join(",\n" + indent)
|
164
|
+
end
|
165
|
+
|
166
|
+
def output_boolean(name, value, indent)
|
167
|
+
io.puts indent + name + ' ' + (value.to_i == 0 ? 'false' : 'true') if value
|
168
|
+
end
|
169
|
+
|
170
|
+
def output_comment(text, indent)
|
171
|
+
text and text.each_line { |l| io.puts indent + ('# ' + l).rstrip }
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|