From f4d3d312dbdd9250c4c2ce467d3d30a251530a92 Mon Sep 17 00:00:00 2001 From: d056950 Date: Fri, 30 Sep 2016 17:34:18 +0200 Subject: [PATCH] Add YAML --- gulpfile.js | 3 +- src/monaco.contribution.ts | 7 ++ src/yaml.ts | 213 +++++++++++++++++++++++++++++++++++++ test/all.js | 1 + test/yaml.test.ts | 129 ++++++++++++++++++++++ tsconfig.json | 2 + 6 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 src/yaml.ts create mode 100644 test/yaml.test.ts diff --git a/gulpfile.js b/gulpfile.js index d24f74c0..81220b7c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -73,7 +73,8 @@ gulp.task('release', ['clean-release','compile'], function() { bundleOne('src/sql'), bundleOne('src/swift'), bundleOne('src/vb'), - bundleOne('src/xml') + bundleOne('src/xml'), + bundleOne('src/yaml') ) .pipe(uglify({ preserveComments: 'some' diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 539d79b7..318a204f 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -247,3 +247,10 @@ registerLanguage({ mimetypes: ['text/css'], module: './css' }); +registerLanguage({ + id: 'yaml', + extensions: ['.yaml', '.yml'], + aliases: ['YAML', 'yaml', 'YML', 'yml'], + mimetypes: ['application/x-yaml'], + module: './yaml' +}); diff --git a/src/yaml.ts b/src/yaml.ts new file mode 100644 index 00000000..50dcf6e0 --- /dev/null +++ b/src/yaml.ts @@ -0,0 +1,213 @@ +import IRichLanguageConfiguration = monaco.languages.LanguageConfiguration; +import ILanguage = monaco.languages.IMonarchLanguage; + +export const conf: IRichLanguageConfiguration = { + comments: { + lineComment: '#' + }, + brackets: [['{', '}'], ['[', ']']], + autoClosingPairs: [ + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: '\'', close: '\'', notIn: ['string', 'comment'] }, + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '[', close: ']', notIn: ['string', 'comment'] } + ] +}; + +export const language = { + defaultToken: '', + ignoreCase: true, + tokenPostfix: '.yaml', + + brackets: [ + { token: 'delimiter.bracket', open: '{', close: '}' }, + { token: 'delimiter.square', open: '[', close: ']' } + ], + + keywords: ['true', 'false', 'null', '~'], + + // we include these common regular expressions + escapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/, + + // The main tokenizer for our languages + tokenizer: { + root: [ + {include: '@whitespace'}, + {include: '@comment'}, + + // Directive + [/%[^ ]+.*$/, 'meta.directive'], + + // Document Markers + [/---/, 'operators.directivesEnd'], + [/\.{3}/, 'operators.documentEnd'], + + // Block Structure Indicators + [/[-?:](?= )/, 'operators'], + + {include: '@tagHandle'}, + {include: '@flowCollections'}, + {include: '@blockStyle'}, + + // Key of a Key:Value pair + [/(?:".*?"|'.*?'|.*?)(?=\s*: \S+)/, 'type', '@value'], + [/(".*?"|'.*?'|.*?)(\s*)(:)(\s*)/, ['type', 'white', 'operators', 'white']], + + // string nodes + [/.+$/, 'string'] + ], + + // Value of a Key:Value pair + value: [ + {include: '@whitespace'}, + {include: '@comment'}, + + // Key:Value separator + [/:(?= )/, 'operators'], + + {include: '@flowCollections'}, + {include: '@flowScalars'}, + {include: '@blockStyle'}, + + [/[&*][^\[\]\{\}, ]+\s*$/, 'namespace', '@pop'], // Anchor copy to ensure it leaves the state with line-break + {include: '@anchor'}, + {include: '@tagHandle'}, + + // Numbers, cannot reuse as these terminate with the end of the line and pop the state + [/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?$/, 'number.date', '@pop'], + [/\d*\.\d+([eE][\-+]?\d+)?$/, 'number.float', '@pop'], + [/0[xX][0-9a-fA-F]+[lL]?$/, 'number.hex', '@pop'], + [/0[bB][0-1]+[lL]?$/, 'number.binary', '@pop'], + [/(0[oO][0-7]+|0[0-7]+)[lL]?$/, 'number.octal', '@pop'], + [/-?.(inf|NaN)$/, 'number.other', '@pop'], + [/[+-]?(0|[1-9]\d*)[lL]?$/, 'number', '@pop'], + + // Other value (keyword or string) + [/.+/, {cases: {'@keywords': { token: 'keyword', next: '@pop' }, + '@default': { token: 'string', next: '@pop' }}}] + ], + + // Flow Collection: Flow Mapping + object: [ + {include: '@whitespace'}, + {include: '@comment'}, + + // Flow Mapping termination + [/\}/, '@brackets', '@pop'], + + // Flow Mapping delimiter + [/,/, 'delimiter.comma'], + + // Flow Mapping Key:Value delimiter + [/:(?= )/, 'operators'], + + // Flow Mapping Key:Value key + [/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/, 'type'], + + // Start Flow Style + {include: '@flowCollections'}, + {include: '@flowScalars'}, + + // Scalar Data types + {include: '@tagHandle'}, + {include: '@anchor'}, + {include: '@number'}, + + // Other value (keyword or string) + [/[^\},]+/, {cases: {'@keywords': 'keyword', + '@default': 'string'}}] + ], + + // Flow Collection: Flow Sequence + array: [ + {include: '@whitespace'}, + {include: '@comment'}, + + // Flow Sequence termination + [/\]/, '@brackets', '@pop'], + + // Flow Sequence delimiter + [/,/, 'delimiter.comma'], + + // Start Flow Style + {include: '@flowCollections'}, + {include: '@flowScalars'}, + + // Scalar Data types + {include: '@tagHandle'}, + {include: '@anchor'}, + {include: '@number'}, + + // Other value (keyword or string) + [/[^\],]+/, {cases: {'@keywords': 'keyword', + '@default': 'string'}}] + ], + + // Flow Scalars (quoted strings) + string: [ + [/[^\\"']+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/["']/, { cases: { '$#==$S2' : { token: 'string', next: '@pop' }, + '@default': 'string' }} ] + ], + + // First line of a Block Style + multiString: [ + [/^( +).+$/, 'string', '@multiStringContinued.$1'] + ], + + // Further lines of a Block Style + // Workaround for indentation detection + multiStringContinued: [ + [/^( *).+$/, {cases: {'$1==$S2': 'string', + '@default': {token: '@rematch', next: '@popall'}}}] + ], + + whitespace: [ + [/[ \t\r\n]+/, 'white'] + ], + + // Only line comments + comment: [ + [/#.*$/, 'comment'] + ], + + // Start Flow Collections + flowCollections: [ + [/\[/, '@brackets', '@array'], + [/\{/, '@brackets', '@object'] + ], + + // Start Flow Scalars (quoted strings) + flowScalars: [ + [/"/, 'string', '@string."' ], + [/'/, 'string', '@string.\'' ] + ], + + // Start Block Scalar + blockStyle: [ + [/[>|][0-9]*[+-]?$/, 'operators', '@multiString'] + ], + + number: [ + // Date format (does not test validity) + [/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/, 'number.date'], + + [/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float'], + [/0[xX][0-9a-fA-F]+[lL]?/, 'number.hex'], + [/0[bB][0-1]+[lL]?/, 'number.binary'], + [/(0[oO][0-7]+|0[0-7]+)[lL]?/, 'number.octal'], + [/-?.(inf|NaN)/, 'number.other'], + [/[+-]?(0|[1-9]\d*)[lL]?/, 'number'] + ], + + tagHandle: [ + [/\![^ ]*/, 'tag'] + ], + + anchor: [ + [/[&*][^ ]+/, 'namespace'] + ] + } +}; diff --git a/test/all.js b/test/all.js index f53caea0..264734ed 100644 --- a/test/all.js +++ b/test/all.js @@ -48,6 +48,7 @@ requirejs([ 'out/test/sql.test', 'out/test/vb.test', 'out/test/xml.test', + 'out/test/yaml.test' ], function() { run(); // We can launch the tests! }); diff --git a/test/yaml.test.ts b/test/yaml.test.ts new file mode 100644 index 00000000..915c2250 --- /dev/null +++ b/test/yaml.test.ts @@ -0,0 +1,129 @@ +import {testTokenization} from './testRunner'; + +testTokenization('yaml', [ + // YAML directive + [{ + line: '%YAML 1.2', + tokens: [{ + startIndex: 0, + type: 'meta.directive.yaml' + }] + }], + + // Comments + [{ + line: '#Comment', + tokens: [{ + startIndex: 0, + type: 'comment.yaml' + }] + }], + + // Document Marker - Directives End + [{ + line: '---', + tokens: [{ + startIndex: 0, + type: 'operators.directivesEnd.yaml' + }] + }], + + // Document Marker - Document End + [{ + line: '...', + tokens: [{ + startIndex: 0, + type: 'operators.documentEnd.yaml' + }] + }], + + // Tag Handle + [{ + line: '!', + tokens: [{ + startIndex: 0, + type: 'tag.yaml' + }] + }], + + // Key:Value + [{ + line: 'key: value', + tokens: [{ + startIndex: 0, + type: 'type.yaml' + }, { + startIndex: 3, + type: 'operators.yaml' + }, { + startIndex: 4, + type: 'white.yaml' + }, { + startIndex: 5, + type: 'string.yaml' + }] + }], + + // Key:Value - Quoted Keys + [{ + line: '":": value', + tokens: [{ + startIndex: 0, + type: 'type.yaml' + }, { + startIndex: 3, + type: 'operators.yaml' + }, { + startIndex: 4, + type: 'white.yaml' + }, { + startIndex: 5, + type: 'string.yaml' + }] + }], + + // Flow Sequence - Data types + [{ + line: '[string,"double",\'single\',1,1.1,2002-04-28]', + tokens: [{ + startIndex: 0, + type: 'delimiter.square.yaml' + }, { + startIndex: 1, + type: 'string.yaml' + }, { + startIndex: 7, + type: 'delimiter.comma.yaml' + }, { + startIndex: 8, + type: 'string.yaml' + }, { + startIndex: 16, + type: 'delimiter.comma.yaml' + }, { + startIndex: 17, + type: 'string.yaml' + }, { + startIndex: 25, + type: 'delimiter.comma.yaml' + }, { + startIndex: 26, + type: 'number.yaml' + }, { + startIndex: 27, + type: 'delimiter.comma.yaml' + }, { + startIndex: 28, + type: 'number.float.yaml' + }, { + startIndex: 31, + type: 'delimiter.comma.yaml' + }, { + startIndex: 32, + type: 'number.date.yaml' + }, { + startIndex: 42, + type: 'delimiter.square.yaml' + }] + }] +]); diff --git a/tsconfig.json b/tsconfig.json index c3dd46ec..d3eaa29e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -40,6 +40,7 @@ "src/swift.ts", "src/vb.ts", "src/xml.ts", + "test/yaml.ts", "test/assert.d.ts", "test/bat.test.ts", "test/coffee.test.ts", @@ -68,6 +69,7 @@ "test/testRunner.ts", "test/vb.test.ts", "test/xml.test.ts", + "test/yaml.test.ts", "node_modules/monaco-editor-core/monaco.d.ts" ] }