diff --git a/README.md b/README.md index a296122a..461d62bd 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Colorization and configuration supports for multiple languages for the Monaco Ed ![monaco-languages](https://cloud.githubusercontent.com/assets/5047891/15938606/1fd4bac6-2e74-11e6-8839-d455da8bc8a7.gif) +* apex * azcli * bat * clojure diff --git a/scripts/bundle.js b/scripts/bundle.js index 9bce8b1a..614d5c83 100644 --- a/scripts/bundle.js +++ b/scripts/bundle.js @@ -69,6 +69,7 @@ bundleOne('shell/shell'); bundleOne('perl/perl'), bundleOne('powerquery/powerquery') bundleOne('azcli/azcli') +bundleOne('apex/apex'); function bundleOne(moduleId, exclude) { requirejs.optimize({ diff --git a/src/apex/apex.contribution.ts b/src/apex/apex.contribution.ts new file mode 100644 index 00000000..d7a38595 --- /dev/null +++ b/src/apex/apex.contribution.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { registerLanguage } from '../_.contribution'; + +// Allow for running under nodejs/requirejs in tests +const _monaco: typeof monaco = (typeof monaco === 'undefined' ? (self).monaco : monaco); + +registerLanguage({ + id: 'apex', + extensions: ['.cls'], + aliases: ['Apex', 'apex'], + mimetypes: ['text/x-apex-source', 'text/x-apex'], + loader: () => _monaco.Promise.wrap(import('./apex')) +}); diff --git a/src/apex/apex.test.ts b/src/apex/apex.test.ts new file mode 100644 index 00000000..5f9f6c8f --- /dev/null +++ b/src/apex/apex.test.ts @@ -0,0 +1,651 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { testTokenization } from '../test/testRunner'; + +testTokenization('apex', [ + // Comments - single line + [{ + line: '//', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }], + + [{ + line: ' // a comment', + tokens: [ + { startIndex: 0, type: '' }, + { startIndex: 4, type: 'comment.apex' } + ] + }], + + // Broken nested tokens due to invalid comment tokenization + [{ + line: '/* //*/ a', + tokens: [ + { startIndex: 0, type: 'comment.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'identifier.apex' } + ] + }], + + [{ + line: '// a comment', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }], + + [{ + line: '//sticky comment', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }], + + [{ + line: '/almost a comment', + tokens: [ + { startIndex: 0, type: 'delimiter.apex' }, + { startIndex: 1, type: 'identifier.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'identifier.apex' }, + { startIndex: 9, type: '' }, + { startIndex: 10, type: 'identifier.apex' } + ] + }], + + [{ + line: '1 / 2; /* comment', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: '' }, + { startIndex: 2, type: 'delimiter.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'number.apex' }, + { startIndex: 5, type: 'delimiter.apex' }, + { startIndex: 6, type: '' }, + { startIndex: 7, type: 'comment.apex' } + ] + }], + + [{ + line: 'int x = 1; // my comment // is a nice one', + tokens: [ + { startIndex: 0, type: 'keyword.int.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.apex' }, + { startIndex: 5, type: '' }, + { startIndex: 6, type: 'delimiter.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'number.apex' }, + { startIndex: 9, type: 'delimiter.apex' }, + { startIndex: 10, type: '' }, + { startIndex: 11, type: 'comment.apex' } + ] + }], + + // Comments - range comment, single line + [{ + line: '/* a simple comment */', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }], + + [{ + line: 'int x = /* a simple comment */ 1;', + tokens: [ + { startIndex: 0, type: 'keyword.int.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.apex' }, + { startIndex: 5, type: '' }, + { startIndex: 6, type: 'delimiter.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'comment.apex' }, + { startIndex: 30, type: '' }, + { startIndex: 31, type: 'number.apex' }, + { startIndex: 32, type: 'delimiter.apex' } + ] + }], + + [{ + line: 'int x = /* comment */ 1; */', + tokens: [ + { startIndex: 0, type: 'keyword.int.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.apex' }, + { startIndex: 5, type: '' }, + { startIndex: 6, type: 'delimiter.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'comment.apex' }, + { startIndex: 21, type: '' }, + { startIndex: 22, type: 'number.apex' }, + { startIndex: 23, type: 'delimiter.apex' }, + { startIndex: 24, type: '' } + ] + }], + + [{ + line: 'x = /**/;', + tokens: [ + { startIndex: 0, type: 'identifier.apex' }, + { startIndex: 1, type: '' }, + { startIndex: 2, type: 'delimiter.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'comment.apex' }, + { startIndex: 8, type: 'delimiter.apex' } + ] + }], + + [{ + line: 'x = /*/;', + tokens: [ + { startIndex: 0, type: 'identifier.apex' }, + { startIndex: 1, type: '' }, + { startIndex: 2, type: 'delimiter.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'comment.apex' } + ] + }], + + // Comments - range comment, multiple lines + [{ + line: '/* start of multiline comment', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }, { + line: 'a comment between without a star', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }, { + line: 'end of multiline comment*/', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }], + + [{ + line: 'int x = /* start a comment', + tokens: [ + { startIndex: 0, type: 'keyword.int.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.apex' }, + { startIndex: 5, type: '' }, + { startIndex: 6, type: 'delimiter.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'comment.apex' } + ] + }, { + line: ' a ', + tokens: [ + { startIndex: 0, type: 'comment.apex' } + ] + }, { + line: 'and end it */ 2;', + tokens: [ + { startIndex: 0, type: 'comment.apex' }, + { startIndex: 13, type: '' }, + { startIndex: 14, type: 'number.apex' }, + { startIndex: 15, type: 'delimiter.apex' } + ] + }], + + // Comments - apex doc, multiple lines + [{ + line: '/** start of Apex Doc', + tokens: [ + { startIndex: 0, type: 'comment.doc.apex' } + ] + }, { + line: 'a comment between without a star', + tokens: [ + { startIndex: 0, type: 'comment.doc.apex' } + ] + }, { + line: 'end of multiline comment*/', + tokens: [ + { startIndex: 0, type: 'comment.doc.apex' } + ] + }], + + // Keywords + [{ + line: 'package test; class Program { static void main(String[] args) {} } }', + tokens: [ + { startIndex: 0, type: 'keyword.package.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'identifier.apex' }, + { startIndex: 12, type: 'delimiter.apex' }, + { startIndex: 13, type: '' }, + { startIndex: 14, type: 'keyword.class.apex' }, + { startIndex: 19, type: '' }, + { startIndex: 20, type: 'type.identifier.apex' }, + { startIndex: 27, type: '' }, + { startIndex: 28, type: 'delimiter.curly.apex' }, + { startIndex: 29, type: '' }, + { startIndex: 30, type: 'keyword.static.apex' }, + { startIndex: 36, type: '' }, + { startIndex: 37, type: 'keyword.void.apex' }, + { startIndex: 41, type: '' }, + { startIndex: 42, type: 'identifier.apex' }, + { startIndex: 46, type: 'delimiter.parenthesis.apex' }, + { startIndex: 47, type: 'type.identifier.apex' }, + { startIndex: 53, type: 'delimiter.square.apex' }, + { startIndex: 55, type: '' }, + { startIndex: 56, type: 'identifier.apex' }, + { startIndex: 60, type: 'delimiter.parenthesis.apex' }, + { startIndex: 61, type: '' }, + { startIndex: 62, type: 'delimiter.curly.apex' }, + { startIndex: 64, type: '' }, + { startIndex: 65, type: 'delimiter.curly.apex' }, + { startIndex: 66, type: '' }, + { startIndex: 67, type: 'delimiter.curly.apex' } + ] + }], + + // Keywords with case variations + [{ + line: 'Package test; CLASS Program { Static void main(String[] args) {} } }', + tokens: [ + { startIndex: 0, type: 'keyword.Package.apex' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'identifier.apex' }, + { startIndex: 12, type: 'delimiter.apex' }, + { startIndex: 13, type: '' }, + { startIndex: 14, type: 'keyword.CLASS.apex' }, + { startIndex: 19, type: '' }, + { startIndex: 20, type: 'type.identifier.apex' }, + { startIndex: 27, type: '' }, + { startIndex: 28, type: 'delimiter.curly.apex' }, + { startIndex: 29, type: '' }, + { startIndex: 30, type: 'keyword.Static.apex' }, + { startIndex: 36, type: '' }, + { startIndex: 37, type: 'keyword.void.apex' }, + { startIndex: 41, type: '' }, + { startIndex: 42, type: 'identifier.apex' }, + { startIndex: 46, type: 'delimiter.parenthesis.apex' }, + { startIndex: 47, type: 'type.identifier.apex' }, + { startIndex: 53, type: 'delimiter.square.apex' }, + { startIndex: 55, type: '' }, + { startIndex: 56, type: 'identifier.apex' }, + { startIndex: 60, type: 'delimiter.parenthesis.apex' }, + { startIndex: 61, type: '' }, + { startIndex: 62, type: 'delimiter.curly.apex' }, + { startIndex: 64, type: '' }, + { startIndex: 65, type: 'delimiter.curly.apex' }, + { startIndex: 66, type: '' }, + { startIndex: 67, type: 'delimiter.curly.apex' } + ] + }], + + // Numbers + [{ + line: '0', + tokens: [ + { startIndex: 0, type: 'number.apex' } + ] + }], + + [{ + line: '0.10', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '0x', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: 'identifier.apex' } + ] + }], + + [{ + line: '10e3', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '10f', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5e3', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5e-3', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5E3', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5E-3', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5F', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5f', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5D', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23.5d', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '1.72E3D', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '1.72E3d', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '1.72E-3d', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '1.72e3D', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '1.72e3d', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '1.72e-3d', + tokens: [ + { startIndex: 0, type: 'number.float.apex' } + ] + }], + + [{ + line: '23L', + tokens: [ + { startIndex: 0, type: 'number.apex' } + ] + }], + + [{ + line: '23l', + tokens: [ + { startIndex: 0, type: 'number.apex' } + ] + }], + + [{ + line: '0_52', + tokens: [ + { startIndex: 0, type: 'number.apex' } + ] + }], + + [{ + line: '5_2', + tokens: [ + { startIndex: 0, type: 'number.apex' } + ] + }], + + [{ + line: '5_______2', + tokens: [ + { startIndex: 0, type: 'number.apex' } + ] + }], + + [{ + line: '3_.1415F', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: 'identifier.apex' }, + { startIndex: 2, type: 'delimiter.apex' }, + { startIndex: 3, type: 'number.float.apex' } + ] + }], + + [{ + line: '3._1415F', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: 'delimiter.apex' }, + { startIndex: 2, type: 'identifier.apex' } + ] + }], + + [{ + line: '999_99_9999_L', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 11, type: 'identifier.apex' } + ] + }], + + [{ + line: '52_', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 2, type: 'identifier.apex' } + ] + }], + + [{ + line: '0_x52', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: 'identifier.apex' } + ] + }], + + [{ + line: '0x_52', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: 'identifier.apex' } + ] + }], + + [{ + line: '23.5L', + tokens: [ + { startIndex: 0, type: 'number.float.apex' }, + { startIndex: 4, type: 'type.identifier.apex' } + ] + }], + + [{ + line: '0+0', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: 'delimiter.apex' }, + { startIndex: 2, type: 'number.apex' } + ] + }], + + [{ + line: '100+10', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 3, type: 'delimiter.apex' }, + { startIndex: 4, type: 'number.apex' } + ] + }], + + [{ + line: '0 + 0', + tokens: [ + { startIndex: 0, type: 'number.apex' }, + { startIndex: 1, type: '' }, + { startIndex: 2, type: 'delimiter.apex' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'number.apex' } + ] + }], + + // single line Strings + [{ + line: 'String s = "I\'m an Apex String";', + tokens: [ + { startIndex: 0, type: 'type.identifier.apex' }, + { startIndex: 6, type: '' }, + { startIndex: 7, type: 'identifier.apex' }, + { startIndex: 8, type: '' }, + { startIndex: 9, type: 'delimiter.apex' }, + { startIndex: 10, type: '' }, + { startIndex: 11, type: 'string.apex' }, + { startIndex: 31, type: 'delimiter.apex' } + ] + }], + + [{ + line: 'String s = "concatenated" + " String" ;', + tokens: [ + { startIndex: 0, type: 'type.identifier.apex' }, + { startIndex: 6, type: '' }, + { startIndex: 7, type: 'identifier.apex' }, + { startIndex: 8, type: '' }, + { startIndex: 9, type: 'delimiter.apex' }, + { startIndex: 10, type: '' }, + { startIndex: 11, type: 'string.apex' }, + { startIndex: 25, type: '' }, + { startIndex: 26, type: 'delimiter.apex' }, + { startIndex: 27, type: '' }, + { startIndex: 28, type: 'string.apex' }, + { startIndex: 37, type: '' }, + { startIndex: 38, type: 'delimiter.apex' } + ] + }], + + [{ + line: '"quote in a string"', + tokens: [ + { startIndex: 0, type: 'string.apex' } + ] + }], + + [{ + line: '"escaping \\"quotes\\" is cool"', + tokens: [ + { startIndex: 0, type: 'string.apex' }, + { startIndex: 10, type: 'string.escape.apex' }, + { startIndex: 12, type: 'string.apex' }, + { startIndex: 18, type: 'string.escape.apex' }, + { startIndex: 20, type: 'string.apex' } + ] + }], + + [{ + line: '"\\"', + tokens: [ + { startIndex: 0, type: 'string.invalid.apex' } + ] + }], + + // Annotations + [{ + line: '@', + tokens: [ + { startIndex: 0, type: '' } + ] + }], + + [{ + line: '@Override', + tokens: [ + { startIndex: 0, type: 'annotation.apex' } + ] + }], + + [{ + line: '@SuppressWarnings(value = "aString")', + tokens: [ + { startIndex: 0, type: 'annotation.apex' }, + { startIndex: 17, type: 'delimiter.parenthesis.apex' }, + { startIndex: 18, type: 'identifier.apex' }, + { startIndex: 23, type: '' }, + { startIndex: 24, type: 'delimiter.apex' }, + { startIndex: 25, type: '' }, + { startIndex: 26, type: 'string.apex' }, + { startIndex: 35, type: 'delimiter.parenthesis.apex' } + ] + }], + + [{ + line: '@ AnnotationWithKeywordAfter private', + tokens: [ + { startIndex: 0, type: 'annotation.apex' }, + { startIndex: 28, type: '' }, + { startIndex: 29, type: 'keyword.private.apex' } + ] + }] +]); + diff --git a/src/apex/apex.ts b/src/apex/apex.ts new file mode 100644 index 00000000..c02eddd9 --- /dev/null +++ b/src/apex/apex.ts @@ -0,0 +1,310 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import IRichLanguageConfiguration = monaco.languages.LanguageConfiguration; +import ILanguage = monaco.languages.IMonarchLanguage; + +export const conf: IRichLanguageConfiguration = { + // the default separators except `@$` + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, + comments: { + lineComment: '//', + blockComment: ['/*', '*/'], + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: '\'', close: '\'' }, + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: '\'', close: '\'' }, + { open: '<', close: '>' }, + ], + folding: { + markers: { + start: new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:))") + } + } +}; + +const keywords = [ + 'abstract', + 'activate', + 'and', + 'any', + 'array', + 'as', + 'asc', + 'assert', + 'autonomous', + 'begin', + 'bigdecimal', + 'blob', + 'boolean', + 'break', + 'bulk', + 'by', + 'case', + 'cast', + 'catch', + 'char', + 'class', + 'collect', + 'commit', + 'const', + 'continue', + 'convertcurrency', + 'decimal', + 'default', + 'delete', + 'desc', + 'do', + 'double', + 'else', + 'end', + 'enum', + 'exception', + 'exit', + 'export', + 'extends', + 'false', + 'final', + 'finally', + 'float', + 'for', + 'from', + 'future', + 'get', + 'global', + 'goto', + 'group', + 'having', + 'hint', + 'if', + 'implements', + 'import', + 'in', + 'inner', + 'insert', + 'instanceof', + 'int', + 'interface', + 'into', + 'join', + 'last_90_days', + 'last_month', + 'last_n_days', + 'last_week', + 'like', + 'limit', + 'list', + 'long', + 'loop', + 'map', + 'merge', + 'native', + 'new', + 'next_90_days', + 'next_month', + 'next_n_days', + 'next_week', + 'not', + 'null', + 'nulls', + 'number', + 'object', + 'of', + 'on', + 'or', + 'outer', + 'override', + 'package', + 'parallel', + 'pragma', + 'private', + 'protected', + 'public', + 'retrieve', + 'return', + 'returning', + 'rollback', + 'savepoint', + 'search', + 'select', + 'set', + 'short', + 'sort', + 'stat', + 'static', + 'strictfp', + 'super', + 'switch', + 'synchronized', + 'system', + 'testmethod', + 'then', + 'this', + 'this_month', + 'this_week', + 'throw', + 'throws', + 'today', + 'tolabel', + 'tomorrow', + 'transaction', + 'transient', + 'trigger', + 'true', + 'try', + 'type', + 'undelete', + 'update', + 'upsert', + 'using', + 'virtual', + 'void', + 'volatile', + 'webservice', + 'when', + 'where', + 'while', + 'yesterday' +]; + +// create case variations of the keywords - apex is case insensitive, but we can't make the highlighter case insensitive +// because we use a heuristic to assume that identifiers starting with an upper case letter are types. +const uppercaseFirstLetter = (lowercase) => lowercase.charAt(0).toUpperCase() + lowercase.substr(1); + +let keywordsWithCaseVariations = []; +keywords.forEach(lowercase => { + keywordsWithCaseVariations.push(lowercase); + keywordsWithCaseVariations.push(lowercase.toUpperCase()); + keywordsWithCaseVariations.push(uppercaseFirstLetter(lowercase)); +}) + +export const language = { + defaultToken: '', + tokenPostfix: '.apex', + + keywords: keywordsWithCaseVariations, + + operators: [ + '=', '>', '<', '!', '~', '?', ':', + '==', '<=', '>=', '!=', '&&', '||', '++', '--', + '+', '-', '*', '/', '&', '|', '^', '%', '<<', + '>>', '>>>', '+=', '-=', '*=', '/=', '&=', '|=', + '^=', '%=', '<<=', '>>=', '>>>=' + ], + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, '@brackets'], + [/@symbols/, { + cases: { + '@operators': 'delimiter', + '@default': '' + } + }], + + // @ annotations. + [/@\s*[a-zA-Z_\$][\w\$]*/, 'annotation'], + + // numbers + [/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/, 'number.float'], + [/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/, 'number.float'], + [/(@digits)[fFdD]/, 'number.float'], + [/(@digits)[lL]?/, 'number'], + + // delimiter: after number because of .\d floats + [/[;,.]/, 'delimiter'], + + // strings + [/"([^"\\]|\\.)*$/, 'string.invalid' ], // non-teminated string + [/'([^'\\]|\\.)*$/, 'string.invalid' ], // non-teminated string + [/"/, 'string', '@string."' ], + [/'/, 'string', '@string.\'' ], + + + // characters + [/'[^\\']'/, 'string'], + [/(')(@escapes)(')/, ['string', 'string.escape', 'string']], + [/'/, 'string.invalid'] + ], + + whitespace: [ + [/[ \t\r\n]+/, ''], + [/\/\*\*(?!\/)/, 'comment.doc', '@apexdoc'], + [/\/\*/, 'comment', '@comment'], + [/\/\/.*$/, 'comment'], + ], + + comment: [ + [/[^\/*]+/, 'comment'], + // [/\/\*/, 'comment', '@push' ], // nested comment not allowed :-( + // [/\/\*/, 'comment.invalid' ], // this breaks block comments in the shape of /* //*/ + [/\*\//, 'comment', '@pop'], + [/[\/*]/, 'comment'] + ], + + //Identical copy of comment above, except for the addition of .doc + apexdoc: [ + [/[^\/*]+/, 'comment.doc'], + [/\*\//, 'comment.doc', '@pop'], + [/[\/*]/, 'comment.doc'] + ], + + string: [ + [/[^\\"']+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/["']/, { cases: { '$#==$S2' : { token: 'string', next: '@pop' }, + '@default': 'string' }} ] + ], + }, +}; diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 6f96d356..ae9aad59 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -52,3 +52,4 @@ import './clojure/clojure.contribution'; import './shell/shell.contribution'; import './perl/perl.contribution'; import './azcli/azcli.contribution'; +import './apex/apex.contribution'; diff --git a/test/setup.js b/test/setup.js index 03ca7803..dca4ff44 100644 --- a/test/setup.js +++ b/test/setup.js @@ -72,7 +72,8 @@ define(['require'], function (require) { 'release/dev/clojure/clojure.test', 'release/dev/shell/shell.test', 'release/dev/perl/perl.test', - 'release/dev/azcli/azcli.test' + 'release/dev/azcli/azcli.test', + 'release/dev/apex/apex.test' ], function () { run(); // We can launch the tests! }, function (err) {