tangled
alpha
login
or
join now
vmx-atproto-dev.bsky.social
/
mlf
forked from
stavola.xyz/mlf
0
fork
atom
A human-friendly DSL for ATProto Lexicons
0
fork
atom
overview
issues
pulls
pipelines
Syntax highlighting and autocomplete for annotations
stavola.xyz
5 months ago
8a5756bb
76940da8
+782
-3
7 changed files
expand all
collapse all
unified
split
mlf-lsp
src
context.rs
server.rs
tree-sitter-mlf
grammar.js
queries
highlights.scm
src
grammar.json
node-types.json
website
syntaxes
mlf.sublime-syntax
+52
mlf-lsp/src/context.rs
···
10
10
TypePosition,
11
11
/// Inside constrained { } block
12
12
ConstraintBlock,
13
13
+
/// Typing an annotation (after @)
14
14
+
Annotation,
15
15
+
/// Typing annotation selectors (after @rust,)
16
16
+
AnnotationSelector,
13
17
/// Unknown context
14
18
Unknown,
15
19
}
16
20
17
21
/// Detect the completion context based on the text before the cursor
18
22
pub fn detect_context(text_before_cursor: &str) -> CompletionContext {
23
23
+
// Check if we're typing an annotation
24
24
+
if let Some(last_line) = text_before_cursor.lines().last() {
25
25
+
let last_line_trimmed = last_line.trim_start();
26
26
+
27
27
+
// Check for annotation selector context: @rust, or @rust,typescript,
28
28
+
if let Some(at_pos) = last_line_trimmed.rfind('@') {
29
29
+
let after_at = &last_line_trimmed[at_pos + 1..];
30
30
+
31
31
+
// If there's a colon, we're past the selectors
32
32
+
if after_at.contains(':') {
33
33
+
// Check if we're completing the annotation name
34
34
+
let parts: Vec<&str> = after_at.split(':').collect();
35
35
+
if parts.len() >= 2 && !parts[1].contains('(') {
36
36
+
return CompletionContext::Annotation;
37
37
+
}
38
38
+
} else if after_at.ends_with(',') || (after_at.contains(',') && !after_at.contains(':')) {
39
39
+
// We're completing another selector: @rust, or @rust,typescript,
40
40
+
return CompletionContext::AnnotationSelector;
41
41
+
} else if !after_at.is_empty() && !after_at.contains(':') && !after_at.contains('(') {
42
42
+
// We're completing the first part (could be selector or annotation name)
43
43
+
// Default to annotation for simplicity
44
44
+
return CompletionContext::Annotation;
45
45
+
} else if after_at.is_empty() {
46
46
+
// Just typed @ - suggest annotations
47
47
+
return CompletionContext::Annotation;
48
48
+
}
49
49
+
}
50
50
+
}
51
51
+
19
52
// Check if we're in a use statement (check before trimming!)
20
53
if let Some(last_line) = text_before_cursor.lines().last() {
21
54
let last_line = last_line.trim_start(); // Only trim left side
···
126
159
fn test_top_level() {
127
160
assert_eq!(detect_context(""), CompletionContext::TopLevel);
128
161
assert_eq!(detect_context("rec"), CompletionContext::TopLevel);
162
162
+
}
163
163
+
164
164
+
#[test]
165
165
+
fn test_annotation() {
166
166
+
assert_eq!(detect_context("@"), CompletionContext::Annotation);
167
167
+
assert_eq!(detect_context("@dep"), CompletionContext::Annotation);
168
168
+
assert_eq!(detect_context("@deprecated"), CompletionContext::Annotation);
169
169
+
}
170
170
+
171
171
+
#[test]
172
172
+
fn test_annotation_selector() {
173
173
+
assert_eq!(detect_context("@rust,"), CompletionContext::AnnotationSelector);
174
174
+
assert_eq!(detect_context("@rust,typescript,"), CompletionContext::AnnotationSelector);
175
175
+
}
176
176
+
177
177
+
#[test]
178
178
+
fn test_annotation_after_selector() {
179
179
+
assert_eq!(detect_context("@rust:"), CompletionContext::Annotation);
180
180
+
assert_eq!(detect_context("@rust,typescript:"), CompletionContext::Annotation);
129
181
}
130
182
}
+154
mlf-lsp/src/server.rs
···
792
792
".".to_string(),
793
793
":".to_string(),
794
794
" ".to_string(),
795
795
+
"@".to_string(),
796
796
+
",".to_string(),
795
797
]),
796
798
..Default::default()
797
799
}),
···
896
898
if let Some(lexicon) = &doc_state.lexicon {
897
899
// Convert position to offset
898
900
if let Some(offset) = position_to_offset(&doc_state.text, position) {
901
901
+
// Check if we're hovering over an annotation
902
902
+
for item in &lexicon.items {
903
903
+
let annotations_to_check = match item {
904
904
+
Item::Record(r) => Some(&r.annotations),
905
905
+
Item::InlineType(i) => Some(&i.annotations),
906
906
+
Item::DefType(d) => Some(&d.annotations),
907
907
+
Item::Token(t) => Some(&t.annotations),
908
908
+
Item::Query(q) => Some(&q.annotations),
909
909
+
Item::Procedure(p) => Some(&p.annotations),
910
910
+
Item::Subscription(s) => Some(&s.annotations),
911
911
+
_ => None,
912
912
+
};
913
913
+
914
914
+
if let Some(annotations) = annotations_to_check {
915
915
+
for annotation in annotations {
916
916
+
if annotation.span.start <= offset && offset <= annotation.span.end {
917
917
+
// Build hover content for the annotation
918
918
+
let mut contents = vec![];
919
919
+
920
920
+
// Format annotation name with selectors if present
921
921
+
let annotation_display = if annotation.selectors.is_empty() {
922
922
+
format!("@{}", annotation.name.name)
923
923
+
} else {
924
924
+
let selector_names: Vec<_> = annotation.selectors.iter()
925
925
+
.map(|s| s.name.as_str())
926
926
+
.collect();
927
927
+
format!("@{}:{}", selector_names.join(","), annotation.name.name)
928
928
+
};
929
929
+
930
930
+
contents.push(MarkedString::LanguageString(LanguageString {
931
931
+
language: "mlf".to_string(),
932
932
+
value: annotation_display.clone(),
933
933
+
}));
934
934
+
935
935
+
// Add description based on annotation name
936
936
+
let description = match annotation.name.name.as_str() {
937
937
+
"deprecated" => "Marks this definition as deprecated",
938
938
+
"main" => "Designates this as the main definition for conflict resolution",
939
939
+
"key" => "Specifies the record key type (e.g., 'tid', 'literal:self')",
940
940
+
"encoding" => "Specifies MIME type encoding for XRPC (e.g., 'application/json', 'application/cbor')",
941
941
+
"since" => "Indicates the version when this was added",
942
942
+
"doc" => "Provides a documentation URL",
943
943
+
"validate" => "Specifies validation rules",
944
944
+
"cache" => "Defines caching strategy",
945
945
+
"indexed" => "Marks this field as indexed",
946
946
+
"sensitive" => "Marks this field as containing sensitive data (e.g., PII)",
947
947
+
_ => "Custom annotation",
948
948
+
};
949
949
+
950
950
+
contents.push(MarkedString::String(description.to_string()));
951
951
+
952
952
+
// Add information about selectors if present
953
953
+
if !annotation.selectors.is_empty() {
954
954
+
let selector_info = format!(
955
955
+
"This annotation applies to: {}",
956
956
+
annotation.selectors.iter()
957
957
+
.map(|s| s.name.as_str())
958
958
+
.collect::<Vec<_>>()
959
959
+
.join(", ")
960
960
+
);
961
961
+
contents.push(MarkedString::String(selector_info));
962
962
+
} else {
963
963
+
contents.push(MarkedString::String(
964
964
+
"This annotation is visible to all generators".to_string()
965
965
+
));
966
966
+
}
967
967
+
968
968
+
return Ok(Some(Hover {
969
969
+
contents: HoverContents::Array(contents),
970
970
+
range: None,
971
971
+
}));
972
972
+
}
973
973
+
}
974
974
+
}
975
975
+
976
976
+
// Also check field annotations
977
977
+
match item {
978
978
+
Item::Record(r) => {
979
979
+
for field in &r.fields {
980
980
+
for annotation in &field.annotations {
981
981
+
if annotation.span.start <= offset && offset <= annotation.span.end {
982
982
+
let annotation_display = if annotation.selectors.is_empty() {
983
983
+
format!("@{}", annotation.name.name)
984
984
+
} else {
985
985
+
let selector_names: Vec<_> = annotation.selectors.iter()
986
986
+
.map(|s| s.name.as_str())
987
987
+
.collect();
988
988
+
format!("@{}:{}", selector_names.join(","), annotation.name.name)
989
989
+
};
990
990
+
991
991
+
return Ok(Some(Hover {
992
992
+
contents: HoverContents::Scalar(
993
993
+
MarkedString::LanguageString(LanguageString {
994
994
+
language: "mlf".to_string(),
995
995
+
value: format!("Field annotation: {}", annotation_display),
996
996
+
})
997
997
+
),
998
998
+
range: None,
999
999
+
}));
1000
1000
+
}
1001
1001
+
}
1002
1002
+
}
1003
1003
+
}
1004
1004
+
_ => {}
1005
1005
+
}
1006
1006
+
}
1007
1007
+
899
1008
// Find the item at this position
900
1009
if let Some(item) = find_item_at_offset(lexicon, offset) {
901
1010
let name = get_item_name(item);
···
1260
1369
}
1261
1370
1262
1371
tracing::debug!("Total completions: {}", completions.len());
1372
1372
+
}
1373
1373
+
1374
1374
+
MlfCompletionContext::Annotation => {
1375
1375
+
// Suggest common annotation names
1376
1376
+
let annotations = vec![
1377
1377
+
("deprecated", "Mark as deprecated"),
1378
1378
+
("main", "Main definition for conflict resolution"),
1379
1379
+
("key", "Specify record key type (e.g., @key(\"literal:self\"))"),
1380
1380
+
("encoding", "Specify MIME type encoding (e.g., @encoding(\"application/cbor\"))"),
1381
1381
+
("since", "Version when added (e.g., @since(1, 2, 0))"),
1382
1382
+
("doc", "Documentation URL (e.g., @doc(\"https://example.com\"))"),
1383
1383
+
("validate", "Validation rules (e.g., @validate(min: 0, max: 100))"),
1384
1384
+
("cache", "Caching strategy (e.g., @cache(ttl: 3600))"),
1385
1385
+
("indexed", "Mark field as indexed"),
1386
1386
+
("sensitive", "Mark field as containing sensitive data"),
1387
1387
+
];
1388
1388
+
1389
1389
+
for (label, detail) in annotations {
1390
1390
+
completions.push(CompletionItem {
1391
1391
+
label: label.to_string(),
1392
1392
+
kind: Some(CompletionItemKind::FUNCTION),
1393
1393
+
detail: Some(detail.to_string()),
1394
1394
+
..Default::default()
1395
1395
+
});
1396
1396
+
}
1397
1397
+
}
1398
1398
+
1399
1399
+
MlfCompletionContext::AnnotationSelector => {
1400
1400
+
// Suggest generator selector names
1401
1401
+
let generators = vec![
1402
1402
+
("rust", "Rust code generator"),
1403
1403
+
("typescript", "TypeScript code generator"),
1404
1404
+
("go", "Go code generator"),
1405
1405
+
("python", "Python code generator"),
1406
1406
+
("java", "Java code generator"),
1407
1407
+
];
1408
1408
+
1409
1409
+
for (label, detail) in generators {
1410
1410
+
completions.push(CompletionItem {
1411
1411
+
label: label.to_string(),
1412
1412
+
kind: Some(CompletionItemKind::MODULE),
1413
1413
+
detail: Some(detail.to_string()),
1414
1414
+
..Default::default()
1415
1415
+
});
1416
1416
+
}
1263
1417
}
1264
1418
1265
1419
MlfCompletionContext::TopLevel => {
+50
tree-sitter-mlf/grammar.js
···
39
39
doc_comment: $ => token(seq('///', /.*/)),
40
40
comment: $ => token(seq('//', /.*/)),
41
41
42
42
+
// Annotations
43
43
+
annotation: $ => seq(
44
44
+
'@',
45
45
+
optional(field('selectors', $.annotation_selectors)),
46
46
+
field('name', $.identifier),
47
47
+
optional(field('args', $.annotation_args))
48
48
+
),
49
49
+
50
50
+
annotation_selectors: $ => seq(
51
51
+
$.identifier,
52
52
+
repeat(seq(',', $.identifier)),
53
53
+
':'
54
54
+
),
55
55
+
56
56
+
annotation_args: $ => seq(
57
57
+
'(',
58
58
+
optional(seq(
59
59
+
$.annotation_arg,
60
60
+
repeat(seq(',', $.annotation_arg)),
61
61
+
optional(',')
62
62
+
)),
63
63
+
')'
64
64
+
),
65
65
+
66
66
+
annotation_arg: $ => choice(
67
67
+
// Named argument: name: value
68
68
+
seq(
69
69
+
field('name', $.identifier),
70
70
+
':',
71
71
+
field('value', $.annotation_value)
72
72
+
),
73
73
+
// Positional argument: value
74
74
+
field('value', $.annotation_value)
75
75
+
),
76
76
+
77
77
+
annotation_value: $ => choice(
78
78
+
$.string,
79
79
+
$.number,
80
80
+
$.boolean
81
81
+
),
82
82
+
42
83
// Use statements
43
84
use_statement: $ => seq(
44
85
'use',
···
68
109
69
110
// Record definition
70
111
record_definition: $ => seq(
112
112
+
repeat($.annotation),
71
113
'record',
72
114
field('name', $.identifier),
73
115
field('body', $.record_body)
···
81
123
82
124
field: $ => seq(
83
125
optional($.doc_comment),
126
126
+
repeat($.annotation),
84
127
field('name', $.identifier),
85
128
optional('!'),
86
129
':',
···
90
133
91
134
// Inline type definition
92
135
inline_type_definition: $ => seq(
136
136
+
repeat($.annotation),
93
137
'inline',
94
138
'type',
95
139
field('name', $.identifier),
···
100
144
101
145
// Def type definition
102
146
def_type_definition: $ => seq(
147
147
+
repeat($.annotation),
103
148
'def',
104
149
'type',
105
150
field('name', $.identifier),
···
110
155
111
156
// Token definition
112
157
token_definition: $ => seq(
158
158
+
repeat($.annotation),
113
159
'token',
114
160
field('name', $.identifier),
115
161
';'
···
117
163
118
164
// Query definition
119
165
query_definition: $ => seq(
166
166
+
repeat($.annotation),
120
167
'query',
121
168
field('name', $.identifier),
122
169
field('params', $.parameter_list),
···
127
174
128
175
// Procedure definition
129
176
procedure_definition: $ => seq(
177
177
+
repeat($.annotation),
130
178
'procedure',
131
179
field('name', $.identifier),
132
180
field('params', $.parameter_list),
···
137
185
138
186
// Subscription definition
139
187
subscription_definition: $ => seq(
188
188
+
repeat($.annotation),
140
189
'subscription',
141
190
field('name', $.identifier),
142
191
field('params', $.parameter_list),
···
156
205
),
157
206
158
207
parameter: $ => seq(
208
208
+
repeat($.annotation),
159
209
field('name', $.identifier),
160
210
optional('!'),
161
211
':',
+12
tree-sitter-mlf/queries/highlights.scm
···
87
87
(doc_comment) @comment.documentation
88
88
(comment) @comment
89
89
90
90
+
; Annotations
91
91
+
"@" @punctuation.special
92
92
+
93
93
+
(annotation
94
94
+
name: (identifier) @attribute)
95
95
+
96
96
+
(annotation_selectors
97
97
+
(identifier) @namespace)
98
98
+
99
99
+
(annotation_arg
100
100
+
name: (identifier) @property)
101
101
+
90
102
; Operators
91
103
[
92
104
":"
+254
tree-sitter-mlf/src/grammar.json
···
77
77
]
78
78
}
79
79
},
80
80
+
"annotation": {
81
81
+
"type": "SEQ",
82
82
+
"members": [
83
83
+
{
84
84
+
"type": "STRING",
85
85
+
"value": "@"
86
86
+
},
87
87
+
{
88
88
+
"type": "CHOICE",
89
89
+
"members": [
90
90
+
{
91
91
+
"type": "FIELD",
92
92
+
"name": "selectors",
93
93
+
"content": {
94
94
+
"type": "SYMBOL",
95
95
+
"name": "annotation_selectors"
96
96
+
}
97
97
+
},
98
98
+
{
99
99
+
"type": "BLANK"
100
100
+
}
101
101
+
]
102
102
+
},
103
103
+
{
104
104
+
"type": "FIELD",
105
105
+
"name": "name",
106
106
+
"content": {
107
107
+
"type": "SYMBOL",
108
108
+
"name": "identifier"
109
109
+
}
110
110
+
},
111
111
+
{
112
112
+
"type": "CHOICE",
113
113
+
"members": [
114
114
+
{
115
115
+
"type": "FIELD",
116
116
+
"name": "args",
117
117
+
"content": {
118
118
+
"type": "SYMBOL",
119
119
+
"name": "annotation_args"
120
120
+
}
121
121
+
},
122
122
+
{
123
123
+
"type": "BLANK"
124
124
+
}
125
125
+
]
126
126
+
}
127
127
+
]
128
128
+
},
129
129
+
"annotation_selectors": {
130
130
+
"type": "SEQ",
131
131
+
"members": [
132
132
+
{
133
133
+
"type": "SYMBOL",
134
134
+
"name": "identifier"
135
135
+
},
136
136
+
{
137
137
+
"type": "REPEAT",
138
138
+
"content": {
139
139
+
"type": "SEQ",
140
140
+
"members": [
141
141
+
{
142
142
+
"type": "STRING",
143
143
+
"value": ","
144
144
+
},
145
145
+
{
146
146
+
"type": "SYMBOL",
147
147
+
"name": "identifier"
148
148
+
}
149
149
+
]
150
150
+
}
151
151
+
},
152
152
+
{
153
153
+
"type": "STRING",
154
154
+
"value": ":"
155
155
+
}
156
156
+
]
157
157
+
},
158
158
+
"annotation_args": {
159
159
+
"type": "SEQ",
160
160
+
"members": [
161
161
+
{
162
162
+
"type": "STRING",
163
163
+
"value": "("
164
164
+
},
165
165
+
{
166
166
+
"type": "CHOICE",
167
167
+
"members": [
168
168
+
{
169
169
+
"type": "SEQ",
170
170
+
"members": [
171
171
+
{
172
172
+
"type": "SYMBOL",
173
173
+
"name": "annotation_arg"
174
174
+
},
175
175
+
{
176
176
+
"type": "REPEAT",
177
177
+
"content": {
178
178
+
"type": "SEQ",
179
179
+
"members": [
180
180
+
{
181
181
+
"type": "STRING",
182
182
+
"value": ","
183
183
+
},
184
184
+
{
185
185
+
"type": "SYMBOL",
186
186
+
"name": "annotation_arg"
187
187
+
}
188
188
+
]
189
189
+
}
190
190
+
},
191
191
+
{
192
192
+
"type": "CHOICE",
193
193
+
"members": [
194
194
+
{
195
195
+
"type": "STRING",
196
196
+
"value": ","
197
197
+
},
198
198
+
{
199
199
+
"type": "BLANK"
200
200
+
}
201
201
+
]
202
202
+
}
203
203
+
]
204
204
+
},
205
205
+
{
206
206
+
"type": "BLANK"
207
207
+
}
208
208
+
]
209
209
+
},
210
210
+
{
211
211
+
"type": "STRING",
212
212
+
"value": ")"
213
213
+
}
214
214
+
]
215
215
+
},
216
216
+
"annotation_arg": {
217
217
+
"type": "CHOICE",
218
218
+
"members": [
219
219
+
{
220
220
+
"type": "SEQ",
221
221
+
"members": [
222
222
+
{
223
223
+
"type": "FIELD",
224
224
+
"name": "name",
225
225
+
"content": {
226
226
+
"type": "SYMBOL",
227
227
+
"name": "identifier"
228
228
+
}
229
229
+
},
230
230
+
{
231
231
+
"type": "STRING",
232
232
+
"value": ":"
233
233
+
},
234
234
+
{
235
235
+
"type": "FIELD",
236
236
+
"name": "value",
237
237
+
"content": {
238
238
+
"type": "SYMBOL",
239
239
+
"name": "annotation_value"
240
240
+
}
241
241
+
}
242
242
+
]
243
243
+
},
244
244
+
{
245
245
+
"type": "FIELD",
246
246
+
"name": "value",
247
247
+
"content": {
248
248
+
"type": "SYMBOL",
249
249
+
"name": "annotation_value"
250
250
+
}
251
251
+
}
252
252
+
]
253
253
+
},
254
254
+
"annotation_value": {
255
255
+
"type": "CHOICE",
256
256
+
"members": [
257
257
+
{
258
258
+
"type": "SYMBOL",
259
259
+
"name": "string"
260
260
+
},
261
261
+
{
262
262
+
"type": "SYMBOL",
263
263
+
"name": "number"
264
264
+
},
265
265
+
{
266
266
+
"type": "SYMBOL",
267
267
+
"name": "boolean"
268
268
+
}
269
269
+
]
270
270
+
},
80
271
"use_statement": {
81
272
"type": "SEQ",
82
273
"members": [
···
267
458
"type": "SEQ",
268
459
"members": [
269
460
{
461
461
+
"type": "REPEAT",
462
462
+
"content": {
463
463
+
"type": "SYMBOL",
464
464
+
"name": "annotation"
465
465
+
}
466
466
+
},
467
467
+
{
270
468
"type": "STRING",
271
469
"value": "record"
272
470
},
···
324
522
]
325
523
},
326
524
{
525
525
+
"type": "REPEAT",
526
526
+
"content": {
527
527
+
"type": "SYMBOL",
528
528
+
"name": "annotation"
529
529
+
}
530
530
+
},
531
531
+
{
327
532
"type": "FIELD",
328
533
"name": "name",
329
534
"content": {
···
365
570
"type": "SEQ",
366
571
"members": [
367
572
{
573
573
+
"type": "REPEAT",
574
574
+
"content": {
575
575
+
"type": "SYMBOL",
576
576
+
"name": "annotation"
577
577
+
}
578
578
+
},
579
579
+
{
368
580
"type": "STRING",
369
581
"value": "inline"
370
582
},
···
402
614
"type": "SEQ",
403
615
"members": [
404
616
{
617
617
+
"type": "REPEAT",
618
618
+
"content": {
619
619
+
"type": "SYMBOL",
620
620
+
"name": "annotation"
621
621
+
}
622
622
+
},
623
623
+
{
405
624
"type": "STRING",
406
625
"value": "def"
407
626
},
···
439
658
"type": "SEQ",
440
659
"members": [
441
660
{
661
661
+
"type": "REPEAT",
662
662
+
"content": {
663
663
+
"type": "SYMBOL",
664
664
+
"name": "annotation"
665
665
+
}
666
666
+
},
667
667
+
{
442
668
"type": "STRING",
443
669
"value": "token"
444
670
},
···
459
685
"query_definition": {
460
686
"type": "SEQ",
461
687
"members": [
688
688
+
{
689
689
+
"type": "REPEAT",
690
690
+
"content": {
691
691
+
"type": "SYMBOL",
692
692
+
"name": "annotation"
693
693
+
}
694
694
+
},
462
695
{
463
696
"type": "STRING",
464
697
"value": "query"
···
501
734
"type": "SEQ",
502
735
"members": [
503
736
{
737
737
+
"type": "REPEAT",
738
738
+
"content": {
739
739
+
"type": "SYMBOL",
740
740
+
"name": "annotation"
741
741
+
}
742
742
+
},
743
743
+
{
504
744
"type": "STRING",
505
745
"value": "procedure"
506
746
},
···
541
781
"subscription_definition": {
542
782
"type": "SEQ",
543
783
"members": [
784
784
+
{
785
785
+
"type": "REPEAT",
786
786
+
"content": {
787
787
+
"type": "SYMBOL",
788
788
+
"name": "annotation"
789
789
+
}
790
790
+
},
544
791
{
545
792
"type": "STRING",
546
793
"value": "subscription"
···
640
887
"parameter": {
641
888
"type": "SEQ",
642
889
"members": [
890
890
+
{
891
891
+
"type": "REPEAT",
892
892
+
"content": {
893
893
+
"type": "SYMBOL",
894
894
+
"name": "annotation"
895
895
+
}
896
896
+
},
643
897
{
644
898
"type": "FIELD",
645
899
"name": "name",
+206
-3
tree-sitter-mlf/src/node-types.json
···
1
1
[
2
2
{
3
3
+
"type": "annotation",
4
4
+
"named": true,
5
5
+
"fields": {
6
6
+
"args": {
7
7
+
"multiple": false,
8
8
+
"required": false,
9
9
+
"types": [
10
10
+
{
11
11
+
"type": "annotation_args",
12
12
+
"named": true
13
13
+
}
14
14
+
]
15
15
+
},
16
16
+
"name": {
17
17
+
"multiple": false,
18
18
+
"required": true,
19
19
+
"types": [
20
20
+
{
21
21
+
"type": "identifier",
22
22
+
"named": true
23
23
+
}
24
24
+
]
25
25
+
},
26
26
+
"selectors": {
27
27
+
"multiple": false,
28
28
+
"required": false,
29
29
+
"types": [
30
30
+
{
31
31
+
"type": "annotation_selectors",
32
32
+
"named": true
33
33
+
}
34
34
+
]
35
35
+
}
36
36
+
}
37
37
+
},
38
38
+
{
39
39
+
"type": "annotation_arg",
40
40
+
"named": true,
41
41
+
"fields": {
42
42
+
"name": {
43
43
+
"multiple": false,
44
44
+
"required": false,
45
45
+
"types": [
46
46
+
{
47
47
+
"type": "identifier",
48
48
+
"named": true
49
49
+
}
50
50
+
]
51
51
+
},
52
52
+
"value": {
53
53
+
"multiple": false,
54
54
+
"required": true,
55
55
+
"types": [
56
56
+
{
57
57
+
"type": "annotation_value",
58
58
+
"named": true
59
59
+
}
60
60
+
]
61
61
+
}
62
62
+
}
63
63
+
},
64
64
+
{
65
65
+
"type": "annotation_args",
66
66
+
"named": true,
67
67
+
"fields": {},
68
68
+
"children": {
69
69
+
"multiple": true,
70
70
+
"required": false,
71
71
+
"types": [
72
72
+
{
73
73
+
"type": "annotation_arg",
74
74
+
"named": true
75
75
+
}
76
76
+
]
77
77
+
}
78
78
+
},
79
79
+
{
80
80
+
"type": "annotation_selectors",
81
81
+
"named": true,
82
82
+
"fields": {},
83
83
+
"children": {
84
84
+
"multiple": true,
85
85
+
"required": true,
86
86
+
"types": [
87
87
+
{
88
88
+
"type": "identifier",
89
89
+
"named": true
90
90
+
}
91
91
+
]
92
92
+
}
93
93
+
},
94
94
+
{
95
95
+
"type": "annotation_value",
96
96
+
"named": true,
97
97
+
"fields": {},
98
98
+
"children": {
99
99
+
"multiple": false,
100
100
+
"required": true,
101
101
+
"types": [
102
102
+
{
103
103
+
"type": "boolean",
104
104
+
"named": true
105
105
+
},
106
106
+
{
107
107
+
"type": "number",
108
108
+
"named": true
109
109
+
},
110
110
+
{
111
111
+
"type": "string",
112
112
+
"named": true
113
113
+
}
114
114
+
]
115
115
+
}
116
116
+
},
117
117
+
{
3
118
"type": "array_literal",
4
119
"named": true,
5
120
"fields": {},
···
153
268
}
154
269
]
155
270
}
271
271
+
},
272
272
+
"children": {
273
273
+
"multiple": true,
274
274
+
"required": false,
275
275
+
"types": [
276
276
+
{
277
277
+
"type": "annotation",
278
278
+
"named": true
279
279
+
}
280
280
+
]
156
281
}
157
282
},
158
283
{
···
207
332
}
208
333
},
209
334
"children": {
210
210
-
"multiple": false,
335
335
+
"multiple": true,
211
336
"required": false,
212
337
"types": [
338
338
+
{
339
339
+
"type": "annotation",
340
340
+
"named": true
341
341
+
},
213
342
{
214
343
"type": "doc_comment",
215
344
"named": true
···
291
420
}
292
421
]
293
422
}
423
423
+
},
424
424
+
"children": {
425
425
+
"multiple": true,
426
426
+
"required": false,
427
427
+
"types": [
428
428
+
{
429
429
+
"type": "annotation",
430
430
+
"named": true
431
431
+
}
432
432
+
]
294
433
}
295
434
},
296
435
{
···
406
545
}
407
546
]
408
547
}
548
548
+
},
549
549
+
"children": {
550
550
+
"multiple": true,
551
551
+
"required": false,
552
552
+
"types": [
553
553
+
{
554
554
+
"type": "annotation",
555
555
+
"named": true
556
556
+
}
557
557
+
]
409
558
}
410
559
},
411
560
{
···
462
611
}
463
612
]
464
613
}
614
614
+
},
615
615
+
"children": {
616
616
+
"multiple": true,
617
617
+
"required": false,
618
618
+
"types": [
619
619
+
{
620
620
+
"type": "annotation",
621
621
+
"named": true
622
622
+
}
623
623
+
]
465
624
}
466
625
},
467
626
{
···
498
657
}
499
658
]
500
659
}
660
660
+
},
661
661
+
"children": {
662
662
+
"multiple": true,
663
663
+
"required": false,
664
664
+
"types": [
665
665
+
{
666
666
+
"type": "annotation",
667
667
+
"named": true
668
668
+
}
669
669
+
]
501
670
}
502
671
},
503
672
{
···
539
708
}
540
709
]
541
710
}
711
711
+
},
712
712
+
"children": {
713
713
+
"multiple": true,
714
714
+
"required": false,
715
715
+
"types": [
716
716
+
{
717
717
+
"type": "annotation",
718
718
+
"named": true
719
719
+
}
720
720
+
]
542
721
}
543
722
},
544
723
{
···
624
803
}
625
804
]
626
805
}
806
806
+
},
807
807
+
"children": {
808
808
+
"multiple": true,
809
809
+
"required": false,
810
810
+
"types": [
811
811
+
{
812
812
+
"type": "annotation",
813
813
+
"named": true
814
814
+
}
815
815
+
]
627
816
}
628
817
},
629
818
{
···
640
829
}
641
830
]
642
831
}
832
832
+
},
833
833
+
"children": {
834
834
+
"multiple": true,
835
835
+
"required": false,
836
836
+
"types": [
837
837
+
{
838
838
+
"type": "annotation",
839
839
+
"named": true
840
840
+
}
841
841
+
]
643
842
}
644
843
},
645
844
{
···
764
963
"named": false
765
964
},
766
965
{
966
966
+
"type": "@",
967
967
+
"named": false
968
968
+
},
969
969
+
{
767
970
"type": "[",
768
971
"named": false
769
972
},
···
849
1052
},
850
1053
{
851
1054
"type": "string",
852
852
-
"named": true
1055
1055
+
"named": false
853
1056
},
854
1057
{
855
1058
"type": "string",
856
856
-
"named": false
1059
1059
+
"named": true
857
1060
},
858
1061
{
859
1062
"type": "subscription",
+54
website/syntaxes/mlf.sublime-syntax
···
8
8
contexts:
9
9
main:
10
10
- include: comments
11
11
+
- include: annotations
11
12
- include: keywords
12
13
- include: types
13
14
- include: strings
···
26
27
push:
27
28
- meta_scope: comment.line.double-slash.mlf
28
29
- match: $
30
30
+
pop: true
31
31
+
32
32
+
annotations:
33
33
+
# Annotation with selectors and args: @rust,typescript:deprecated(true)
34
34
+
- match: '@([a-zA-Z_][a-zA-Z0-9_]*(?:,[a-zA-Z_][a-zA-Z0-9_]*)*):([a-zA-Z_][a-zA-Z0-9_]*)'
35
35
+
scope: meta.annotation.mlf
36
36
+
captures:
37
37
+
1: entity.name.namespace.mlf
38
38
+
2: entity.name.function.annotation.mlf
39
39
+
push:
40
40
+
- match: '\('
41
41
+
scope: punctuation.section.arguments.begin.mlf
42
42
+
set:
43
43
+
- meta_scope: meta.annotation.arguments.mlf
44
44
+
- match: '\)'
45
45
+
scope: punctuation.section.arguments.end.mlf
46
46
+
pop: true
47
47
+
- match: '([a-zA-Z_][a-zA-Z0-9_]*)\s*(:)'
48
48
+
captures:
49
49
+
1: variable.parameter.mlf
50
50
+
2: punctuation.separator.mlf
51
51
+
- include: strings
52
52
+
- include: numbers
53
53
+
- match: '\b(true|false)\b'
54
54
+
scope: constant.language.mlf
55
55
+
- match: ','
56
56
+
scope: punctuation.separator.mlf
57
57
+
- match: '(?=\S)'
58
58
+
pop: true
59
59
+
# Bare annotation with args: @deprecated(true)
60
60
+
- match: '@([a-zA-Z_][a-zA-Z0-9_]*)'
61
61
+
scope: meta.annotation.mlf
62
62
+
captures:
63
63
+
1: entity.name.function.annotation.mlf
64
64
+
push:
65
65
+
- match: '\('
66
66
+
scope: punctuation.section.arguments.begin.mlf
67
67
+
set:
68
68
+
- meta_scope: meta.annotation.arguments.mlf
69
69
+
- match: '\)'
70
70
+
scope: punctuation.section.arguments.end.mlf
71
71
+
pop: true
72
72
+
- match: '([a-zA-Z_][a-zA-Z0-9_]*)\s*(:)'
73
73
+
captures:
74
74
+
1: variable.parameter.mlf
75
75
+
2: punctuation.separator.mlf
76
76
+
- include: strings
77
77
+
- include: numbers
78
78
+
- match: '\b(true|false)\b'
79
79
+
scope: constant.language.mlf
80
80
+
- match: ','
81
81
+
scope: punctuation.separator.mlf
82
82
+
- match: '(?=\S)'
29
83
pop: true
30
84
31
85
keywords: