diff --git a/__tests__/__fixtures__/solidity/sample.sol b/__tests__/__fixtures__/solidity/sample.sol
index 14c0f555..d07253a6 100644
--- a/__tests__/__fixtures__/solidity/sample.sol
+++ b/__tests__/__fixtures__/solidity/sample.sol
@@ -1,7 +1,156 @@
-pragma solidity ^0.8.9;
+// SPDX-License-Identifier: GPL-3.0
+pragma solidity >=0.7.0 <0.9.0;
+/// @title Voting with delegation.
+contract Ballot {
+ // This declares a new complex type which will
+ // be used for variables later.
+ // It will represent a single voter.
+ struct Voter {
+ uint weight; // weight is accumulated by delegation
+ bool voted; // if true, that person already voted
+ address delegate; // person delegated to
+ uint vote; // index of the voted proposal
+ }
+
+ // This is a type for a single proposal.
+ struct Proposal {
+ bytes32 name; // short name (up to 32 bytes)
+ uint voteCount; // number of accumulated votes
+ }
+
+ address public chairperson;
+
+ // This declares a state variable that
+ // stores a `Voter` struct for each possible address.
+ mapping(address => Voter) public voters;
+
+ // A dynamically-sized array of `Proposal` structs.
+ Proposal[] public proposals;
+
+ /// Create a new ballot to choose one of `proposalNames`.
+ constructor(bytes32[] memory proposalNames) {
+ chairperson = msg.sender;
+ voters[chairperson].weight = 1;
+
+ // For each of the provided proposal names,
+ // create a new proposal object and add it
+ // to the end of the array.
+ for (uint i = 0; i < proposalNames.length; i++) {
+ // `Proposal({...})` creates a temporary
+ // Proposal object and `proposals.push(...)`
+ // appends it to the end of `proposals`.
+ proposals.push(Proposal({
+ name: proposalNames[i],
+ voteCount: 0
+ }));
+ }
+ }
+
+ // Give `voter` the right to vote on this ballot.
+ // May only be called by `chairperson`.
+ function giveRightToVote(address voter) external {
+ // If the first argument of `require` evaluates
+ // to `false`, execution terminates and all
+ // changes to the state and to Ether balances
+ // are reverted.
+ // This used to consume all gas in old EVM versions, but
+ // not anymore.
+ // It is often a good idea to use `require` to check if
+ // functions are called correctly.
+ // As a second argument, you can also provide an
+ // explanation about what went wrong.
+ require(
+ msg.sender == chairperson,
+ "Only chairperson can give right to vote."
+ );
+ require(
+ !voters[voter].voted,
+ "The voter already voted."
+ );
+ require(voters[voter].weight == 0);
+ voters[voter].weight = 1;
+ }
+
+ /// Delegate your vote to the voter `to`.
+ function delegate(address to) external {
+ // assigns reference
+ Voter storage sender = voters[msg.sender];
+ require(sender.weight != 0, "You have no right to vote");
+ require(!sender.voted, "You already voted.");
+
+ require(to != msg.sender, "Self-delegation is disallowed.");
+
+ // Forward the delegation as long as
+ // `to` also delegated.
+ // In general, such loops are very dangerous,
+ // because if they run too long, they might
+ // need more gas than is available in a block.
+ // In this case, the delegation will not be executed,
+ // but in other situations, such loops might
+ // cause a contract to get "stuck" completely.
+ while (voters[to].delegate != address(0)) {
+ to = voters[to].delegate;
+
+ // We found a loop in the delegation, not allowed.
+ require(to != msg.sender, "Found loop in delegation.");
+ }
+
+ Voter storage delegate_ = voters[to];
+
+ // Voters cannot delegate to accounts that cannot vote.
+ require(delegate_.weight >= 1);
+
+ // Since `sender` is a reference, this
+ // modifies `voters[msg.sender]`.
+ sender.voted = true;
+ sender.delegate = to;
+
+ if (delegate_.voted) {
+ // If the delegate already voted,
+ // directly add to the number of votes
+ proposals[delegate_.vote].voteCount += sender.weight;
+ } else {
+ // If the delegate did not vote yet,
+ // add to her weight.
+ delegate_.weight += sender.weight;
+ }
+ }
+
+ /// Give your vote (including votes delegated to you)
+ /// to proposal `proposals[proposal].name`.
+ function vote(uint proposal) external {
+ Voter storage sender = voters[msg.sender];
+ require(sender.weight != 0, "Has no right to vote");
+ require(!sender.voted, "Already voted.");
+ sender.voted = true;
+ sender.vote = proposal;
+
+ // If `proposal` is out of the range of the array,
+ // this will throw automatically and revert all
+ // changes.
+ proposals[proposal].voteCount += sender.weight;
+ }
+
+ /// @dev Computes the winning proposal taking all
+ /// previous votes into account.
+ function winningProposal() public view
+ returns (uint winningProposal_)
+ {
+ uint winningVoteCount = 0;
+ for (uint p = 0; p < proposals.length; p++) {
+ if (proposals[p].voteCount > winningVoteCount) {
+ winningVoteCount = proposals[p].voteCount;
+ winningProposal_ = p;
+ }
+ }
+ }
-contract HelloWorld {
- function render () public pure returns (string memory) {
- return 'Hello World';
+ // Calls winningProposal() function to get the index
+ // of the winner contained in the proposals array and then
+ // returns the name of the winner
+ function winnerName() external view
+ returns (bytes32 winnerName_)
+ {
+ winnerName_ = proposals[winningProposal()].name;
}
}
diff --git a/__tests__/__snapshots__/codeMirror.test.js.snap b/__tests__/__snapshots__/codeMirror.test.js.snap
index 904c93dd..853b1d76 100644
--- a/__tests__/__snapshots__/codeMirror.test.js.snap
+++ b/__tests__/__snapshots__/codeMirror.test.js.snap
@@ -345,11 +345,160 @@ exports[`Supported languages Shell should syntax highlight an example 1`] = `
`;
exports[`Supported languages Solidity should syntax highlight an example 1`] = `
-"
pragma solidity ^0.
8.9;
+"
+pragma solidity >=0.7.0 <0.9.0;
+/// @title Voting with delegation.
+contract Ballot {
+
+
+
+ struct Voter {
+ uint weight;
+ bool voted;
+ address delegate;
+ uint vote;
+ }
+
+
+ struct Proposal {
+ bytes32 name;
+ uint voteCount;
+ }
+
+ address public chairperson;
+
+
+
+ mapping(address => Voter) public voters;
+
+
+ Proposal[] public proposals;
+
+ /// Create a new ballot to choose one of \`proposalNames\`.
+ constructor(bytes32[] memory proposalNames) {
+ chairperson = msg.sender;
+ voters[chairperson].weight = 1;
+
+
+
+
+ for (uint i = 0; i < proposalNames.length; i++) {
+
+
+
+ proposals.push(Proposal({
+ name: proposalNames[i],
+ voteCount: 0
+ }));
+ }
+ }
+
+
+
+ function giveRightToVote(address voter) external {
+
+
+
+
+
+
+
+
+
+
+ require(
+ msg.sender == chairperson,
+ "Only chairperson can give right to vote."
+ );
+ require(
+ !voters[voter].voted,
+ "The voter already voted."
+ );
+ require(voters[voter].weight == 0);
+ voters[voter].weight = 1;
+ }
+
+ /// Delegate your vote to the voter \`to\`.
+ function delegate(address to) external {
+
+ Voter storage sender = voters[msg.sender];
+ require(sender.weight != 0, "You have no right to vote");
+ require(!sender.voted, "You already voted.");
+
+ require(to != msg.sender, "Self-delegation is disallowed.");
+
+
+
+
+
+
+
+
+
+ while (voters[to].delegate != address(0)) {
+ to = voters[to].delegate;
+
+
+ require(to != msg.sender, "Found loop in delegation.");
+ }
+
+ Voter storage delegate_ = voters[to];
+
+
+ require(delegate_.weight >= 1);
+
+
+
+ sender.voted = true;
+ sender.delegate = to;
+
+ if (delegate_.voted) {
+
+
+ proposals[delegate_.vote].voteCount += sender.weight;
+ } else {
+
+
+ delegate_.weight += sender.weight;
+ }
+ }
+
+ /// Give your vote (including votes delegated to you)
+ /// to proposal \`proposals[proposal].name\`.
+ function vote(uint proposal) external {
+ Voter storage sender = voters[msg.sender];
+ require(sender.weight != 0, "Has no right to vote");
+ require(!sender.voted, "Already voted.");
+ sender.voted = true;
+ sender.vote = proposal;
+
+
+
+
+ proposals[proposal].voteCount += sender.weight;
+ }
+
+ /// @dev Computes the winning proposal taking all
+ /// previous votes into account.
+ function winningProposal() public view
+ returns (uint winningProposal_)
+ {
+ uint winningVoteCount = 0;
+ for (uint p = 0; p < proposals.length; p++) {
+ if (proposals[p].voteCount > winningVoteCount) {
+ winningVoteCount = proposals[p].voteCount;
+ winningProposal_ = p;
+ }
+ }
+ }
-contract HelloWorld {
- function render () public pure returns (string memory) {
- return 'Hello World';
+
+
+
+ function winnerName() external view
+ returns (bytes32 winnerName_)
+ {
+ winnerName_ = proposals[winningProposal()].name;
}
}
"
diff --git a/src/utils/cm-mode-imports.js b/src/utils/cm-mode-imports.js
index b704aba1..6451bade 100644
--- a/src/utils/cm-mode-imports.js
+++ b/src/utils/cm-mode-imports.js
@@ -28,3 +28,4 @@ require('codemirror/mode/swift/swift');
require('codemirror/mode/toml/toml');
require('codemirror/mode/yaml/yaml');
require('codemirror-graphql/mode');
+require('./modes/solidity');
diff --git a/src/utils/modes.js b/src/utils/modes.js
index 0f033975..7e78e88d 100644
--- a/src/utils/modes.js
+++ b/src/utils/modes.js
@@ -76,8 +76,8 @@ const modes = {
scala: ['clike', 'text/x-scala'],
scss: 'css',
sh: 'shell',
- sol: 'clike',
- solidity: 'clike',
+ sol: 'solidity',
+ solidity: 'solidity',
sql: ['sql', 'text/x-sql'],
sqlite: ['sql', 'text/x-sqlite'],
styl: 'css',
diff --git a/src/utils/modes/solidity.js b/src/utils/modes/solidity.js
new file mode 100644
index 00000000..f85fd7f5
--- /dev/null
+++ b/src/utils/modes/solidity.js
@@ -0,0 +1,599 @@
+/* eslint-disable operator-assignment */
+/* eslint-disable prefer-template */
+/* eslint-disable object-shorthand */
+/* eslint-disable no-return-assign */
+/* eslint-disable unicorn/no-unsafe-regex */
+/* eslint-disable no-unused-expressions */
+/* eslint-disable consistent-return */
+/* eslint-disable no-prototype-builtins */
+/* eslint-disable no-useless-escape */
+/* eslint-disable eqeqeq */
+/* eslint-disable no-use-before-define */
+/* eslint-disable global-require */
+
+/**
+ * This mode has been adapted from the source of `codemirror-solidity` as the way that library is
+ * structured makes it impossible for us load as an isolated mode for our Codemirror needs. The
+ * source is mostly presented as-is with the exception of a few areas adapted into to our coding
+ * standards (such as using `const` instead of `let` for immutable data).
+ *
+ * @license MIT
+ * @see {@link https://github.com/alincode/codemirror-solidity}
+ */
+(function (mod) {
+ if (typeof exports === 'object' && typeof module === 'object') {
+ // CommonJS
+ mod(require('codemirror/lib/codemirror'));
+ // eslint-disable-next-line no-undef
+ } else if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['codemirror/lib/codemirror'], mod); // eslint-disable-line no-undef
+ } else {
+ // Plain browser env
+ mod(CodeMirror); // eslint-disable-line no-undef
+ }
+})(function (CodeMirror) {
+ CodeMirror.defineMode('solidity', function (config) {
+ const indentUnit = config.indentUnit;
+
+ // let functionKeyword = 'function'
+ // let functionNameKeyword = 'Name'
+ // let leftBracketSign = '('
+ // let rightBracketSign = ')'
+ // let functionVariableName = 'variable'
+ // let keyWordContract = 'contract'
+
+ const 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,
+ };
+
+ const keywordsSpecial = {
+ pragma: true,
+ returns: true,
+ address: true,
+ contract: true,
+ function: true,
+ struct: true,
+ };
+
+ const keywordsEtherUnit = {
+ wei: true,
+ szabo: true,
+ finney: true,
+ ether: true,
+ };
+ const keywordsTimeUnit = {
+ seconds: true,
+ minutes: true,
+ hours: true,
+ days: true,
+ weeks: true,
+ };
+ const keywordsBlockAndTransactionProperties = {
+ block: ['coinbase', 'difficulty', 'gaslimit', 'number', 'timestamp'],
+ msg: ['data', 'sender', 'sig', 'value'],
+ tx: ['gasprice', 'origin'],
+ };
+ const keywordsMoreBlockAndTransactionProperties = {
+ now: true,
+ gasleft: true,
+ blockhash: true,
+ };
+ const keywordsErrorHandling = {
+ assert: true,
+ require: true,
+ revert: true,
+ throw: true,
+ };
+ const keywordsMathematicalAndCryptographicFuctions = {
+ addmod: true,
+ mulmod: true,
+ keccak256: true,
+ sha256: true,
+ ripemd160: true,
+ ecrecover: true,
+ };
+ const keywordsContractRelated = {
+ this: true,
+ selfdestruct: true,
+ super: true,
+ };
+ const keywordsTypeInformation = { type: true };
+ const keywordsContractList = {};
+
+ const keywordsControlStructures = {
+ if: true,
+ else: true,
+ while: true,
+ do: true,
+ for: true,
+ break: true,
+ continue: true,
+ switch: true,
+ case: true,
+ default: true,
+ };
+
+ const keywordsValueTypes = {
+ bool: true,
+ byte: true,
+ string: true,
+ enum: true,
+ address: true,
+ };
+
+ const 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,
+ };
+
+ const keywordsAbiEncodeDecodeFunctions = {
+ abi: ['decode', 'encodePacked', 'encodeWithSelector', 'encodeWithSignature', 'encode'],
+ };
+
+ const keywordsMembersOfAddressType = ['transfer', 'send', 'balance', 'call', 'delegatecall', 'staticcall'];
+
+ const natSpecTags = ['title', 'author', 'notice', 'dev', 'param', 'return'];
+
+ // let functionStructureStage = [{
+ // function: ['function', 'returns']
+ // },
+ // leftBracketSign,
+ // parameterName,
+ // parameterValue,
+ // rightBracketSign,
+ // ];
+
+ const 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
+ };
+
+ const isOperatorChar = /[+\-*&^%:=<>!|\/~]/;
+ const 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]/);
+
+ const 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';
+
+ const 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;
+ let next;
+ let 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;
+ let 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;
+ const 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;
+ const 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;
+ const 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;
+ let ch;
+ let hexValue = '';
+ let 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';
+ }
+
+ 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;
+ const 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) {
+ const 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;
+ const 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;
+ const ctx = state.context;
+ const firstChar = textAfter && textAfter.charAt(0);
+ if (ctx.type == 'case' && /^(?:case|default)\b/.test(textAfter)) {
+ state.context.type = '}';
+ return ctx.indented;
+ }
+ const closing = firstChar == ctx.type;
+ if (ctx.align) return ctx.column + (closing ? 0 : 1);
+ return ctx.indented + (closing ? 0 : indentUnit);
+ },
+
+ electricChars: '{}):',
+ closeBrackets: '()[]{}\'\'""``',
+ fold: 'brace',
+ blockCommentStart: '/*',
+ blockCommentEnd: '*/',
+ lineComment: '//',
+ };
+ });
+
+ CodeMirror.defineMIME('text/x-solidity', 'solidity');
+});