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
data/SYNTAX.md
ADDED
@@ -0,0 +1,927 @@
|
|
1
|
+
|
2
|
+
This preliminary documentation supposes you are familiar with the configuration files
|
3
|
+
of TextMate (grammars, preferences, snippets and themes) and Sublime Text (commands,
|
4
|
+
keymaps, mousemaps, menus, settings).
|
5
|
+
|
6
|
+
## General Design
|
7
|
+
|
8
|
+
### Import: Converting to DSL
|
9
|
+
|
10
|
+
When importing a ST package, some files are grouped into one DSL file:
|
11
|
+
|
12
|
+
- all macros (*.sublime-macro) are grouped into macros.rb
|
13
|
+
- all snippets (*.sublime-snippet, *.tmSnippet) are grouped into snippets.rb
|
14
|
+
- all commands (*.sublime-commands) are grouped into commands.rb
|
15
|
+
- all preferences (*.tmPreferences) are grouped into preferences.rb
|
16
|
+
- all settings (*.sublime-settings) are grouped into settings.rb
|
17
|
+
|
18
|
+
Other files generate one DSL file:
|
19
|
+
|
20
|
+
- *.tmLanguage => *.tmLanguage.rb
|
21
|
+
- *.sublime-menu => *.menu.rb
|
22
|
+
- *.sublime-keymap => *.keymap.rb
|
23
|
+
- *.sublime-mousemap => *.mousemap.rb
|
24
|
+
|
25
|
+
Finally, files with an extension not listed above (e.g., *.py) are copied "as is".
|
26
|
+
In particular, there is no DSL (yet?) for *.sublime-build and *.sublime-completions.
|
27
|
+
|
28
|
+
### Export: Converting from DSL
|
29
|
+
|
30
|
+
The export processes all *.rb files located in the DSL directory, and copies
|
31
|
+
other files "as is". Therefore, the organization of DSL files is free: a whole
|
32
|
+
package can be specified into one source file, or into several source files.
|
33
|
+
|
34
|
+
## Grammars
|
35
|
+
|
36
|
+
Grammars are defined with the `language` method:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
language 'Ruby' => 'source.ruby' do
|
40
|
+
# definitions
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
The exported file by will be named 'Ruby.tmLanguage', unless a :file option is given:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
language 'Ruby' => 'source.ruby', :file => 'MyRuby' do
|
48
|
+
# definitions
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
Inside the `language` block, the properties correspond to method calls with the
|
53
|
+
"snake case" version of the property:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
file_types %w(rb rbx rjs Rakefile rake cgi fcgi gemspec irbrc capfile Gemfile)
|
57
|
+
first_line_match %r'^#!/.*\bruby'
|
58
|
+
folding_start_marker %r:(?x)^
|
59
|
+
...
|
60
|
+
:
|
61
|
+
folding_stop_marker %r/(?x)
|
62
|
+
...
|
63
|
+
/
|
64
|
+
key_equivalent "^~R" # TextMate only
|
65
|
+
uuid "E00B62AC-6B1C-11D9-9B1F-000D93589AF6" # TextMate only
|
66
|
+
```
|
67
|
+
|
68
|
+
There are 3 exceptions:
|
69
|
+
|
70
|
+
- `scopeName` is not available, since it is specified in the arguments of the `language` method.
|
71
|
+
|
72
|
+
- `patterns` is not there: each rule inside patterns= corresponds to a `rule` block.
|
73
|
+
|
74
|
+
- `repository` is not there either: each fragment inside repository= corresponds to a `fragment`
|
75
|
+
block.
|
76
|
+
|
77
|
+
Rules are specified in `rule` blocks, the scope name being the (optional) argument of the
|
78
|
+
`rule` method:
|
79
|
+
|
80
|
+
```
|
81
|
+
{ name = 'string.unquoted.here-doc';
|
82
|
+
begin = '<<(\w+)'; // match here-doc token
|
83
|
+
end = '^\1$'; // match end of here-doc
|
84
|
+
}
|
85
|
+
```
|
86
|
+
|
87
|
+
becomes:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
rule 'string.unquoted.here-doc' do
|
91
|
+
from %r/<<(\w+)/ # match here-doc token
|
92
|
+
to %r/^§1$/ # match end of here-doc
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
`begin` and `end` being reserved words in Ruby, they are replaced by `from` and `to` (and
|
97
|
+
`applyEndPatternLast` is just `to_last`).
|
98
|
+
|
99
|
+
Notice also how back-references in `to` are noted with `§` instead of `\\`:
|
100
|
+
otherwise, the corresponding Ruby Regexp would be invalid.
|
101
|
+
|
102
|
+
Finally, the leading `%r` is to avoid syntactic warnings from Ruby.
|
103
|
+
One could also write (matter of taste):
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
rule 'string.unquoted.here-doc' do
|
107
|
+
from(/<<(\w+)/)
|
108
|
+
to(/^§1$/)
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
Or, if you accept warnings (frown):
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
rule 'string.unquoted.here-doc' do
|
116
|
+
from /<<(\w+)/
|
117
|
+
to /^§1$/
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
Nesting is performed by nesting rules, without patterns=:
|
122
|
+
|
123
|
+
```
|
124
|
+
{ begin = '<%'
|
125
|
+
end = '%>'
|
126
|
+
patterns = (
|
127
|
+
{ match = '\b(def|end)\b'; … },
|
128
|
+
…
|
129
|
+
);
|
130
|
+
};
|
131
|
+
```
|
132
|
+
|
133
|
+
becomes:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
rule do
|
137
|
+
from %r'<%'
|
138
|
+
to %r'%>'
|
139
|
+
rule do
|
140
|
+
match %r'\b(def|end)\b'
|
141
|
+
…
|
142
|
+
end
|
143
|
+
…
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
`contentName` is `content_scope`:
|
148
|
+
|
149
|
+
```
|
150
|
+
{ begin = '#if 0(\s.*)?$'; end = '#endif';
|
151
|
+
contentName = 'comment.block.preprocessor';
|
152
|
+
};
|
153
|
+
```
|
154
|
+
|
155
|
+
becomes:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
rule do
|
159
|
+
from %r'#if 0(\s.*)?$'
|
160
|
+
to %r'#endif'
|
161
|
+
content_scope 'comment.block.preprocessor'
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
`captures`, `beginCaptures`, `endCaptures` are specified as a final hash argument
|
166
|
+
for `match`, `from` and `to`, respectively:
|
167
|
+
|
168
|
+
```
|
169
|
+
{ match = '(@selector\()(.*?)(\))';
|
170
|
+
captures = {
|
171
|
+
1 = { name = 'storage.type.objc'; };
|
172
|
+
3 = { name = 'storage.type.objc'; };
|
173
|
+
};
|
174
|
+
};
|
175
|
+
```
|
176
|
+
|
177
|
+
becomes:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
rule do
|
181
|
+
match %r'(@selector\()(.*?)(\))',
|
182
|
+
1 => 'storage.type.objc',
|
183
|
+
2 => 'storage.type.objc'
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
Captures that are common to `from` and `to` can be specified with `both`:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
rule 'comment.block.documentation.ruby' do
|
191
|
+
from %r/^=begin/
|
192
|
+
to %r/^=end/
|
193
|
+
both 0 => 'punctuation.definition.comment.ruby'
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
197
|
+
`include` is specified without patterns=:
|
198
|
+
|
199
|
+
```
|
200
|
+
{ begin = '<\?(php|=)?'; end = '\?>'; patterns = (
|
201
|
+
{ include = "source.php"; }
|
202
|
+
);
|
203
|
+
}
|
204
|
+
```
|
205
|
+
|
206
|
+
becomes:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
rule do
|
210
|
+
from %r'<\?(php|=)?'
|
211
|
+
to %r'\?>'
|
212
|
+
include 'source.php'
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
Finally, repository entries use `fragment`:
|
217
|
+
|
218
|
+
```
|
219
|
+
patterns = (
|
220
|
+
{ name = 'string.unquoted.qq.perl';
|
221
|
+
begin = 'qq\('; end = '\)'; patterns = (
|
222
|
+
{ include = '#qq_string_content'; },
|
223
|
+
);
|
224
|
+
},
|
225
|
+
…
|
226
|
+
); // end of patterns
|
227
|
+
repository = {
|
228
|
+
qq_string_content = {
|
229
|
+
begin = '\('; end = '\)'; patterns = (
|
230
|
+
{ include = '#qq_string_content'; },
|
231
|
+
);
|
232
|
+
};
|
233
|
+
};
|
234
|
+
```
|
235
|
+
|
236
|
+
becomes:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
rule 'string.unquoted.qq.perl' do
|
240
|
+
from %r'qq\('
|
241
|
+
to %r'\)'
|
242
|
+
include '#qq_string_content'
|
243
|
+
end
|
244
|
+
|
245
|
+
fragment :qq_string_content
|
246
|
+
rule do
|
247
|
+
from %r'\('
|
248
|
+
to %r'\)'
|
249
|
+
include '#qq_string_content'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
|
255
|
+
## Commands
|
256
|
+
|
257
|
+
Commands are specified in a `commands` block by the `item` method:
|
258
|
+
|
259
|
+
```json
|
260
|
+
// in Default.sublime-commands:
|
261
|
+
{ "caption": "Word Wrap: Toggle", "command": "toggle_setting", "args": {"setting": "word_wrap"} },
|
262
|
+
{ "caption": "Convert Case: Upper Case", "command": "upper_case" },
|
263
|
+
{ "command": "toggle_comment", "args": {"block": false}, "caption": "Toggle Comment" },
|
264
|
+
```
|
265
|
+
|
266
|
+
becomes:
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
commands 'Default' do
|
270
|
+
item 'Word Wrap: Toggle', toggle_setting("word_wrap")
|
271
|
+
item 'Convert Case: Upper Case', upper_case
|
272
|
+
item 'Toggle Comment', toggle_comment(block: false)
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
Commands are transcribed as Ruby method invocations. If the first argument key is the same
|
277
|
+
as the last word of the command, it is not written: `toggle_setting(setting: "word_wrap")`
|
278
|
+
is transcribed as `toggle_setting("word_wrap")`. This is the case everywhere ST commands are
|
279
|
+
specified (mouse & key bindings, macros).
|
280
|
+
|
281
|
+
## Menus
|
282
|
+
|
283
|
+
Menus are specified in a `menu` block with the `item` method, that accepts an optional
|
284
|
+
block to define submenus:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
menu 'Main' do
|
288
|
+
item '&File', id: 'file' do
|
289
|
+
item '&New File', new_file
|
290
|
+
item '&Open File…', prompt_open_file, platform: '!OSX'
|
291
|
+
item 'Open Folder…', prompt_open_folder, platform: '!OSX'
|
292
|
+
item 'Open…', prompt_open, platform: 'OSX'
|
293
|
+
item 'Open &Recent' do
|
294
|
+
item 'Reopen Closed File', reopen_last_file
|
295
|
+
item '-'
|
296
|
+
0.upto(7) { |i| item open_recent_file(index: i) }
|
297
|
+
item '-'
|
298
|
+
0.upto(7) { |i| item open_recent_folder(index: i) }
|
299
|
+
item '-'
|
300
|
+
item 'Clear Items', clear_recent_files
|
301
|
+
end
|
302
|
+
...
|
303
|
+
end
|
304
|
+
end
|
305
|
+
```
|
306
|
+
|
307
|
+
The `mnemonic` option can be specified, but is also obtained by prefixing
|
308
|
+
the letter by `&` in the caption (use `&&` to obtain the character `&`).
|
309
|
+
|
310
|
+
## Mouse Bindings
|
311
|
+
|
312
|
+
Mouse bindings use the `mousemap` method with a block:
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
mousemap 'Default (Windows)' do
|
316
|
+
|
317
|
+
click 'button1', down: drag_select
|
318
|
+
click 'ctrl+button1', down: drag_select(additive: true)
|
319
|
+
click 'alt+button1', down: drag_select(subtractive: true)
|
320
|
+
click 'shift+button1', down: drag_select(extend: true)
|
321
|
+
click 'shift+ctrl+button1', down: drag_select(additive: true, extend: true)
|
322
|
+
click 'shift+alt+button1', down: drag_select(subtractive: true, extend: true)
|
323
|
+
click2 'button1', down: drag_select(by: "words")
|
324
|
+
click2 'ctrl+button1', down: drag_select(by: "words", additive: true)
|
325
|
+
click2 'alt+button1', down: drag_select(by: "words", subtractive: true)
|
326
|
+
click3 'button1', down: drag_select(by: "lines")
|
327
|
+
click3 'ctrl+button1', down: drag_select(by: "lines", additive: true)
|
328
|
+
click3 'alt+button1', down: drag_select(by: "lines", subtractive: true)
|
329
|
+
click 'shift+button2', down: drag_select(by: "columns")
|
330
|
+
click 'shift+ctrl+button2', down: drag_select(by: "columns", additive: true)
|
331
|
+
click 'shift+alt+button2', down: drag_select(by: "columns", subtractive: true)
|
332
|
+
click 'button3', down: drag_select(by: "columns")
|
333
|
+
click 'ctrl+button3', down: drag_select(by: "columns", additive: true)
|
334
|
+
click 'alt+button3', down: drag_select(by: "columns", subtractive: true)
|
335
|
+
click1 'button2+button1', down: drag_select, up: expand_selection(to: "line")
|
336
|
+
click2 'button2+button1', up: expand_selection_to_paragraph
|
337
|
+
click3 'button2+button1', up: select_all
|
338
|
+
click 'button4', up: prev_view
|
339
|
+
click 'button5', up: next_view
|
340
|
+
click 'button2+scroll_down', up: next_view
|
341
|
+
click 'button2+scroll_up', up: prev_view
|
342
|
+
click 'ctrl+scroll_down', up: decrease_font_size
|
343
|
+
click 'ctrl+scroll_up', up: increase_font_size
|
344
|
+
click 'button2', up: context_menu
|
345
|
+
|
346
|
+
end
|
347
|
+
```
|
348
|
+
|
349
|
+
The method `click` is for a single click (`click1` is also valid), while
|
350
|
+
`click2` is for a double-click and `click3` is for a triple-click. `press_command` is the
|
351
|
+
`down` option, `command` is the `up` option.
|
352
|
+
|
353
|
+
The buttons and modifiers can be renamed with `button_names`:
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
mousemap 'Default (Windows)' do
|
357
|
+
|
358
|
+
button_names(
|
359
|
+
'left' => 'button1',
|
360
|
+
'right' => 'button2',
|
361
|
+
'middle' => 'button3',
|
362
|
+
'back' => 'button4',
|
363
|
+
'forward' => 'button5',
|
364
|
+
'down' => 'scroll_down',
|
365
|
+
'up' => 'scroll_up',
|
366
|
+
'win' => 'super'
|
367
|
+
)
|
368
|
+
|
369
|
+
click 'left', down: drag_select
|
370
|
+
click 'ctrl+left', down: drag_select(additive: true)
|
371
|
+
click 'alt+left', down: drag_select(subtractive: true)
|
372
|
+
click 'shift+left', down: drag_select(extend: true)
|
373
|
+
click 'shift+ctrl+left', down: drag_select(additive: true, extend: true)
|
374
|
+
click 'shift+alt+left', down: drag_select(subtractive: true, extend: true)
|
375
|
+
click2 'left', down: drag_select(by: "words")
|
376
|
+
click2 'ctrl+left', down: drag_select(by: "words", additive: true)
|
377
|
+
click2 'alt+left', down: drag_select(by: "words", subtractive: true)
|
378
|
+
click3 'left', down: drag_select(by: "lines")
|
379
|
+
click3 'ctrl+left', down: drag_select(by: "lines", additive: true)
|
380
|
+
click3 'alt+left', down: drag_select(by: "lines", subtractive: true)
|
381
|
+
click 'shift+right', down: drag_select(by: "columns")
|
382
|
+
click 'shift+ctrl+right', down: drag_select(by: "columns", additive: true)
|
383
|
+
click 'shift+alt+right', down: drag_select(by: "columns", subtractive: true)
|
384
|
+
click 'middle', down: drag_select(by: "columns")
|
385
|
+
click 'ctrl+middle', down: drag_select(by: "columns", additive: true)
|
386
|
+
click 'alt+middle', down: drag_select(by: "columns", subtractive: true)
|
387
|
+
click1 'right+left', down: drag_select, up: expand_selection(to: "line")
|
388
|
+
click2 'right+left', up: expand_selection_to_paragraph
|
389
|
+
click3 'right+left', up: select_all
|
390
|
+
click 'back', up: prev_view
|
391
|
+
click 'forward', up: next_view
|
392
|
+
click 'right+down', up: next_view
|
393
|
+
click 'right+up', up: prev_view
|
394
|
+
click 'ctrl+down', up: decrease_font_size
|
395
|
+
click 'ctrl+up', up: increase_font_size
|
396
|
+
click 'right', up: context_menu
|
397
|
+
|
398
|
+
end
|
399
|
+
```
|
400
|
+
|
401
|
+
## Key Bindings
|
402
|
+
|
403
|
+
Key bindings are defined inside a `keymap` block. Each binding is specified
|
404
|
+
by the `bind` method, and the context by methods equivalent to `if`, `and`
|
405
|
+
and `or`. Because the latters are reserved in Ruby, `conditionals` gives
|
406
|
+
the method names used, which default to their French equivalent (`si`, `et`
|
407
|
+
and `ou`). You can also leave out `conditionals`, in which case you have to
|
408
|
+
use `_if`, `_and` and `_or`.
|
409
|
+
|
410
|
+
ST has three types of the context conditions:
|
411
|
+
|
412
|
+
- Just a key:
|
413
|
+
```json
|
414
|
+
{ "key": "auto_complete_visible" }
|
415
|
+
```
|
416
|
+
becomes:
|
417
|
+
```ruby
|
418
|
+
si auto_complete_visible
|
419
|
+
```
|
420
|
+
|
421
|
+
- A key and an operand.
|
422
|
+
|
423
|
+
- If the operand is a string, it is rendered as "== symbol"
|
424
|
+
```json
|
425
|
+
{"key": "panel", "operand": "find"}
|
426
|
+
```
|
427
|
+
becomes:
|
428
|
+
```ruby
|
429
|
+
si panel == :find
|
430
|
+
```
|
431
|
+
|
432
|
+
- Otherwise, it is rendered with `is`:
|
433
|
+
```json
|
434
|
+
{ "key": "setting.auto_complete_commit_on_tab", "operand": false }
|
435
|
+
```
|
436
|
+
becomes:
|
437
|
+
```ruby
|
438
|
+
si setting.auto_complete_commit_on_tab is false
|
439
|
+
```
|
440
|
+
|
441
|
+
- A key, an operator and an operand: this is rendered as a Ruby condition, with
|
442
|
+
the following operators:
|
443
|
+
```
|
444
|
+
equal ==
|
445
|
+
not_equal !=
|
446
|
+
regex_contains =~
|
447
|
+
not_regex_contains !~
|
448
|
+
regex_match .regex_match # comment
|
449
|
+
not_regex_match .not_regex_match # comment
|
450
|
+
```
|
451
|
+
The comment is 'could use =~ (resp. !~) with \A and \z' (correct me if I'm wrong).
|
452
|
+
|
453
|
+
Example:
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
keymap 'Default (Windows)' do
|
457
|
+
|
458
|
+
conditionals if: 'si', and: 'et', or: 'ou'
|
459
|
+
|
460
|
+
bind 'esc', single_selection
|
461
|
+
si num_selections != 1
|
462
|
+
bind 'esc', clear_fields
|
463
|
+
si has_next_field ## true
|
464
|
+
ou has_prev_field ## true
|
465
|
+
bind 'esc', hide_panel(cancel: true)
|
466
|
+
si panel_visible ## true
|
467
|
+
bind 'esc', hide_overlay
|
468
|
+
si overlay_visible ## true
|
469
|
+
bind 'esc', hide_auto_complete
|
470
|
+
si auto_complete_visible ## true
|
471
|
+
|
472
|
+
bind 'tab', insert_best_completion(default: "\t", exact: true)
|
473
|
+
bind 'tab', insert_best_completion(default: "\t", exact: false)
|
474
|
+
si setting.tab_completion ## true
|
475
|
+
bind 'tab', replace_completion_with_next_completion
|
476
|
+
si last_command ## "insert_best_completion"
|
477
|
+
et setting.tab_completion ## true
|
478
|
+
bind 'tab', reindent
|
479
|
+
si setting.auto_indent ## true
|
480
|
+
et all.selection_empty ## true
|
481
|
+
et all.preceding_text =~ /\A\z/
|
482
|
+
et all.following_text =~ /\A\z/
|
483
|
+
bind 'tab', indent
|
484
|
+
si text =~ /\n/
|
485
|
+
bind 'tab', next_field
|
486
|
+
si has_next_field ## true
|
487
|
+
bind 'tab', commit_completion
|
488
|
+
si auto_complete_visible
|
489
|
+
et setting.auto_complete_commit_on_tab
|
490
|
+
|
491
|
+
bind 'shift+tab', insert(characters: "\t")
|
492
|
+
bind 'shift+tab', unindent
|
493
|
+
si setting.shift_tab_unindent ## true
|
494
|
+
ou preceding_text =~ /\A[\t ]*\z/
|
495
|
+
ou text =~ /\n/
|
496
|
+
bind 'shift+tab', prev_field
|
497
|
+
si has_prev_field ## true
|
498
|
+
|
499
|
+
end
|
500
|
+
```
|
501
|
+
|
502
|
+
## Keyboard Definitions
|
503
|
+
|
504
|
+
This feature is experimental. It allows translating keymaps between what you type and what ST sees.
|
505
|
+
For instance, when I type <kbd>ctrl+^</kbd> on my French keyboard on Windows, ST sees
|
506
|
+
<kbd>ctrl+]</kbd>. And when I type <kbd>ctrl+!</kbd>, it sees... nothing!
|
507
|
+
|
508
|
+
So unless your keyboard matches the Sublime Text one, you can describe your keyboard in
|
509
|
+
a keyboard definition file `*.keyboard.rb`, and specify it when importing the package:
|
510
|
+
this will convert the key bindings for your keyboard, adding an instruction
|
511
|
+
`keyboard '(name)'` inside the keymap block of key bindings. There will be a FIXME
|
512
|
+
comment when the keystroke is not available for your keyboard:
|
513
|
+
|
514
|
+
```ruby
|
515
|
+
# FIXME: no equivalent for keystroke: ctrl+break
|
516
|
+
# bind 'ctrl+break', exec(kill: true)
|
517
|
+
```
|
518
|
+
|
519
|
+
The key bindings will be converted back to ST key names when exporting.
|
520
|
+
|
521
|
+
This works well on Windows, but so-so (if at all, I don't remember well) on my Mac with
|
522
|
+
French keyboard. Not tested on Linux.
|
523
|
+
|
524
|
+
To understand the instructions below, look at a French keyboard (from France).
|
525
|
+
|
526
|
+
```ruby
|
527
|
+
keyboard 'AZERTY-fr-FR-v6 (Windows 7)' do
|
528
|
+
|
529
|
+
os 'Windows'
|
530
|
+
|
531
|
+
# modifiers
|
532
|
+
# they will be displayed in the order given here
|
533
|
+
|
534
|
+
add_modifiers 'shift ctrl win alt'
|
535
|
+
|
536
|
+
map_modifier 'win' => 'super'
|
537
|
+
|
538
|
+
# describe the physical keys present on the keyboard
|
539
|
+
#
|
540
|
+
# If the name matches a ST key name, this creates the association,
|
541
|
+
# assuming the key behaves exactly like the ST key when the name
|
542
|
+
# has more than 1 character. When it has only one character,
|
543
|
+
# it is assumed to generate just a chr_event without modifiers,
|
544
|
+
# and to behave like le ST key when modified.
|
545
|
+
|
546
|
+
add_keys 'esc', st_keys: 'escape'
|
547
|
+
add_keys 'f1-f12'
|
548
|
+
add_keys 'print_screen scroll_lock'
|
549
|
+
add_keys 'pause'
|
550
|
+
|
551
|
+
add_keys '²'
|
552
|
+
# we could do this:
|
553
|
+
# add_keys '& é " \' ( - è _ ç à', st_keys: '1 2 3 4 5 6 7 8 9 0'
|
554
|
+
# but I prefer to see 'ctrl+1' rather than 'ctrl+&', for instance, so:
|
555
|
+
add_keys '1 2 3 4 5 6 7 8 9 0'
|
556
|
+
add_keys ') ='
|
557
|
+
add_keys 'backspace tab'
|
558
|
+
add_keys 'a z e r t y u i o p ^ $ enter'
|
559
|
+
add_keys 'q s d f g h j k l m ù *'
|
560
|
+
add_keys '< w x c v b n , ; : !'
|
561
|
+
add_keys 'space context_menu'
|
562
|
+
|
563
|
+
add_keys 'ins del home end pgup pgdn', st_keys: 'insert delete home end pageup pagedown'
|
564
|
+
add_keys 'up down left right' #, display: '↑ ↓ ← →'
|
565
|
+
|
566
|
+
add_keys 'num/ num* num- num+', st_keys: 'keypad_divide keypad_multiply keypad_minus keypad_plus'
|
567
|
+
add_keys 'num0-num9 num.', st_keys: 'keypad0-keypad9 keypad_period'
|
568
|
+
add_keys 'num_enter', st_keys: 'keypad_enter'
|
569
|
+
|
570
|
+
# assign general keystroke substitutions
|
571
|
+
|
572
|
+
map_key '²' => "'" # modifier+² is seen as modifier+'
|
573
|
+
|
574
|
+
map_key ')' => '['
|
575
|
+
map_key '=' => '=' # match
|
576
|
+
|
577
|
+
map_key '^' => ']'
|
578
|
+
map_key '$' => ';'
|
579
|
+
|
580
|
+
map_key 'ù' => '`'
|
581
|
+
map_key '*' => '\\'
|
582
|
+
|
583
|
+
map_key ',' => ',' # match
|
584
|
+
map_key ';' => '.'
|
585
|
+
|
586
|
+
map_key ':' => '/'
|
587
|
+
map_key '!' => nil # modifier+! is not seen
|
588
|
+
|
589
|
+
map_key '<' => nil
|
590
|
+
|
591
|
+
# map some special keystrokes to characters
|
592
|
+
# this does not remove the key mapping, if any:
|
593
|
+
# it therefore means ST sees both a key event and a chr event
|
594
|
+
# 'dead' means it's a dead key
|
595
|
+
|
596
|
+
map_char 'ctrl+alt+2' => '~', dead: true
|
597
|
+
map_char 'ctrl+alt+3' => '#'
|
598
|
+
map_char "ctrl+alt+4" => '{'
|
599
|
+
map_char 'ctrl+alt+5' => '['
|
600
|
+
map_char 'ctrl+alt+6' => '|'
|
601
|
+
map_char 'ctrl+alt+7' => '`', dead: true
|
602
|
+
map_char 'ctrl+alt+8' => '\\'
|
603
|
+
map_char 'ctrl+alt+9' => '^'
|
604
|
+
map_char 'ctrl+alt+0' => '@'
|
605
|
+
map_char 'ctrl+alt+)' => ']'
|
606
|
+
map_char 'ctrl+alt+=' => '}'
|
607
|
+
map_char 'ctrl+alt+e' => '€'
|
608
|
+
map_char 'ctrl+alt+$' => '¤'
|
609
|
+
|
610
|
+
# custom v6 mappings
|
611
|
+
|
612
|
+
map_char 'ctrl+)' => '@'
|
613
|
+
map_char 'ctrl+=' => '#'
|
614
|
+
map_char 'ctrl+^' => '{'
|
615
|
+
map_char 'ctrl+$' => '}'
|
616
|
+
map_char 'ctrl+ù' => '['
|
617
|
+
map_char 'ctrl+*' => ']'
|
618
|
+
map_char 'ctrl+:' => '\\'
|
619
|
+
map_char 'ctrl+!' => '|'
|
620
|
+
|
621
|
+
# OS-reserved keystokes (by default, means ST does not see the keystoke)
|
622
|
+
|
623
|
+
os_action 'win+f1' => 'Windows Help'
|
624
|
+
|
625
|
+
os_action 'win+tab' => 'Aero Flip'
|
626
|
+
os_action 'win+pause' => 'System Properties'
|
627
|
+
os_action 'win+e' => 'Explorer (Computer)'
|
628
|
+
os_action 'win+r' => 'Run'
|
629
|
+
os_action 'win+t' => 'TaskBar'
|
630
|
+
os_action 'win+u' => 'Ease of Access Center (Utility Manager)'
|
631
|
+
os_action 'win+p' => 'Display Choice'
|
632
|
+
os_action 'win+d' => 'Desktop'
|
633
|
+
os_action 'win+f' => 'Find Files'
|
634
|
+
os_action 'win+l' => 'Lock Computer'
|
635
|
+
os_action 'win+m' => 'Minimize All'
|
636
|
+
os_action 'win+shift+m' => 'Restore All'
|
637
|
+
os_action 'win+b' => 'Focus Taskbar Icons'
|
638
|
+
os_action 'win+num+' => 'Magnifier'
|
639
|
+
|
640
|
+
os_action 'ctrl+alt+del' => 'System Menu/Reboot'
|
641
|
+
os_action 'alt+tab' => 'Cycle Running Apps'
|
642
|
+
os_action 'shift+alt+tab' => 'Cycle Back Running Apps'
|
643
|
+
|
644
|
+
os_action 'print_screen' => 'Screen Capture'
|
645
|
+
os_action 'alt+print_screen' => 'Window Capture'
|
646
|
+
|
647
|
+
os_action 'ctrl+esc' => 'Start Menu'
|
648
|
+
os_action 'ctrl+shift+esc' => 'Task Manager'
|
649
|
+
|
650
|
+
os_action 'alt+esc' => 'Next Taskbar Window'
|
651
|
+
os_action 'alt+space' => 'Window Control Menu', key_event: true
|
652
|
+
|
653
|
+
# keystrokes not seen by ST
|
654
|
+
|
655
|
+
map_key 'win+h' => nil
|
656
|
+
map_key 'win+c' => nil
|
657
|
+
map_key 'win+v' => nil
|
658
|
+
|
659
|
+
# strange combinations generating character events
|
660
|
+
|
661
|
+
map_char 'win+shift+d' => 'D'
|
662
|
+
map_char 'ctrl+pause' => "\x03" # ETX
|
663
|
+
|
664
|
+
map_key 'ctrl+pause' => nil # implies that shift+ctrl+pause, alt+ctrl+pause are not seen either
|
665
|
+
|
666
|
+
# numeric keypad (with NumLock on)
|
667
|
+
|
668
|
+
'/*-+'.chars.each do |c|
|
669
|
+
map_char 'num' << c => c
|
670
|
+
map_char 'shift+num' << c => c
|
671
|
+
end
|
672
|
+
|
673
|
+
'0123456789'.chars.each do |c|
|
674
|
+
map_char 'num' << c => c
|
675
|
+
end
|
676
|
+
|
677
|
+
map_char 'num.' => '.'
|
678
|
+
|
679
|
+
%w(0:insert 1:end 2:down 3:pagedown 4:left 5:clear 6:right 7:home 8:up 9:pageup).each do |spec|
|
680
|
+
n, k = spec.split(':')
|
681
|
+
map_key "shift+num#{n}" => k
|
682
|
+
end
|
683
|
+
|
684
|
+
map_key 'shift+num.' => 'delete'
|
685
|
+
|
686
|
+
map_key 'num_enter' => 'enter' # so keypad_enter is not available
|
687
|
+
|
688
|
+
# test: shift+ctrl+keypad5 is undestood as ctrl+clear
|
689
|
+
|
690
|
+
end
|
691
|
+
```
|
692
|
+
|
693
|
+
## Macros
|
694
|
+
|
695
|
+
Macros are rendered by `macro` blocks, like this:
|
696
|
+
|
697
|
+
```ruby
|
698
|
+
macro "Add Line" do
|
699
|
+
move_to "hardeol"
|
700
|
+
insert characters: "\n"
|
701
|
+
end
|
702
|
+
|
703
|
+
macro "Add Line Before" do
|
704
|
+
move_to "hardbol"
|
705
|
+
insert characters: "\n"
|
706
|
+
move by: "lines", forward: false
|
707
|
+
reindent force_indent: false
|
708
|
+
end
|
709
|
+
|
710
|
+
macro "Add Line in Braces" do
|
711
|
+
insert characters: "\n\n"
|
712
|
+
move by: "lines", forward: false
|
713
|
+
move_to "hardeol", extend: false
|
714
|
+
reindent single_line: true
|
715
|
+
end
|
716
|
+
```
|
717
|
+
|
718
|
+
## Settings
|
719
|
+
|
720
|
+
Settings are rendered as `settings` blocks:
|
721
|
+
|
722
|
+
```ruby
|
723
|
+
settings 'Preferences' do
|
724
|
+
|
725
|
+
color_scheme 'Packages/Color Scheme - Default/Monokai.tmTheme'
|
726
|
+
font_size 10
|
727
|
+
font_options []
|
728
|
+
word_separators './\()"\'-:,.;<>~!@#$%^&*|+=[]{}`~?'
|
729
|
+
line_numbers true
|
730
|
+
...
|
731
|
+
auto_complete_triggers [{"selector"=>"text.html", "characters"=>"<"}]
|
732
|
+
...
|
733
|
+
scroll_speed 1.0
|
734
|
+
folder_exclude_patterns %w(.svn .git .hg CVS)
|
735
|
+
...
|
736
|
+
|
737
|
+
end
|
738
|
+
```
|
739
|
+
|
740
|
+
## Themes
|
741
|
+
|
742
|
+
Themes are `theme` blocks. Here is my own coloring theme:
|
743
|
+
|
744
|
+
```ruby
|
745
|
+
theme 'Spartan' do
|
746
|
+
|
747
|
+
# I dont't like code looking like a Christmas tree.
|
748
|
+
# Initially, it was Mac Classic by Chris Thomas.
|
749
|
+
|
750
|
+
author 'Thierry Lambert'
|
751
|
+
uuid '85C02434-2B86-488A-A8F2-E7A9A53385C5'
|
752
|
+
|
753
|
+
white = '#FFFFFF'
|
754
|
+
black = '#000000'
|
755
|
+
brown = '#804000'
|
756
|
+
navy = '#0000A0' # a bit brighter than 'official' navy
|
757
|
+
blue = '#0000FF'
|
758
|
+
red = '#FF0000'
|
759
|
+
green = '#008000'
|
760
|
+
|
761
|
+
gray = '#666666'
|
762
|
+
light_gray = '#888888'
|
763
|
+
pale_gray = '#BFBFBF'
|
764
|
+
blue_gray = '#365F91'
|
765
|
+
|
766
|
+
very_transparent_gray = '#D0D0D040'
|
767
|
+
more_transparent_gray = '#80808020'
|
768
|
+
transparent_gray = '#80808030'
|
769
|
+
less_transparent_gray = '#80808050'
|
770
|
+
|
771
|
+
turquoise_green = '#009070'
|
772
|
+
|
773
|
+
pale_red = '#FFD0D0'
|
774
|
+
|
775
|
+
base_colors \
|
776
|
+
background: white,
|
777
|
+
foreground: black,
|
778
|
+
caret: black,
|
779
|
+
invisibles: pale_gray,
|
780
|
+
line_highlight: transparent_gray,
|
781
|
+
selection: transparent_gray,
|
782
|
+
selection_border: less_transparent_gray,
|
783
|
+
inactive_selection: more_transparent_gray
|
784
|
+
|
785
|
+
item 'Marks', 'mark, bookmark', red
|
786
|
+
|
787
|
+
item 'Comment', 'comment', green
|
788
|
+
|
789
|
+
item 'String', 'string', brown
|
790
|
+
item 'Unquoted String', 'string.unquoted', back: very_transparent_gray
|
791
|
+
# item 'Regular Expression', 'string.regexp', brown
|
792
|
+
item 'Ruby Symbol', 'constant.other.symbol', brown
|
793
|
+
item 'Number', 'constant.numeric', brown
|
794
|
+
item 'Character Escape', 'constant.character.escape', brown, bold
|
795
|
+
item 'Markup: Character Entity', 'constant.character.entity, constant.character.parameter-entity', brown
|
796
|
+
|
797
|
+
item 'Keyword', 'keyword.control, keyword.other, storage', blue
|
798
|
+
item 'Language Constant', 'constant.language', blue
|
799
|
+
item 'Language Variable', 'variable.language', blue
|
800
|
+
|
801
|
+
item 'Library Constant', 'support.constant', navy
|
802
|
+
item 'Operator', 'keyword.operator', navy
|
803
|
+
item 'Other Keyword: Unit', 'keyword.other.unit, keyword.other.ini', navy
|
804
|
+
|
805
|
+
item 'Source in String', 'string source', bold
|
806
|
+
item 'Embedded Source', 'text source', back: '#D0D0A020' # looks bluish otherwise
|
807
|
+
|
808
|
+
item 'Type Name', 'entity.name.type', bold
|
809
|
+
# item 'Inherited Type Name', 'entity.other.inherited-class'
|
810
|
+
# item 'Type Reference', 'support.class'
|
811
|
+
|
812
|
+
item 'Function Name', 'entity.name.function, support.function.any-method', bold
|
813
|
+
item 'Library Function', 'support.function', navy
|
814
|
+
|
815
|
+
item 'Marked Variable', 'variable.other.readwrite, variable.other.macro', navy
|
816
|
+
# item 'Parameter Variable', 'variable.parameter'
|
817
|
+
|
818
|
+
item 'Property Name', 'support.type, support.constant.tm-grammar', navy
|
819
|
+
item 'Section Name', 'entity.name.section', bold
|
820
|
+
|
821
|
+
item 'Preprocessor Line', 'meta.preprocessor', turquoise_green
|
822
|
+
item 'Preprocessor Directive', 'keyword.control.import', turquoise_green, bold
|
823
|
+
|
824
|
+
item 'Deprecated', 'invalid.deprecated', red
|
825
|
+
item 'Deprecated Trailing Whitespace', 'invalid.deprecated.trailing-whitespace', back: pale_red
|
826
|
+
item 'Illegal', 'invalid.illegal', red
|
827
|
+
|
828
|
+
# XML, HTML, CSS
|
829
|
+
item 'ML DOCTYPE', <<-SCOPE, gray
|
830
|
+
meta.tag.sgml.doctype, meta.tag.sgml.doctype entity, meta.tag.sgml.doctype string,
|
831
|
+
meta.tag.preprocessor.xml, meta.tag.preprocessor.xml entity, meta.tag.preprocessor.xml string
|
832
|
+
SCOPE
|
833
|
+
item 'ML Tag Characters', 'meta.tag, declaration.tag', navy
|
834
|
+
item 'ML Tag Name', 'entity.name.tag', navy
|
835
|
+
item 'ML Attribute Name', 'entity.other.attribute-name', navy
|
836
|
+
item 'CSS Selectors', 'source.css entity.name.tag, source.css entity.other.attribute-name', blue
|
837
|
+
|
838
|
+
# Markdown, Textile, etc.
|
839
|
+
item 'Markup: Heading Characters', 'markup.heading', bold
|
840
|
+
# item 'Markup: List', 'markup.list'
|
841
|
+
# item 'Markup: Quote', 'markup.quote'
|
842
|
+
item 'Markup: Raw', 'markup.raw', back: very_transparent_gray
|
843
|
+
item 'Markup: Bold', 'markup.bold', bold
|
844
|
+
item 'Markup: Italic', 'markup.italic', italic
|
845
|
+
item 'Markup: Underline', 'markup.underline', underline
|
846
|
+
# item 'Markup: Deleted', 'markup.deleted'
|
847
|
+
# item 'Markup: Inserted', 'markup.inserted'
|
848
|
+
|
849
|
+
# Subtitles
|
850
|
+
item 'Markup: Number', 'markup.number', pale_gray
|
851
|
+
item 'Markup: Timing', 'markup.timing', light_gray
|
852
|
+
item 'Markup: Timing Arrow', 'markup.punctuation', light_gray
|
853
|
+
|
854
|
+
end
|
855
|
+
```
|
856
|
+
|
857
|
+
## Preferences
|
858
|
+
|
859
|
+
Preferences are specified in `preferences` blocks with a name and a scope:
|
860
|
+
|
861
|
+
```ruby
|
862
|
+
preferences 'Indentation Rules - Comments' => 'comment' do
|
863
|
+
preserve_indent true
|
864
|
+
end
|
865
|
+
|
866
|
+
preferences 'Indentation Rules' => 'source' do
|
867
|
+
decrease_indent_pattern %r'^(.*\*/)?\s*\}[;\s]*$'
|
868
|
+
increase_indent_pattern %r/^.*(\{[^}"']*)$/
|
869
|
+
disable_indent_next_line_pattern %r/^\s*\{[\]})]*\s*$/
|
870
|
+
indent_parens true
|
871
|
+
end
|
872
|
+
|
873
|
+
preferences 'Symbol List' => 'entity.name.function, entity.name.type, meta.toc-list' do
|
874
|
+
show_in_symbol_list true
|
875
|
+
uuid "0A0DA1FC-59DE-4FD9-9A2C-63C6811A3C39"
|
876
|
+
end
|
877
|
+
```
|
878
|
+
|
879
|
+
## Snippets
|
880
|
+
|
881
|
+
Snippets are defined in `snippets` blocks.
|
882
|
+
|
883
|
+
```ruby
|
884
|
+
snippets do
|
885
|
+
# definition statements
|
886
|
+
end
|
887
|
+
```
|
888
|
+
|
889
|
+
### Definition statements:
|
890
|
+
|
891
|
+
- `default_scope 'scope'`
|
892
|
+
|
893
|
+
Sets the default scope for the following definitions.
|
894
|
+
|
895
|
+
- `file_format <format>`
|
896
|
+
|
897
|
+
Defines the file format for the following definitions.
|
898
|
+
Valid formats are `:textmate` and `:sublime_text`.
|
899
|
+
The default is `:sublime_text`.
|
900
|
+
|
901
|
+
- `tab 'tab trigger', 'description', 'content' [, options]`
|
902
|
+
|
903
|
+
Defines a snippet inserted by pressing <kbd>tab</kbd> after the
|
904
|
+
text given by `'tab trigger'`.
|
905
|
+
|
906
|
+
- `key 'key equivalent', 'description', 'content' [, options]`
|
907
|
+
|
908
|
+
Defines a snippet inserted by pressing the keystroke
|
909
|
+
`'key equivalent'` (TextMate only)
|
910
|
+
|
911
|
+
### Options for 'tab' and 'key':
|
912
|
+
|
913
|
+
- `scope: 'scopes'` (the default is given by default_scope)
|
914
|
+
|
915
|
+
- `file: 'base_file_name'` (the default is the description)
|
916
|
+
|
917
|
+
### Options for TextMate only:
|
918
|
+
|
919
|
+
- `key_equivalent: 'key equivalent'` (for 'tab')
|
920
|
+
|
921
|
+
- `tab_trigger: 'tab trigger'` (for 'key')
|
922
|
+
|
923
|
+
- `semantic_class: 'semantic class scope'`
|
924
|
+
|
925
|
+
- `uuid: 'uuid'`
|
926
|
+
|
927
|
+
- `bundle_uuid: 'bundle uuid'`
|