mirror of https://github.com/sgoudham/carbon.git
parent
7f49dad02b
commit
7bf1aca897
@ -1 +1,582 @@
|
||||
import 'codemirror-solidity'
|
||||
/**
|
||||
* codemirror-solidity by alincode — https://github.com/alincode/codemirror-solidity
|
||||
* Distributed under MIT
|
||||
*/
|
||||
'use strict'
|
||||
const CodeMirror = require('codemirror')
|
||||
|
||||
CodeMirror.defineMode('solidity', function (config) {
|
||||
let indentUnit = config.indentUnit
|
||||
|
||||
// let functionKeyword = 'function'
|
||||
// let functionNameKeyword = 'Name'
|
||||
// let leftBracketSign = '('
|
||||
// let rightBracketSign = ')'
|
||||
// let functionVariableName = 'variable'
|
||||
// let keyWordContract = 'contract'
|
||||
|
||||
let keywords = {
|
||||
pragma: true,
|
||||
solidity: true,
|
||||
import: true,
|
||||
as: true,
|
||||
from: true,
|
||||
contract: true,
|
||||
constructor: true,
|
||||
is: true,
|
||||
function: true,
|
||||
modifier: true,
|
||||
// modifiers
|
||||
pure: true,
|
||||
view: true,
|
||||
payable: true,
|
||||
constant: true,
|
||||
anonymous: true,
|
||||
indexed: true,
|
||||
returns: true,
|
||||
return: true,
|
||||
event: true,
|
||||
struct: true,
|
||||
mapping: true,
|
||||
interface: true,
|
||||
using: true,
|
||||
library: true,
|
||||
storage: true,
|
||||
memory: true,
|
||||
calldata: true,
|
||||
public: true,
|
||||
private: true,
|
||||
external: true,
|
||||
internal: true,
|
||||
emit: true,
|
||||
assembly: true,
|
||||
abstract: true,
|
||||
after: true,
|
||||
catch: true,
|
||||
final: true,
|
||||
in: true,
|
||||
inline: true,
|
||||
let: true,
|
||||
match: true,
|
||||
null: true,
|
||||
of: true,
|
||||
relocatable: true,
|
||||
static: true,
|
||||
try: true,
|
||||
typeof: true,
|
||||
var: true,
|
||||
}
|
||||
|
||||
let keywordsSpecial = {
|
||||
pragma: true,
|
||||
returns: true,
|
||||
address: true,
|
||||
contract: true,
|
||||
function: true,
|
||||
struct: true,
|
||||
}
|
||||
|
||||
let keywordsEtherUnit = {
|
||||
wei: true,
|
||||
szabo: true,
|
||||
finney: true,
|
||||
ether: true,
|
||||
}
|
||||
let keywordsTimeUnit = {
|
||||
seconds: true,
|
||||
minutes: true,
|
||||
hours: true,
|
||||
days: true,
|
||||
weeks: true,
|
||||
}
|
||||
let keywordsBlockAndTransactionProperties = {
|
||||
['block']: ['coinbase', 'difficulty', 'gaslimit', 'number', 'timestamp'],
|
||||
['msg']: ['data', 'sender', 'sig', 'value'],
|
||||
['tx']: ['gasprice', 'origin'],
|
||||
}
|
||||
let keywordsMoreBlockAndTransactionProperties = {
|
||||
now: true,
|
||||
gasleft: true,
|
||||
blockhash: true,
|
||||
}
|
||||
let keywordsErrorHandling = {
|
||||
assert: true,
|
||||
require: true,
|
||||
revert: true,
|
||||
throw: true,
|
||||
}
|
||||
let keywordsMathematicalAndCryptographicFuctions = {
|
||||
addmod: true,
|
||||
mulmod: true,
|
||||
keccak256: true,
|
||||
sha256: true,
|
||||
ripemd160: true,
|
||||
ecrecover: true,
|
||||
}
|
||||
let keywordsContractRelated = {
|
||||
this: true,
|
||||
selfdestruct: true,
|
||||
super: true,
|
||||
}
|
||||
let keywordsTypeInformation = { type: true }
|
||||
let keywordsContractList = {}
|
||||
|
||||
let keywordsControlStructures = {
|
||||
if: true,
|
||||
else: true,
|
||||
while: true,
|
||||
do: true,
|
||||
for: true,
|
||||
break: true,
|
||||
continue: true,
|
||||
switch: true,
|
||||
case: true,
|
||||
default: true,
|
||||
}
|
||||
|
||||
let keywordsValueTypes = {
|
||||
bool: true,
|
||||
byte: true,
|
||||
string: true,
|
||||
enum: true,
|
||||
address: true,
|
||||
}
|
||||
|
||||
let keywordsV0505NewReserve = {
|
||||
alias: true,
|
||||
apply: true,
|
||||
auto: true,
|
||||
copyof: true,
|
||||
define: true,
|
||||
immutable: true,
|
||||
implements: true,
|
||||
macro: true,
|
||||
mutable: true,
|
||||
override: true,
|
||||
partial: true,
|
||||
promise: true,
|
||||
reference: true,
|
||||
sealed: true,
|
||||
sizeof: true,
|
||||
supports: true,
|
||||
typedef: true,
|
||||
unchecked: true,
|
||||
}
|
||||
|
||||
let keywordsAbiEncodeDecodeFunctions = {
|
||||
['abi']: ['decode', 'encodePacked', 'encodeWithSelector', 'encodeWithSignature', 'encode'],
|
||||
}
|
||||
|
||||
let keywordsMembersOfAddressType = [
|
||||
'transfer',
|
||||
'send',
|
||||
'balance',
|
||||
'call',
|
||||
'delegatecall',
|
||||
'staticcall',
|
||||
]
|
||||
|
||||
let natSpecTags = ['title', 'author', 'notice', 'dev', 'param', 'return']
|
||||
|
||||
// let functionStructureStage = [{
|
||||
// function: ['function', 'returns']
|
||||
// },
|
||||
// leftBracketSign,
|
||||
// parameterName,
|
||||
// parameterValue,
|
||||
// rightBracketSign,
|
||||
// ];
|
||||
|
||||
let atoms = {
|
||||
delete: true,
|
||||
new: true,
|
||||
true: true,
|
||||
false: true,
|
||||
// "iota": true, "nil": true, "append": true,
|
||||
// "cap": true, "close": true, "complex": true, "copy": true, "imag": true,
|
||||
// "make": true, "panic": true, "print": true,
|
||||
// "println": true, "real": true, "recover": true
|
||||
}
|
||||
|
||||
let isOperatorChar = /[+\-*&^%:=<>!|\/~]/
|
||||
let isNegativeChar = /[-]/
|
||||
|
||||
let curPunc
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
let ch = stream.next()
|
||||
|
||||
if (ch == '"' || ch == "'" || ch == '`') {
|
||||
state.tokenize = tokenString(ch)
|
||||
return state.tokenize(stream, state)
|
||||
}
|
||||
|
||||
if (isVersion(stream, state)) return 'version'
|
||||
|
||||
if (
|
||||
ch == '.' &&
|
||||
keywordsMembersOfAddressType.some(function (item) {
|
||||
return stream.match(`${item}`)
|
||||
})
|
||||
)
|
||||
return 'addressFunction'
|
||||
|
||||
if (isNumber(ch, stream)) return 'number'
|
||||
|
||||
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
return updateGarmmer(ch, state)
|
||||
}
|
||||
|
||||
if (ch == '/') {
|
||||
if (stream.eat('*')) {
|
||||
state.tokenize = tokenComment
|
||||
return tokenComment(stream, state)
|
||||
}
|
||||
if (stream.match(/\/{2}/)) {
|
||||
while ((ch = stream.next())) {
|
||||
if (ch == '@') {
|
||||
stream.backUp(1)
|
||||
state.grammar = 'doc'
|
||||
break
|
||||
}
|
||||
}
|
||||
return 'doc'
|
||||
}
|
||||
|
||||
if (stream.eat('/')) {
|
||||
stream.skipToEnd()
|
||||
return 'comment'
|
||||
}
|
||||
}
|
||||
|
||||
if (isNegativeChar.test(ch)) {
|
||||
if (isNumber(stream.peek(), stream)) return 'number'
|
||||
return 'operator'
|
||||
}
|
||||
|
||||
if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar)
|
||||
return 'operator'
|
||||
}
|
||||
stream.eatWhile(/[\w\$_\xa1-\uffff]/)
|
||||
|
||||
let cur = stream.current()
|
||||
|
||||
if (state.grammar == 'doc') {
|
||||
if (
|
||||
natSpecTags.some(function (item) {
|
||||
return cur == `@${item}`
|
||||
})
|
||||
)
|
||||
return 'docReserve'
|
||||
return 'doc'
|
||||
}
|
||||
|
||||
if (cur === 'solidity' && state.lastToken == 'pragma') {
|
||||
state.lastToken = state.lastToken + ' ' + cur
|
||||
}
|
||||
|
||||
if (keywords.propertyIsEnumerable(cur)) {
|
||||
if (cur == 'case' || cur == 'default') curPunc = 'case'
|
||||
if (keywordsSpecial.propertyIsEnumerable(cur)) state.lastToken = cur
|
||||
//if (cur == 'function' && state.para == 'parameterMode')
|
||||
return 'keyword'
|
||||
}
|
||||
|
||||
if (keywordsEtherUnit.propertyIsEnumerable(cur)) return 'etherUnit'
|
||||
if (keywordsContractRelated.propertyIsEnumerable(cur)) return 'contractRelated'
|
||||
if (
|
||||
keywordsControlStructures.propertyIsEnumerable(cur) ||
|
||||
keywordsTypeInformation.propertyIsEnumerable(cur) ||
|
||||
keywordsV0505NewReserve.propertyIsEnumerable(cur)
|
||||
)
|
||||
return 'keyword'
|
||||
if (
|
||||
keywordsValueTypes.propertyIsEnumerable(cur) ||
|
||||
keywordsTimeUnit.propertyIsEnumerable(cur) ||
|
||||
isValidInteger(cur) ||
|
||||
isValidBytes(cur) ||
|
||||
isValidFixed(cur)
|
||||
) {
|
||||
state.lastToken = state.lastToken + 'variable'
|
||||
return 'keyword'
|
||||
}
|
||||
|
||||
if (atoms.propertyIsEnumerable(cur)) return 'atom'
|
||||
if (keywordsErrorHandling.propertyIsEnumerable(cur)) return 'errorHandling'
|
||||
if (keywordsMathematicalAndCryptographicFuctions.propertyIsEnumerable(cur))
|
||||
return 'mathematicalAndCryptographic'
|
||||
|
||||
if (
|
||||
keywordsMoreBlockAndTransactionProperties.propertyIsEnumerable(cur) ||
|
||||
(keywordsBlockAndTransactionProperties.hasOwnProperty(cur) &&
|
||||
keywordsBlockAndTransactionProperties[cur].some(function (item) {
|
||||
return stream.match(`.${item}`)
|
||||
}))
|
||||
)
|
||||
return 'variable-2'
|
||||
|
||||
if (
|
||||
keywordsAbiEncodeDecodeFunctions.hasOwnProperty(cur) &&
|
||||
keywordsAbiEncodeDecodeFunctions[cur].some(function (item) {
|
||||
return stream.match(`.${item}`)
|
||||
})
|
||||
)
|
||||
return 'abi'
|
||||
|
||||
let style = updateHexLiterals(cur, stream)
|
||||
if (style != null) return style
|
||||
|
||||
if (
|
||||
(state.lastToken == 'functionName(' || state.lastToken == 'returns(') &&
|
||||
keywordsContractList.propertyIsEnumerable(cur)
|
||||
) {
|
||||
state.lastToken += 'variable'
|
||||
return 'variable'
|
||||
}
|
||||
if (state.lastToken == 'function') {
|
||||
state.lastToken = 'functionName'
|
||||
if (state.para == null) {
|
||||
state.grammar = 'function'
|
||||
state.para = ''
|
||||
}
|
||||
//state.parasMode = isNaN(state.parasMode) ? 1 : state.functionLayerCount++;
|
||||
state.para += 'functionName'
|
||||
return 'functionName'
|
||||
}
|
||||
|
||||
if (state.lastToken == 'functionName(variable') {
|
||||
state.lastToken = 'functionName('
|
||||
return 'parameterValue'
|
||||
}
|
||||
|
||||
if (state.lastToken == 'returns(variable') {
|
||||
state.lastToken = 'returns('
|
||||
return 'parameterValue'
|
||||
}
|
||||
|
||||
if (state.lastToken == 'address' && cur == 'payable') {
|
||||
state.lastToken = 'address payable'
|
||||
}
|
||||
if (state.lastToken == 'contract' || state.lastToken == 'struct') {
|
||||
keywordsContractList[cur] = true
|
||||
state.lastToken = null
|
||||
}
|
||||
if (state.grammar == 'function') {
|
||||
return 'parameterValue'
|
||||
}
|
||||
|
||||
return 'variable'
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function (stream, state) {
|
||||
let escaped = false,
|
||||
next,
|
||||
end = false
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) {
|
||||
end = true
|
||||
break
|
||||
}
|
||||
escaped = !escaped && quote != '`' && next == '\\'
|
||||
}
|
||||
if (end || !(escaped || quote == '`')) state.tokenize = tokenBase
|
||||
return 'string'
|
||||
}
|
||||
}
|
||||
|
||||
function tokenComment(stream, state) {
|
||||
let maybeEnd = false,
|
||||
ch
|
||||
while ((ch = stream.next())) {
|
||||
if (ch == '/' && maybeEnd) {
|
||||
state.tokenize = tokenBase
|
||||
break
|
||||
}
|
||||
maybeEnd = ch == '*'
|
||||
}
|
||||
return 'comment'
|
||||
}
|
||||
|
||||
function isVersion(stream, state) {
|
||||
if (state.lastToken == 'pragma solidity') {
|
||||
state.lastToken = null
|
||||
return (
|
||||
!state.startOfLine &&
|
||||
(stream.match(/[\^{0}][0-9\.]+/) ||
|
||||
stream.match(/[\>\=]+?[\s]*[0-9\.]+[\s]*[\<]?[\s]*[0-9\.]+/))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function isNumber(ch, stream) {
|
||||
if (/[\d\.]/.test(ch)) {
|
||||
if (ch == '.') {
|
||||
stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/)
|
||||
} else if (ch == '0') {
|
||||
stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/)
|
||||
} else {
|
||||
stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function isValidInteger(token) {
|
||||
if (token.match(/^[u]?int/)) {
|
||||
if (token.indexOf('t') + 1 == token.length) return true
|
||||
let numberPart = token.substr(token.indexOf('t') + 1, token.length)
|
||||
return numberPart % 8 === 0 && numberPart <= 256
|
||||
}
|
||||
}
|
||||
|
||||
function isValidBytes(token) {
|
||||
if (token.match(/^bytes/)) {
|
||||
if (token.indexOf('s') + 1 == token.length) return true
|
||||
let bytesPart = token.substr(token.indexOf('s') + 1, token.length)
|
||||
return bytesPart <= 32
|
||||
}
|
||||
}
|
||||
|
||||
function isValidFixed(token) {
|
||||
if (token.match(/^[u]?fixed([0-9]+x[0-9]+)?/)) {
|
||||
if (token.indexOf('d') + 1 == token.length) return true
|
||||
let numberPart = token.substr(token.indexOf('d') + 1, token.length).split('x')
|
||||
return numberPart[0] % 8 === 0 && numberPart[0] <= 256 && numberPart[1] <= 80
|
||||
}
|
||||
}
|
||||
|
||||
function updateHexLiterals(token, stream) {
|
||||
if (token.match(/^hex/) && stream.peek() == '"') {
|
||||
let maybeEnd = false,
|
||||
ch,
|
||||
hexValue = '',
|
||||
stringAfterHex = ''
|
||||
while ((ch = stream.next())) {
|
||||
stringAfterHex += ch
|
||||
if (ch == '"' && maybeEnd) {
|
||||
hexValue = stringAfterHex.substring(1, stringAfterHex.length - 1)
|
||||
if (hexValue.match(/^[0-9a-fA-F]+$/)) {
|
||||
return 'number'
|
||||
} else {
|
||||
stream.backUp(stringAfterHex.length)
|
||||
}
|
||||
break
|
||||
}
|
||||
maybeEnd = maybeEnd || ch == '"'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateGarmmer(ch, state) {
|
||||
if (ch == ',' && state.para == 'functionName(variable') {
|
||||
state.para = 'functionName('
|
||||
}
|
||||
if (state.para != null && state.para.startsWith('functionName')) {
|
||||
if (ch == ')') {
|
||||
if (state.para.endsWith('(')) {
|
||||
state.para = state.para.substr(0, state.para.length - 1)
|
||||
if (state.para == 'functionName') state.grammar = ''
|
||||
}
|
||||
} else if (ch == '(') {
|
||||
state.para += ch
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '(' && state.lastToken == 'functionName') {
|
||||
state.lastToken += ch
|
||||
} else if (ch == ')' && state.lastToken == 'functionName(') {
|
||||
state.lastToken = null
|
||||
} else if (ch == '(' && state.lastToken == 'returns') {
|
||||
state.lastToken += ch
|
||||
} else if (
|
||||
ch == ')' &&
|
||||
(state.lastToken == 'returns(' || state.lastToken == 'returns(variable')
|
||||
) {
|
||||
state.lastToken = null
|
||||
}
|
||||
if (ch == '(' && state.lastToken == 'address') {
|
||||
state.lastToken += ch
|
||||
}
|
||||
curPunc = ch
|
||||
return null
|
||||
}
|
||||
|
||||
function Context(indented, column, type, align, prev) {
|
||||
this.indented = indented
|
||||
this.column = column
|
||||
this.type = type
|
||||
this.align = align
|
||||
this.prev = prev
|
||||
}
|
||||
function pushContext(state, col, type) {
|
||||
return (state.context = new Context(state.indented, col, type, null, state.context))
|
||||
}
|
||||
function popContext(state) {
|
||||
if (!state.context.prev) return
|
||||
let t = state.context.type
|
||||
if (t == ')' || t == ']' || t == '}') state.indented = state.context.indented
|
||||
return (state.context = state.context.prev)
|
||||
}
|
||||
|
||||
// Interface
|
||||
return {
|
||||
startState: function (basecolumn) {
|
||||
return {
|
||||
tokenize: null,
|
||||
context: new Context((basecolumn || 0) - indentUnit, 0, 'top', false),
|
||||
indented: 0,
|
||||
startOfLine: true,
|
||||
}
|
||||
},
|
||||
|
||||
token: function (stream, state) {
|
||||
let ctx = state.context
|
||||
if (stream.sol()) {
|
||||
if (ctx.align == null) ctx.align = false
|
||||
state.indented = stream.indentation()
|
||||
state.startOfLine = true
|
||||
if (ctx.type == 'case') ctx.type = '}'
|
||||
if (state.grammar == 'doc') state.grammar = null
|
||||
}
|
||||
if (stream.eatSpace()) return null
|
||||
curPunc = null
|
||||
let style = (state.tokenize || tokenBase)(stream, state)
|
||||
|
||||
if (style == 'comment') return style
|
||||
if (ctx.align == null) ctx.align = true
|
||||
|
||||
if (curPunc == '{') pushContext(state, stream.column(), '}')
|
||||
else if (curPunc == '[') pushContext(state, stream.column(), ']')
|
||||
else if (curPunc == '(') pushContext(state, stream.column(), ')')
|
||||
else if (curPunc == 'case') ctx.type = 'case'
|
||||
else if (curPunc == '}' && ctx.type == '}') popContext(state)
|
||||
else if (curPunc == ctx.type) popContext(state)
|
||||
state.startOfLine = false
|
||||
return style
|
||||
},
|
||||
|
||||
indent: function (state, textAfter) {
|
||||
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass
|
||||
let ctx = state.context,
|
||||
firstChar = textAfter && textAfter.charAt(0)
|
||||
if (ctx.type == 'case' && /^(?:case|default)\b/.test(textAfter)) {
|
||||
state.context.type = '}'
|
||||
return ctx.indented
|
||||
}
|
||||
let closing = firstChar == ctx.type
|
||||
if (ctx.align) return ctx.column + (closing ? 0 : 1)
|
||||
else return ctx.indented + (closing ? 0 : indentUnit)
|
||||
},
|
||||
|
||||
electricChars: '{}):',
|
||||
closeBrackets: '()[]{}\'\'""``',
|
||||
fold: 'brace',
|
||||
blockCommentStart: '/*',
|
||||
blockCommentEnd: '*/',
|
||||
lineComment: '//',
|
||||
}
|
||||
})
|
||||
|
||||
CodeMirror.defineMIME('text/x-solidity', 'solidity')
|
||||
|
Loading…
Reference in New Issue