From 817ca5bd99882c977462f51480c132af542ede2a Mon Sep 17 00:00:00 2001
From: masad-frost <farismasad@gmail.com>
Date: Mon, 21 May 2018 23:03:09 -0700
Subject: [PATCH] Add Scheme language

---
 scripts/bundle.js                 |   1 +
 src/monaco.contribution.ts        |   1 +
 src/scheme/scheme.contribution.ts |  18 +++++
 src/scheme/scheme.test.ts         |  91 ++++++++++++++++++++++
 src/scheme/scheme.ts              | 124 ++++++++++++++++++++++++++++++
 test/setup.js                     |   1 +
 6 files changed, 236 insertions(+)
 create mode 100644 src/scheme/scheme.contribution.ts
 create mode 100644 src/scheme/scheme.test.ts
 create mode 100644 src/scheme/scheme.ts

diff --git a/scripts/bundle.js b/scripts/bundle.js
index 531a17c2..34f1527a 100644
--- a/scripts/bundle.js
+++ b/scripts/bundle.js
@@ -61,6 +61,7 @@ bundleOne('redshift/redshift');
 bundleOne('pgsql/pgsql');
 bundleOne('redis/redis');
 bundleOne('csp/csp');
+bundleOne('scheme/scheme');
 
 function bundleOne(moduleId, exclude) {
 	requirejs.optimize({
diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts
index 340dddd3..f3fa2a34 100644
--- a/src/monaco.contribution.ts
+++ b/src/monaco.contribution.ts
@@ -44,3 +44,4 @@ import './swift/swift.contribution';
 import './vb/vb.contribution';
 import './xml/xml.contribution';
 import './yaml/yaml.contribution';
+import './scheme/scheme.contribution';
diff --git a/src/scheme/scheme.contribution.ts b/src/scheme/scheme.contribution.ts
new file mode 100644
index 00000000..ac8eb9b2
--- /dev/null
+++ b/src/scheme/scheme.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' ? (<any>self).monaco : monaco;
+
+registerLanguage({
+    id: 'scheme',
+    extensions: ['.scm', '.ss', '.sch', '.rkt'],
+    aliases: ['scheme', 'Scheme'],
+    loader: () => _monaco.Promise.wrap(import('./scheme')),
+});
diff --git a/src/scheme/scheme.test.ts b/src/scheme/scheme.test.ts
new file mode 100644
index 00000000..4aaa04aa
--- /dev/null
+++ b/src/scheme/scheme.test.ts
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------------------------------
+ *  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('scheme', [
+    // Keywords
+    [
+        {
+            line: 'define-macro some',
+            tokens: [
+                { startIndex: 0, type: 'keyword.scheme' },
+                { startIndex: 12, type: 'white.scheme' },
+                { startIndex: 13, type: 'variable.scheme' },
+            ],
+        },
+    ],
+
+    // comments
+    [
+        {
+            line: '; comment',
+            tokens: [{ startIndex: 0, type: 'comment.scheme' }],
+        },
+    ],
+    [
+        {
+            line: '|# comment',
+            tokens: [{ startIndex: 0, type: 'comment.scheme' }],
+        },
+        {
+            line: 'multiline',
+            tokens: [{ startIndex: 0, type: 'comment.scheme' }],
+        },
+        {
+            line: '#| cons',
+            tokens: [
+                { startIndex: 0, type: 'comment.scheme' },
+                { startIndex: 2, type: 'white.scheme' },
+                { startIndex: 3, type: 'keyword.scheme' },
+            ],
+        },
+    ],
+
+    // strings
+    [
+        {
+            line: '"\\n string "',
+            tokens: [
+                { startIndex: 0, type: 'string.scheme' },
+                { startIndex: 1, type: 'string.escape.scheme' },
+                { startIndex: 3, type: 'string.scheme' },
+            ],
+        },
+    ],
+    [
+        {
+            line: '" string \\',
+            tokens: [{ startIndex: 0, type: 'string.scheme' }],
+        },
+        {
+            line: 'multiline',
+            tokens: [{ startIndex: 0, type: 'string.scheme' }],
+        },
+        {
+            line: ' ',
+            tokens: [
+                // previous line needs to be terminated with \
+                { startIndex: 0, type: 'white.scheme' },
+            ],
+        },
+    ],
+
+    // numbers
+    [
+        {
+            line: '1e2',
+            tokens: [{ startIndex: 0, type: 'number.float.scheme' }],
+        },
+    ],
+    [
+        {
+            line: '#x03BB',
+            tokens: [{ startIndex: 0, type: 'number.hex.scheme' }],
+        },
+    ],
+]);
diff --git a/src/scheme/scheme.ts b/src/scheme/scheme.ts
new file mode 100644
index 00000000..328d81ea
--- /dev/null
+++ b/src/scheme/scheme.ts
@@ -0,0 +1,124 @@
+/*---------------------------------------------------------------------------------------------
+ *  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 = {
+    comments: {
+        lineComment: ';',
+        blockComment: ['#|', '|#'],
+    },
+
+    brackets: [['(', ')'], ['{', '}'], ['[', ']']],
+
+    autoClosingPairs: [
+        { open: '{', close: '}' },
+        { open: '[', close: ']' },
+        { open: '(', close: ')' },
+        { open: '"', close: '"' },
+    ],
+
+    surroundingPairs: [
+        { open: '{', close: '}' },
+        { open: '[', close: ']' },
+        { open: '(', close: ')' },
+        { open: '"', close: '"' },
+    ],
+};
+
+export const language = <ILanguage>{
+    defaultToken: '',
+    ignoreCase: true,
+    tokenPostfix: '.scheme',
+
+    brackets: [
+        { open: '(', close: ')', token: 'delimiter.parenthesis' },
+        { open: '{', close: '}', token: 'delimiter.curly' },
+        { open: '[', close: ']', token: 'delimiter.square' },
+    ],
+
+    keywords: [
+        'case',
+        'do',
+        'let',
+        'loop',
+        'if',
+        'else',
+        'when',
+        'cons',
+        'car',
+        'cdr',
+        'cond',
+        'lambda',
+        'lambda*',
+        'syntax-rules',
+        'format',
+        'set!',
+        'quote',
+        'eval',
+        'append',
+        'list',
+        'list?',
+        'member?',
+        'load',
+    ],
+
+    constants: ['#t', '#f'],
+
+    operators: ['eq?', 'eqv?', 'equal?', 'and', 'or', 'not', 'null?'],
+
+    tokenizer: {
+        root: [
+            [/#[xXoObB][0-9a-fA-F]+/, 'number.hex'],
+            [/[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?/, 'number.float'],
+
+            [/(?:\b(?:(define|define-syntax|define-macro))\b)(\s+)((?:\w|\-|\!|\?)*)/, ['keyword', 'white', 'variable']],
+
+            [
+                /[a-zA-Z_#][a-zA-Z0-9_\-\?\!\*]*/,
+                {
+                    cases: {
+                        '@keywords': 'keyword',
+                        '@constants': 'constant',
+                        '@operators': 'operators',
+                        '@default': 'identifier',
+                    },
+                },
+            ],
+
+            { include: '@whitespace' },
+            { include: '@strings' },
+        ],
+
+        comment: [
+            [/[^\|#]+/, 'comment'],
+            [/\|\#/, 'comment', '@push'],
+            [/#\|/, 'comment', '@pop'],
+            [/[\|#]/, 'comment'],
+        ],
+
+        whitespace: [
+            [/[ \t\r\n]+/, 'white'],
+            [/\|\#/, 'comment', '@comment'],
+            [/;.*$/, 'comment'],
+        ],
+
+        strings: [
+            [/"$/, 'string', '@popall'],
+            [/"(?=.)/, 'string', '@multiLineString'],
+        ],
+
+        multiLineString: [
+            [/\\./, 'string.escape'],
+            [/"/, 'string', '@popall'],
+            [/.(?=.*")/, 'string'],
+            [/.*\\$/, 'string'],
+            [/.*$/, 'string', '@popall'],
+        ],
+    },
+};
diff --git a/test/setup.js b/test/setup.js
index d797c473..a4c957b7 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -65,6 +65,7 @@ define(['require'], function (require) {
 			'release/dev/redis/redis.test',
 			'release/dev/csp/csp.test',
 			'release/dev/st/st.test',
+			'release/dev/scheme/scheme.test',
 		], function () {
 			run(); // We can launch the tests!
 		}, function (err) {