A human-friendly DSL for ATProto Lexicons
27
fork

Configure Feed

Select the types of activity you want to include in your feed.

Syntax highlighting and autocomplete for annotations

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