From 0821a3dd33ff7fece01a42fec45307781e705f76 Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 10:38:33 +0100 Subject: [PATCH 01/12] Add Apex highlighter based on Java highlighter --- src/apex/apex.contribution.ts | 18 + src/apex/apex.test.ts | 663 ++++++++++++++++++++++++++++++++++ src/apex/apex.ts | 151 ++++++++ 3 files changed, 832 insertions(+) create mode 100644 src/apex/apex.contribution.ts create mode 100644 src/apex/apex.test.ts create mode 100644 src/apex/apex.ts 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..37ba8145 --- /dev/null +++ b/src/apex/apex.test.ts @@ -0,0 +1,663 @@ +/*--------------------------------------------------------------------------------------------- + * 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' } + ] + }], + + // 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: '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: '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: '0x123', + tokens: [ + { startIndex: 0, type: 'number.hex.apex' } + ] + }], + + [{ + line: '0x5_2', + tokens: [ + { startIndex: 0, type: 'number.hex.apex' } + ] + }], + + [{ + line: '023L', + tokens: [ + { startIndex: 0, type: 'number.octal.apex' } + ] + }], + + [{ + line: '0123l', + tokens: [ + { startIndex: 0, type: 'number.octal.apex' } + ] + }], + + [{ + line: '05_2', + tokens: [ + { startIndex: 0, type: 'number.octal.apex' } + ] + }], + + [{ + line: '0b1010_0101', + tokens: [ + { startIndex: 0, type: 'number.binary.apex' } + ] + }], + + [{ + line: '0B001', + tokens: [ + { startIndex: 0, type: 'number.binary.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: '0x52_', + tokens: [ + { startIndex: 0, type: 'number.hex.apex' }, + { startIndex: 4, type: 'identifier.apex' } + ] + }], + + [{ + line: '052_', + tokens: [ + { startIndex: 0, type: 'number.octal.apex' }, + { startIndex: 3, type: 'identifier.apex' } + ] + }], + + [{ + line: '23.5L', + tokens: [ + { startIndex: 0, type: 'number.float.apex' }, + { startIndex: 4, 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: '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: '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..d4b6dc37 --- /dev/null +++ b/src/apex/apex.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * 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)|(?:))") + } + } +}; + +export const language = { + defaultToken: '', + tokenPostfix: '.apex', + + keywords: [ + 'abstract', 'continue', 'for', 'new', 'switch', 'assert', 'default', + 'goto', 'package', 'synchronized', 'boolean', 'do', 'if', 'private', + 'this', 'break', 'double', 'implements', 'protected', 'throw', 'byte', + 'else', 'import', 'public', 'throws', 'case', 'enum', 'instanceof', 'return', + 'transient', 'catch', 'extends', 'int', 'short', 'try', 'char', 'final', + 'interface', 'static', 'void', 'class', 'finally', 'long', 'strictfp', + 'volatile', 'const', 'float', 'native', 'super', 'while', 'true', 'false', + 'virtual', 'insert' + ], + + 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'], + [/0[xX](@hexdigits)[Ll]?/, 'number.hex'], + [/0(@octaldigits)[Ll]?/, 'number.octal'], + [/0[bB](@binarydigits)[Ll]?/, 'number.binary'], + [/(@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', '@comment'], + [/\/\/.*$/, 'comment'], + ], + + comment: [ + [/[^\/*]+/, 'comment'], + // [/\/\*/, 'comment', '@push' ], // nested comment not allowed :-( + // [/\/\*/, 'comment.invalid' ], // this breaks block comments in the shape of /* //*/ + [/\*\//, 'comment', '@pop'], + [/[\/*]/, 'comment'] + ], + + string: [ + [/[^\\"']+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/["']/, { cases: { '$#==$S2' : { token: 'string', next: '@pop' }, + '@default': 'string' }} ] + ], + }, +}; From 62d143821a1227cd919ac54f9d4335f145b2b7cf Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 10:39:13 +0100 Subject: [PATCH 02/12] Register apex language and load tests --- src/monaco.contribution.ts | 1 + test/setup.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) 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) { From cd8c782c0ef62736d98f34ae6c7b1d0f76afcf0a Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 10:41:36 +0100 Subject: [PATCH 03/12] Bundle apex --- scripts/bundle.js | 1 + 1 file changed, 1 insertion(+) 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({ From ca8e66588af7aa6bf942fec89cbce3db60503cbf Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 10:54:25 +0100 Subject: [PATCH 04/12] Add apex to readme list --- README.md | 1 + 1 file changed, 1 insertion(+) 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 From 5557352259d3b089d4c9d7ae2f93dcbb94133648 Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 11:28:44 +0100 Subject: [PATCH 05/12] Merge keyword list with keyword list from SFDC documentation --- src/apex/apex.ts | 149 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 8 deletions(-) diff --git a/src/apex/apex.ts b/src/apex/apex.ts index d4b6dc37..7848895b 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -48,14 +48,147 @@ export const language = { tokenPostfix: '.apex', keywords: [ - 'abstract', 'continue', 'for', 'new', 'switch', 'assert', 'default', - 'goto', 'package', 'synchronized', 'boolean', 'do', 'if', 'private', - 'this', 'break', 'double', 'implements', 'protected', 'throw', 'byte', - 'else', 'import', 'public', 'throws', 'case', 'enum', 'instanceof', 'return', - 'transient', 'catch', 'extends', 'int', 'short', 'try', 'char', 'final', - 'interface', 'static', 'void', 'class', 'finally', 'long', 'strictfp', - 'volatile', 'const', 'float', 'native', 'super', 'while', 'true', 'false', - 'virtual', 'insert' + 'abstract', + 'activate', + 'and', + 'any', + 'array', + 'as', + 'asc', + 'assert', + 'autonomous', + 'begin', + 'bigdecimal', + 'blob', + 'boolean', + 'break', + 'bulk', + 'by', + 'byte', + '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', + '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' ], operators: [ From 7140444de90d044a4dd9233353e82180a353b244 Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 11:57:03 +0100 Subject: [PATCH 06/12] Assume identifiers starting with an uppercase letter are types --- src/apex/apex.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/apex/apex.ts b/src/apex/apex.ts index 7848895b..258172f7 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -211,13 +211,21 @@ export const language = { tokenizer: { root: [ // identifiers and keywords - [/[a-zA-Z_$][\w$]*/, { + [/[a-z_$][\w$]*/, { cases: { '@keywords': { token: 'keyword.$0' }, '@default': 'identifier' } }], + // assume that identifiers starting with an uppercase letter are types + [/[A-Z][\w\$]*/, { + cases: { + '@keywords': { token: 'keyword.$0' }, + '@default': 'type.identifier' + } + }], + // whitespace { include: '@whitespace' }, From 9498adec54d4b0dc2e2d457351b72f375ba307e0 Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 11:57:12 +0100 Subject: [PATCH 07/12] Update tests to reflect new assumption --- src/apex/apex.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/apex/apex.test.ts b/src/apex/apex.test.ts index 37ba8145..8511a03f 100644 --- a/src/apex/apex.test.ts +++ b/src/apex/apex.test.ts @@ -209,7 +209,7 @@ testTokenization('apex', [ { startIndex: 13, type: '' }, { startIndex: 14, type: 'keyword.class.apex' }, { startIndex: 19, type: '' }, - { startIndex: 20, type: 'identifier.apex' }, + { startIndex: 20, type: 'type.identifier.apex' }, { startIndex: 27, type: '' }, { startIndex: 28, type: 'delimiter.curly.apex' }, { startIndex: 29, type: '' }, @@ -219,7 +219,7 @@ testTokenization('apex', [ { startIndex: 41, type: '' }, { startIndex: 42, type: 'identifier.apex' }, { startIndex: 46, type: 'delimiter.parenthesis.apex' }, - { startIndex: 47, type: 'identifier.apex' }, + { startIndex: 47, type: 'type.identifier.apex' }, { startIndex: 53, type: 'delimiter.square.apex' }, { startIndex: 55, type: '' }, { startIndex: 56, type: 'identifier.apex' }, @@ -530,7 +530,7 @@ testTokenization('apex', [ line: '23.5L', tokens: [ { startIndex: 0, type: 'number.float.apex' }, - { startIndex: 4, type: 'identifier.apex' } + { startIndex: 4, type: 'type.identifier.apex' } ] }], @@ -567,7 +567,7 @@ testTokenization('apex', [ [{ line: 'String s = "I\'m an Apex String";', tokens: [ - { startIndex: 0, type: 'identifier.apex' }, + { startIndex: 0, type: 'type.identifier.apex' }, { startIndex: 6, type: '' }, { startIndex: 7, type: 'identifier.apex' }, { startIndex: 8, type: '' }, @@ -581,7 +581,7 @@ testTokenization('apex', [ [{ line: 'String s = "concatenated" + " String" ;', tokens: [ - { startIndex: 0, type: 'identifier.apex' }, + { startIndex: 0, type: 'type.identifier.apex' }, { startIndex: 6, type: '' }, { startIndex: 7, type: 'identifier.apex' }, { startIndex: 8, type: '' }, From 40dfb97e568926bc35bdf7bb7692ba8173bbd1ce Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 12:37:20 +0100 Subject: [PATCH 08/12] Add support for apexdoc (based on java javadoc support) --- src/apex/apex.test.ts | 18 ++++++++++++++++++ src/apex/apex.ts | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/src/apex/apex.test.ts b/src/apex/apex.test.ts index 8511a03f..1b25de2a 100644 --- a/src/apex/apex.test.ts +++ b/src/apex/apex.test.ts @@ -198,6 +198,24 @@ testTokenization('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) {} } }', diff --git a/src/apex/apex.ts b/src/apex/apex.ts index 258172f7..6773dea0 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -269,6 +269,7 @@ export const language = { whitespace: [ [/[ \t\r\n]+/, ''], + [/\/\*\*(?!\/)/, 'comment.doc', '@apexdoc'], [/\/\*/, 'comment', '@comment'], [/\/\/.*$/, 'comment'], ], @@ -281,6 +282,13 @@ export const language = { [/[\/*]/, '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'], From 1d66cf8412a6d4221d04090f3ba1787b220fb7fa Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 12:45:25 +0100 Subject: [PATCH 09/12] Remove hex, octal and binary numbers - apex doesn't support them --- src/apex/apex.test.ts | 65 ------------------------------------------- src/apex/apex.ts | 3 -- 2 files changed, 68 deletions(-) diff --git a/src/apex/apex.test.ts b/src/apex/apex.test.ts index 1b25de2a..9c6c9b8f 100644 --- a/src/apex/apex.test.ts +++ b/src/apex/apex.test.ts @@ -274,55 +274,6 @@ testTokenization('apex', [ ] }], - [{ - line: '0x123', - tokens: [ - { startIndex: 0, type: 'number.hex.apex' } - ] - }], - - [{ - line: '0x5_2', - tokens: [ - { startIndex: 0, type: 'number.hex.apex' } - ] - }], - - [{ - line: '023L', - tokens: [ - { startIndex: 0, type: 'number.octal.apex' } - ] - }], - - [{ - line: '0123l', - tokens: [ - { startIndex: 0, type: 'number.octal.apex' } - ] - }], - - [{ - line: '05_2', - tokens: [ - { startIndex: 0, type: 'number.octal.apex' } - ] - }], - - [{ - line: '0b1010_0101', - tokens: [ - { startIndex: 0, type: 'number.binary.apex' } - ] - }], - - [{ - line: '0B001', - tokens: [ - { startIndex: 0, type: 'number.binary.apex' } - ] - }], - [{ line: '10e3', tokens: [ @@ -528,22 +479,6 @@ testTokenization('apex', [ ] }], - [{ - line: '0x52_', - tokens: [ - { startIndex: 0, type: 'number.hex.apex' }, - { startIndex: 4, type: 'identifier.apex' } - ] - }], - - [{ - line: '052_', - tokens: [ - { startIndex: 0, type: 'number.octal.apex' }, - { startIndex: 3, type: 'identifier.apex' } - ] - }], - [{ line: '23.5L', tokens: [ diff --git a/src/apex/apex.ts b/src/apex/apex.ts index 6773dea0..dd96db1f 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -245,9 +245,6 @@ export const language = { // numbers [/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/, 'number.float'], [/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/, 'number.float'], - [/0[xX](@hexdigits)[Ll]?/, 'number.hex'], - [/0(@octaldigits)[Ll]?/, 'number.octal'], - [/0[bB](@binarydigits)[Ll]?/, 'number.binary'], [/(@digits)[fFdD]/, 'number.float'], [/(@digits)[lL]?/, 'number'], From c421273fca18b8c447c3ee18c37982e8a4d92ce4 Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 12:46:12 +0100 Subject: [PATCH 10/12] Apex also doesn't have a byte type --- src/apex/apex.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apex/apex.ts b/src/apex/apex.ts index dd96db1f..c98515ea 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -64,7 +64,6 @@ export const language = { 'break', 'bulk', 'by', - 'byte', 'case', 'cast', 'catch', From acea7fd4a187b4101404f6626dadb42de36a1a1c Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 12:50:42 +0100 Subject: [PATCH 11/12] Add missing `get` keyword --- src/apex/apex.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apex/apex.ts b/src/apex/apex.ts index c98515ea..b22fd9b4 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -94,6 +94,7 @@ export const language = { 'for', 'from', 'future', + 'get', 'global', 'goto', 'group', From a3d82924a904c2b09c0fcee486c452acca5dc5fb Mon Sep 17 00:00:00 2001 From: olane Date: Fri, 10 Aug 2018 13:17:45 +0100 Subject: [PATCH 12/12] Create case variations of the keywords Apex is case insensitive, but we can't make the highlighter case insensitive without breaking the heuristic which assumes that identifiers starting with an upper case letter are types. As a compromise, create the common case variations of the keywords and match on all of them. --- src/apex/apex.test.ts | 35 +++++ src/apex/apex.ts | 299 ++++++++++++++++++++++-------------------- 2 files changed, 191 insertions(+), 143 deletions(-) diff --git a/src/apex/apex.test.ts b/src/apex/apex.test.ts index 9c6c9b8f..5f9f6c8f 100644 --- a/src/apex/apex.test.ts +++ b/src/apex/apex.test.ts @@ -251,6 +251,41 @@ testTokenization('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', diff --git a/src/apex/apex.ts b/src/apex/apex.ts index b22fd9b4..c02eddd9 100644 --- a/src/apex/apex.ts +++ b/src/apex/apex.ts @@ -43,153 +43,166 @@ export const conf: IRichLanguageConfiguration = { } }; +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: [ - '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' - ], + keywords: keywordsWithCaseVariations, operators: [ '=', '>', '<', '!', '~', '?', ':',