Skip to content

Commit

Permalink
syntax highlighting for Scilla contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
tom2drum committed Nov 21, 2024
1 parent 438c7de commit b80787f
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 3 deletions.
4 changes: 4 additions & 0 deletions nextjs/csp/policies/monaco.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ export function monaco(): CspDev.DirectiveDescriptor {
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/editor/editor.main.nls.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/basic-languages/solidity/solidity.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/basic-languages/elixir/elixir.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/basic-languages/javascript/javascript.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/basic-languages/typescript/typescript.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/language/json/jsonMode.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/language/json/jsonWorker.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/language/typescript/tsMode.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/language/typescript/tsWorker.js',
'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/base/worker/workerMain.js',
],
'style-src': [
Expand Down
10 changes: 9 additions & 1 deletion ui/shared/monaco/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import CodeEditorTabs from './CodeEditorTabs';
import addExternalLibraryWarningDecoration from './utils/addExternalLibraryWarningDecoration';
import addFileImportDecorations from './utils/addFileImportDecorations';
import addMainContractCodeDecoration from './utils/addMainContractCodeDecoration';
import { defScilla, configScilla } from './utils/defScilla';
import getFullPathOfImportedFile from './utils/getFullPathOfImportedFile';
import * as themes from './utils/themes';
import useThemeColors from './utils/useThemeColors';

const EDITOR_OPTIONS: EditorProps['options'] = {
readOnly: true,
minimap: { enabled: false },
Expand Down Expand Up @@ -70,6 +70,8 @@ const CodeEditor = ({ data, remappings, libraries, language, mainFile, contractN
return 'json';
case 'solidity':
return 'sol';
case 'scilla':
return 'scilla';
default:
return 'javascript';
}
Expand All @@ -87,6 +89,12 @@ const CodeEditor = ({ data, remappings, libraries, language, mainFile, contractN
monaco.editor.defineTheme('blockscout-dark', themes.dark);
monaco.editor.setTheme(colorMode === 'light' ? 'blockscout-light' : 'blockscout-dark');

if (editorLanguage === 'scilla') {
monaco.languages.register({ id: editorLanguage });
monaco.languages.setMonarchTokensProvider(editorLanguage, defScilla);
monaco.languages.setLanguageConfiguration(editorLanguage, configScilla);
}

const loadedModels = monaco.editor.getModels();
const loadedModelsPaths = loadedModels.map((model) => model.uri.path);
const newModels = data.slice(1)
Expand Down
170 changes: 170 additions & 0 deletions ui/shared/monaco/utils/defScilla.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/* eslint-disable max-len */
import type * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

export const configScilla: monaco.languages.LanguageConfiguration = {
comments: {
blockComment: [ '(*', '*)' ],
},
brackets: [
[ '{', '}' ],
[ '[', ']' ],
[ '(', ')' ],
],
autoClosingPairs: [
{ open: '"', close: '"', notIn: [ 'string', 'comment' ] },
{ open: '{', close: '}', notIn: [ 'string', 'comment' ] },
{ open: '[', close: ']', notIn: [ 'string', 'comment' ] },
{ open: '(', close: ')', notIn: [ 'string', 'comment' ] },
],
};

export const defScilla: monaco.languages.IMonarchLanguage = {
// defaultToken: 'invalid',

brackets: [
{ token: 'delimiter.curly', open: '{', close: '}' },
{ token: 'delimiter.parenthesis', open: '(', close: ')' },
{ token: 'delimiter.square', open: '[', close: ']' },
],

keywords: [
'let',
'contains',
'delete',
'put',
'remove',
'library',
'import',
'contract',
'event',
'field',
'send',
'fun',
'transition',
'procedure',
'match',
'end',
'with',
'builtin',
'Emp',
'of',
'scilla_version',
],

builtins: [
'eq',
'add',
'sub',
'mul',
'div',
'rem',
'lt',
'blt',
'in',
'substr',
'sha256hash',
'keccak256hash',
'ripemd160hash',
'to_byStr',
'to_nat',
'pow',
'to_uint256',
'to_uint32',
'to_uint64',
'to_uint128',
'to_int256',
'to_int32',
'to_int64',
'to_int128',
'schnorr_verify',
'concat',
'andb',
'orb',
'bool_to_string',
'negb',
'Nil',
],

operators: [
'=',
'=>',
'<=',
'->',
'<-',
],

// we include these common regular expressions
symbols: /[=><!~?:&|+\-*/^%]+/,
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
integersuffix: /(ll|LL|[uUlL])?(ll|LL|[uUlL])?/,
floatsuffix: /[fl]?/i,

// numbers
decpart: /\d(_?\d)*/,
decimal: /0|@decpart/,

tokenizer: {
root: [
// identifiers and keywords
[
/[a-z_]\w*[!?=]?/i,
{
cases: {
'@keywords': { token: 'keyword.$0' },
'@builtins': 'predefined',
'@default': 'identifier',
},
},
],

// the rule above does not work for Nil and Emp for some reason
// so we have to explicitly define them
[ /\b(Nil)\b/, 'predefined' ],
[ /\b(Emp)\b/, 'keyword' ],

// types
[ /\b(String|Uint32|Uint64|Uint128|Uint256|Int32|Int64|Int128|Int256|Map|True|False|ByStr|ByStr20|ByStr32|ByStr64|ByStr33|BNum|Option|None|Bool|Some|List|Cons|Pair|type|Zero|Succ|Message)\b/, 'type' ],

// whitespace
{ include: '@whitespace' },

// numbers
[ /0x[0-9a-f](_?[0-9a-f])*/i, 'number.hex' ],
[ /0[_o][0-7](_?[0-7])*/i, 'number.octal' ],
[ /0b[01](_?[01])*/i, 'number.binary' ],
[ /0[dD]@decpart/, 'number' ],
[
/@decimal((\.@decpart)?([eE][-+]?@decpart)?)/,
{
cases: {
$1: 'number.float',
'@default': 'number',
},
},
],

// strings
[ /"([^"\\]|\\.)*$/, 'string.invalid' ], // non-teminated string
[ /"/, 'string', '@string' ],

// characters
[ /'[^\\']'/, 'string' ],
[ /(')(@escapes)(')/, [ 'string', 'string.escape', 'string' ] ],
[ /'/, 'string.invalid' ],
],
whitespace: [
[ /[ \t\r\n]+/, 'white' ],
[ /\(\*.*$/, 'comment' ],
],
comment: [
[ /[^(*]+/, 'comment' ],
[ /[(*]/, 'comment' ],
],
string: [
[ /[^\\"]+/, 'string' ],
[ /@escapes/, 'string.escape' ],
[ /\\./, 'string.escape.invalid' ],
[ /"/, { token: 'string.quote', bracket: '@close', next: '@pop' } ],
],
},
};
8 changes: 6 additions & 2 deletions ui/shared/monaco/utils/themes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export const light = {
base: 'vs' as const,
inherit: true,
rules: [],
rules: [
{ token: 'predefined', foreground: '#cd3131' },
],
colors: {
'editor.background': '#f5f5f6',
'editorWidget.background': '#f5f5f6',
Expand Down Expand Up @@ -45,7 +47,9 @@ export const light = {
export const dark = {
base: 'vs-dark' as const,
inherit: true,
rules: [],
rules: [
{ token: 'predefined', foreground: '#f44747' },
],
colors: {
'editor.background': '#1a1b1b',
'editorWidget.background': '#1a1b1b',
Expand Down

0 comments on commit b80787f

Please sign in to comment.