diff --git a/.github/workflows/run-sut.yml b/.github/workflows/run-sut.yml new file mode 100644 index 0000000000..e6b8d98221 --- /dev/null +++ b/.github/workflows/run-sut.yml @@ -0,0 +1,20 @@ + +name: Running Solidity Unit Tests +on: [push] + +jobs: + run_sol_contracts_job: + runs-on: ubuntu-latest + name: A job to run solidity unit tests on github actions CI + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Environment Setup + uses: actions/setup-node@v3 + with: + node-version: 14.17.6 + - name: Run SUT Action + uses: EthereumRemix/sol-test@v1 + with: + test-path: 'apps/remix-ide/contracts/tests' + compiler-version: '0.8.15' \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts index d30c942b59..171ad97b6a 100644 --- a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts @@ -12,6 +12,15 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', false) }, + + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, + 'Should add test and base files #group1': function (browser: NightwatchBrowser) { browser.addFile(examples.testContract.name, examples.testContract) .addFile(examples.baseContract.name, examples.baseContract) diff --git a/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts b/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts index db86224b11..f9f5eef0c4 100644 --- a/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts @@ -18,6 +18,14 @@ module.exports = { init(browser, done, 'http://127.0.0.1:8080', false) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, + 'Should load the test file': function (browser: NightwatchBrowser) { browser.openFile('contracts') .openFile('contracts/3_Ballot.sol') diff --git a/apps/remix-ide-e2e/src/tests/editorReferences.test.ts b/apps/remix-ide-e2e/src/tests/editorReferences.test.ts index 86856a9822..f63837233e 100644 --- a/apps/remix-ide-e2e/src/tests/editorReferences.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorReferences.test.ts @@ -20,6 +20,13 @@ module.exports = { init(browser, done, 'http://127.0.0.1:8080', false) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, 'Should load the test file': function (browser: NightwatchBrowser) { browser.openFile('contracts') .openFile('contracts/3_Ballot.sol') diff --git a/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts index 8d8cba54e0..daa5456982 100644 --- a/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts +++ b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts @@ -8,6 +8,13 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', true) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, 'Should add error marker': function (browser: NightwatchBrowser) { browser .openFile('contracts') diff --git a/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts index afff1c8f28..24eb0540e1 100644 --- a/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts +++ b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts @@ -8,6 +8,13 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', true) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, 'Should add line texts': function (browser: NightwatchBrowser) { browser .openFile('contracts') diff --git a/apps/remix-ide-e2e/src/tests/erc721.test.ts b/apps/remix-ide-e2e/src/tests/erc721.test.ts index b759d457a8..52535ebf3a 100644 --- a/apps/remix-ide-e2e/src/tests/erc721.test.ts +++ b/apps/remix-ide-e2e/src/tests/erc721.test.ts @@ -26,20 +26,17 @@ module.exports = { .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC721.sol"]') - .openFile('contracts/SampleERC721.sol') - .verifyContracts(['SampleERC721']) + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') + .openFile('contracts/MyToken.sol') + .verifyContracts(['MyToken']) // deploy contract .clickLaunchIcon('udapp') - .selectContract('SampleERC721') - .createContract('E,E') + .selectContract('MyToken') + .createContract('') .testFunction('last', { status: 'true Transaction mined and execution succeed', - 'decoded input': { - 'string tokenName': 'E', - 'string tokenSymbol': 'E' - } + 'decoded input': {} }).end() } } diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 3e15a18248..477ed83b7b 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -124,7 +124,7 @@ module.exports = { .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC20.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed @@ -156,7 +156,7 @@ module.exports = { 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/SampleERC20_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]') }, 'Should create ERC721 workspace with files #group1': function (browser: NightwatchBrowser) { @@ -172,7 +172,7 @@ module.exports = { .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC721.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed @@ -204,7 +204,7 @@ module.exports = { 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/SampleERC721_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]') }, // WORKSPACE TEMPLATES E2E END diff --git a/apps/remix-ide/contracts/ballot.sol b/apps/remix-ide/contracts/ballot.sol index d55734ed9a..ffcc6c3609 100644 --- a/apps/remix-ide/contracts/ballot.sol +++ b/apps/remix-ide/contracts/ballot.sol @@ -1,10 +1,13 @@ -pragma solidity ^0.4.0; +// SPDX-License-Identifier: GPL-3.0 -/// @title Voting with delegation. +pragma solidity >=0.7.0 <0.9.0; + +/** + * @title Ballot + * @dev Implements voting process along with vote 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 @@ -12,33 +15,31 @@ contract Ballot { uint vote; // index of the voted proposal } - // This is a type for a single proposal. struct Proposal { + // If you can limit the length to a certain number of bytes, + // always use one of bytes1 to bytes32 because they are much cheaper 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\`. - function Ballot(bytes32[] proposalNames) { + /** + * @dev Create a new ballot to choose one of 'proposalNames'. + * @param proposalNames names of proposals + */ + 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\`. + // 'Proposal({...})' creates a temporary + // Proposal object and 'proposals.push(...)' + // appends it to the end of 'proposals'. proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 @@ -46,98 +47,92 @@ contract Ballot { } } - // Give \`voter\` the right to vote on this ballot. - // May only be called by \`chairperson\`. - function giveRightToVote(address voter) { - if (msg.sender != chairperson || voters[voter].voted) { - // \`throw\` terminates and reverts all changes to - // the state and to Ether balances. It is often - // a good idea to use this if functions are - // called incorrectly. But watch out, this - // will also consume all provided gas. - throw; - } + /** + * @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'. + * @param voter address of voter + */ + function giveRightToVote(address voter) public { + 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) { - // assigns reference - Voter sender = voters[msg.sender]; - if (sender.voted) - throw; - - // 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) && - voters[to].delegate != msg.sender - ) { + /** + * @dev Delegate your vote to the voter 'to'. + * @param to address to which vote is delegated + */ + function delegate(address to) public { + Voter storage sender = voters[msg.sender]; + require(!sender.voted, "You already voted."); + require(to != msg.sender, "Self-delegation is disallowed."); + + while (voters[to].delegate != address(0)) { to = voters[to].delegate; - } - // We found a loop in the delegation, not allowed. - if (to == msg.sender) { - throw; + // We found a loop in the delegation, not allowed. + require(to != msg.sender, "Found loop in delegation."); } - - // Since \`sender\` is a reference, this - // modifies \`voters[msg.sender].voted\` sender.voted = true; sender.delegate = to; - Voter delegate = voters[to]; - if (delegate.voted) { + Voter storage delegate_ = voters[to]; + if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes - proposals[delegate.vote].voteCount += sender.weight; + proposals[delegate_.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. - delegate.weight += sender.weight; + delegate_.weight += sender.weight; } } - /// Give your vote (including votes delegated to you) - /// to proposal \`proposals[proposal].name\`. - function vote(uint proposal) { - Voter sender = voters[msg.sender]; - if (sender.voted) - throw; + /** + * @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'. + * @param proposal index of proposal in the proposals array + */ + function vote(uint proposal) public { + 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, + // 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() constant - returns (uint winningProposal) + /** + * @dev Computes the winning proposal taking all previous votes into account. + * @return winningProposal_ index of winning proposal in the proposals array + */ + 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; + winningProposal_ = p; } } } - - // 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() constant - returns (bytes32 winnerName) + + /** + * @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then + * @return winnerName_ the name of the winner + */ + function winnerName() public view + returns (bytes32 winnerName_) { - winnerName = proposals[winningProposal()].name; + winnerName_ = proposals[winningProposal()].name; } -} +} \ No newline at end of file diff --git a/apps/remix-ide/contracts/tests/Ballot_test.sol b/apps/remix-ide/contracts/tests/Ballot_test.sol new file mode 100644 index 0000000000..452a5433b4 --- /dev/null +++ b/apps/remix-ide/contracts/tests/Ballot_test.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_tests.sol"; // this import is automatically injected by Remix. +import "hardhat/console.sol"; +import "../ballot.sol"; + +contract BallotTest { + + bytes32[] proposalNames; + + Ballot ballotToTest; + function beforeAll () public { + proposalNames.push(bytes32("candidate1")); + ballotToTest = new Ballot(proposalNames); + } + + function checkWinningProposal () public { + console.log("Running checkWinningProposal"); + ballotToTest.vote(0); + Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal"); + Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name"); + } + + function checkWinninProposalWithReturnValue () public view returns (bool) { + return ballotToTest.winningProposal() == 0; + } +} \ No newline at end of file diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 0a4f982fa4..b001164e9c 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -354,10 +354,6 @@ class AppComponent { const queryParams = new QueryParams() const params = queryParams.get() - if (isElectron()) { - this.appManager.activatePlugin('remixd') - } - try { this.engine.register(await this.appManager.registeredPlugins()) } catch (e) { diff --git a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx index c9caf3b70c..b763f81a3c 100644 --- a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx +++ b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx @@ -87,6 +87,18 @@ export class CodeParser extends Plugin { } } + async handleChangeEvents() { + const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') + if (completionSettings) { + await this.antlrService.getCurrentFileAST() + } + const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') + const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') + if(showGasSettings || showErrorSettings || completionSettings) { + await this.compilerService.compile() + } + } + async onActivation() { this.gasService = new CodeParserGasService(this) @@ -102,8 +114,7 @@ export class CodeParser extends Plugin { this.on('editor', 'didChangeFile', async (file) => { await this.call('editor', 'discardLineTexts') - await this.antlrService.getCurrentFileAST() - await this.compilerService.compile() + await this.handleChangeEvents() }) this.on('filePanel', 'setWorkspace', async () => { @@ -113,8 +124,7 @@ export class CodeParser extends Plugin { this.on('fileManager', 'currentFileChanged', async () => { await this.call('editor', 'discardLineTexts') - await this.antlrService.getCurrentFileAST() - await this.compilerService.compile() + await this.handleChangeEvents() }) this.on('solidity', 'loadingCompiler', async (url) => { @@ -188,10 +198,10 @@ export class CodeParser extends Plugin { const index = {} const contractName: string = contractNode.name const callback = (node) => { - if(inScope && node.scope !== contractNode.id + if (inScope && node.scope !== contractNode.id && !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition')) return - if(inScope) node.isClassNode = true; + if (inScope) node.isClassNode = true; node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) node.functionName = node.name + this._getInputParams(node) node.contractName = contractName @@ -227,11 +237,11 @@ export class CodeParser extends Plugin { if ((node.scope && node.scope === baseContract.id) || node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' - ) { + ) { baseNodesWithBaseContractScope[node.id] = node } - if(node.members){ - for(const member of node.members){ + if (node.members) { + for (const member of node.members) { member.contractName = (baseContract as any).name member.contractId = (baseContract as any).id member.isBaseNode = true; @@ -249,7 +259,7 @@ export class CodeParser extends Plugin { if (node.nodeType === 'ImportDirective') { const imported = await this.resolveImports(node, {}) - + for (const importedNode of (Object.values(imported) as any)) { if (importedNode.nodes) for (const subNode of importedNode.nodes) { diff --git a/apps/remix-ide/src/assets/js/loader.js b/apps/remix-ide/src/assets/js/loader.js index ac49d44e3d..4e60dbc36d 100644 --- a/apps/remix-ide/src/assets/js/loader.js +++ b/apps/remix-ide/src/assets/js/loader.js @@ -45,7 +45,7 @@ function isElectron() { return false } -const versionUrl = isElectron() ? 'https://remix.ethereum.org/assets/version.json' : 'assets/version.json' +const versionUrl = 'assets/version.json' fetch(versionUrl, { cache: "no-store" }).then(response => { response.text().then(function (data) { const version = JSON.parse(data); diff --git a/libs/remix-lib/src/execution/txHelper.ts b/libs/remix-lib/src/execution/txHelper.ts index 0b50d96463..f3c78930b1 100644 --- a/libs/remix-lib/src/execution/txHelper.ts +++ b/libs/remix-lib/src/execution/txHelper.ts @@ -102,17 +102,25 @@ export function extractSize (type) { return size ? size[2] : '' } +export function getFunctionLiner (fn, detailTuple: boolean = true) { + /* + if detailsTuple is True, this will return something like fnName((uint, string)) + if detailsTuple is False, this will return something like fnName(tuple) + */ + return fn.name + '(' + fn.inputs.map((value) => { + if (detailTuple && value.components) { + const fullType = makeFullTypeDefinition(value) + return fullType.replace(/tuple/g, '') // return of makeFullTypeDefinition might contain `tuple`, need to remove it cause `methodIdentifier` (fnName) does not include `tuple` keyword + } else { + return value.type + } + }).join(',') + ')' +} + export function getFunction (abi, fnName) { for (let i = 0; i < abi.length; i++) { const fn = abi[i] - if (fn.type === 'function' && fnName === fn.name + '(' + fn.inputs.map((value) => { - if (value.components) { - const fullType = makeFullTypeDefinition(value) - return fullType.replace(/tuple/g, '') // return of makeFullTypeDefinition might contain `tuple`, need to remove it cause `methodIdentifier` (fnName) does not include `tuple` keyword - } else { - return value.type - } - }).join(',') + ')') { + if (fn.type === 'function' && (fnName === getFunctionLiner(fn, true) || fnName === getFunctionLiner(fn, false))) { return fn } } diff --git a/libs/remix-lib/test/txHelper.ts b/libs/remix-lib/test/txHelper.ts index 2532bcd34d..4d9a46426f 100644 --- a/libs/remix-lib/test/txHelper.ts +++ b/libs/remix-lib/test/txHelper.ts @@ -3,7 +3,7 @@ import tape from 'tape' import * as txHelper from '../src/execution/txHelper' tape('getFunction', function (st) { - st.plan(6) + st.plan(11) let fn = txHelper.getFunction(JSON.parse(abi), 'o((address,uint256))') st.equal(fn.name, 'o') @@ -21,6 +21,17 @@ tape('getFunction', function (st) { fn = txHelper.getReceiveInterface(JSON.parse(abi)) st.equal(fn.type, 'receive') + + fn = txHelper.getFunction(testTupleAbi, 'setUser(tuple)') // some compiler version might resolve to tuple. + st.equal(fn.name, 'setUser') + st.equal(fn.inputs[0].type, 'tuple') + st.equal(fn.inputs[0].name, 'user') + + fn = txHelper.getFunctionLiner(testTupleAbi[0], true) + st.equal(fn, 'setUser((string,uint256))') + + fn = txHelper.getFunctionLiner(testTupleAbi[0], false) + st.equal(fn, 'setUser(tuple)') }) const abi = `[ @@ -153,3 +164,5 @@ const abi = `[ "type": "receive" } ]` + +const testTupleAbi = [{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"age","type":"uint256"}],"internalType":"struct Example.User","name":"user","type":"tuple"}],"name":"setUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userByAddress","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"age","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css b/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css index 8c321b9145..d177ce8bcf 100644 --- a/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css +++ b/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css @@ -8,7 +8,7 @@ pre { overflow-x: auto; } .remixIDE { - width : 100vw; + width : 100%; height : 100vh; overflow : hidden; flex-direction : row; diff --git a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts index 4c5285e9ef..a3dd0f05e8 100644 --- a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts +++ b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts @@ -148,6 +148,27 @@ export function getCompletionSnippets(range: IRange, monaco): monaco.languages.C insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, range }, + { + label: 'while loop', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'while (${1:condition}) \n{\n\t${2:code}\n};', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'do while loop', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'do {\n\t${2:code}\n} \nwhile (${1:condition});', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'for loop', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'for (${1:init}; ${2:condition}; ${3:increment}) \n{\n\t${4:code}\n};', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, { label: 'pragma', kind: monaco.languages.CompletionItemKind.Snippet, diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 0a24d3b038..b31313726d 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -41,13 +41,13 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { if (javascriptVM === null || javascriptVM === undefined) ethereumVM(props.config, true, dispatch) const useAutoComplete = props.config.get('settings/auto-completion') - if (useAutoComplete === null || useAutoComplete === undefined) useAutoCompletion(props.config, true, dispatch) + if (useAutoComplete === null || useAutoComplete === undefined) useAutoCompletion(props.config, false, dispatch) const displayErrors = props.config.get('settings/display-errors') - if (displayErrors === null || displayErrors === undefined) useDisplayErrors(props.config, true, dispatch) + if (displayErrors === null || displayErrors === undefined) useDisplayErrors(props.config, false, dispatch) const useShowGas = props.config.get('settings/show-gas') - if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, true, dispatch) + if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, false, dispatch) } useEffect(() => initValue(), [resetState, props.config]) useEffect(() => initValue(), []) @@ -148,9 +148,10 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { const isEditorWrapChecked = props.config.get('settings/text-wrap') || false const isPersonalChecked = props.config.get('settings/personal-mode') || false const isMatomoChecked = props.config.get('settings/matomo-analytics') || false - const isAutoCompleteChecked = props.config.get('settings/auto-completion') === null ? true:props.config.get('settings/auto-completion') - const isShowGasInEditorChecked = props.config.get('settings/show-gas') === null ? true:props.config.get('settings/show-gas') - const displayErrorsChecked = props.config.get('settings/display-errors') === null ? true:props.config.get('settings/display-errors') + + const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false + const isShowGasInEditorChecked = props.config.get('settings/show-gas') || false + const displayErrorsChecked = props.config.get('settings/display-errors') || false return (