Merge branch 'master' of https://github.com/ethereum/remix-project into parserfix

pull/5370/head
filip mertens 2 years ago
commit b492a0eef8
  1. 5
      .circleci/config.yml
  2. 19
      apps/remix-ide-e2e/src/commands/addFile.ts
  3. 4
      apps/remix-ide-e2e/src/helpers/init.ts
  4. 6
      apps/remix-ide-e2e/src/tests/generalSettings.test.ts
  5. 124
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  6. 32
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  7. 120
      apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts
  8. 82
      apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts
  9. 8
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  10. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  11. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  12. 2
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  13. 7
      apps/remix-ide/webpack.config.js
  14. 2
      libs/remix-solidity/src/compiler/compiler.ts
  15. 99
      libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts
  16. 198
      libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt
  17. 100
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts
  18. 18
      libs/remix-ui/editor/src/lib/providers/definitionProvider.ts
  19. 4
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  20. 2
      libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx
  21. 2
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx
  22. 2
      libs/remix-ui/search/src/index.ts
  23. 4
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  24. 2
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  25. 2
      libs/remix-ui/workspace/src/lib/actions/events.ts
  26. 14
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  27. 2
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  28. 2
      libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
  29. 4
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  30. 116
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  31. 4
      libs/remix-ws-templates/src/index.ts
  32. 26
      libs/remix-ws-templates/src/templates/ozerc1155/index.ts
  33. 10
      libs/remix-ws-templates/src/templates/ozerc1155/scripts/deploy_with_ethers.ts
  34. 10
      libs/remix-ws-templates/src/templates/ozerc1155/scripts/deploy_with_web3.ts
  35. 29
      libs/remix-ws-templates/src/templates/ozerc1155/scripts/ethers-lib.ts
  36. 36
      libs/remix-ws-templates/src/templates/ozerc1155/scripts/web3-lib.ts
  37. 19
      libs/remix-ws-templates/src/templates/ozerc1155/tests/MyToken_test.sol
  38. 22
      libs/remix-ws-templates/src/templates/ozerc20/index.ts
  39. 22
      libs/remix-ws-templates/src/templates/ozerc721/index.ts
  40. 6
      package.json
  41. 15
      yarn.lock

@ -493,10 +493,10 @@ jobs:
working_directory: ~/remix-project working_directory: ~/remix-project
steps: steps:
- checkout
- node/install: - node/install:
install-yarn: true install-yarn: true
node-version: "v14.17.6" node-version: "v14.17.6"
- checkout
- run: yarn - run: yarn
- run: yarn run downloadsolc_assets - run: yarn run downloadsolc_assets
- run: yarn run build:production - run: yarn run build:production
@ -526,6 +526,9 @@ jobs:
steps: steps:
- checkout - checkout
- node/install:
install-yarn: true
node-version: "v14.17.6"
- run: yarn - run: yarn
- run: yarn run downloadsolc_assets - run: yarn run downloadsolc_assets
- run: yarn run build:production - run: yarn run build:production

@ -2,7 +2,7 @@ import { NightwatchBrowser, NightwatchContractContent } from 'nightwatch'
import EventEmitter from 'events' import EventEmitter from 'events'
class AddFile extends EventEmitter { class AddFile extends EventEmitter {
command (this: NightwatchBrowser, name: string, content: NightwatchContractContent): NightwatchBrowser { command(this: NightwatchBrowser, name: string, content: NightwatchContractContent): NightwatchBrowser {
this.api.perform((done) => { this.api.perform((done) => {
addFile(this.api, name, content, () => { addFile(this.api, name, content, () => {
done() done()
@ -14,8 +14,21 @@ class AddFile extends EventEmitter {
} }
function addFile (browser: NightwatchBrowser, name: string, content: NightwatchContractContent, done: VoidFunction) { function addFile (browser: NightwatchBrowser, name: string, content: NightwatchContractContent, done: VoidFunction) {
browser.clickLaunchIcon('udapp') browser
.clickLaunchIcon('filePanel') .saveScreenshot('./reports/screenshots/addFile.png')
.isVisible({
selector: "//*[@data-id='sidePanelSwapitTitle' and contains(.,'File explorer')]",
locateStrategy: 'xpath',
suppressNotFoundErrors: true,
timeout: 1000
}, (okVisible) => {
if (!okVisible.value) {
browser.clickLaunchIcon('filePanel')
.saveScreenshot('./reports/screenshots/addFile2.png')
}
})
.scrollInto('li[data-id="treeViewLitreeViewItemREADME.txt"]')
.saveScreenshot('./reports/screenshots/addFile3.png')
.waitForElementVisible('li[data-id="treeViewLitreeViewItemREADME.txt"]') .waitForElementVisible('li[data-id="treeViewLitreeViewItemREADME.txt"]')
.click('li[data-id="treeViewLitreeViewItemREADME.txt"]').pause(1000) // focus on root directory .click('li[data-id="treeViewLitreeViewItemREADME.txt"]').pause(1000) // focus on root directory
.elements('css selector', `li[data-id="treeViewLitreeViewItem${name}"]`, (res) => { .elements('css selector', `li[data-id="treeViewLitreeViewItem${name}"]`, (res) => {

@ -24,8 +24,8 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
.pause(6000) .pause(6000)
.perform(done()) .perform(done())
}) })
.maximizeWindow() //.maximizeWindow()
.fullscreenWindow(() => { .perform(() => {
if (preloadPlugins) { if (preloadPlugins) {
initModules(browser, () => { initModules(browser, () => {
browser.pause(2000).clickLaunchIcon('solidity') browser.pause(2000).clickLaunchIcon('solidity')

@ -163,7 +163,7 @@ const remixIdeThemes = {
}, },
flatly: { flatly: {
primary: '#2C3E50', primary: '#2C3E50',
secondary: '#95a5a6', secondary: '#dadfe0',
success: '#18BC9C', success: '#18BC9C',
info: '#3498DB', info: '#3498DB',
warning: '#F39C12', warning: '#F39C12',
@ -171,7 +171,7 @@ const remixIdeThemes = {
}, },
spacelab: { spacelab: {
primary: '#446E9B', primary: '#446E9B',
secondary: '#999', secondary: '#dadfe0',
success: '#3CB521', success: '#3CB521',
info: '#3399F3', info: '#3399F3',
warning: '#D47500', warning: '#D47500',
@ -179,7 +179,7 @@ const remixIdeThemes = {
}, },
cyborg: { cyborg: {
primary: '#2A9FD6', primary: '#2A9FD6',
secondary: '#555', secondary: '#3c3939',
success: '#77B300', success: '#77B300',
info: '#93C', info: '#93C',
warning: '#F80', warning: '#F80',

@ -66,7 +66,7 @@ module.exports = {
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(2000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content') 'Incorrect content')
@ -129,28 +129,28 @@ module.exports = {
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
// check js and ts files are not transformed // check js and ts files are not transformed
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
.pause(5000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content') 'Incorrect content')
@ -177,28 +177,76 @@ module.exports = {
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
// check js and ts files are not transformed // check js and ts files are not transformed
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]')
},
'Should create ERC1155 workspace with files #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="workspaceCreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc1155' })
.click('select[id="wstemplate"]')
.click('select[id="wstemplate"] option[value=ozerc1155]')
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
.pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
.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
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
.pause(1000)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(4000) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content') 'Incorrect content')
@ -207,6 +255,64 @@ module.exports = {
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]')
}, },
'Should create ERC1155 workspace with template customizations #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="workspaceCreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
.click('select[id="wstemplate"]')
.click('select[id="wstemplate"] option[value=ozerc1155]')
.waitForElementPresent('*[data-id="ozCustomization"]')
.click('*[data-id="featureTypeMintable"]')
.click('*[data-id="featureTypeBurnable"]')
.click('*[data-id="featureTypePausable"]')
.click('*[data-id="upgradeTypeUups"]')
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
.pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]')
.click('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]')
.pause(1000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`contract MyToken is Initializable, ERC1155Upgradeable, OwnableUpgradeable, PausableUpgradeable, ERC1155BurnableUpgradeable, UUPSUpgradeable {`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
// check js and ts files are not transformed
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1,
'Incorrect content')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content')
browser.assert.ok(content.indexOf(`gas: gas || 3600000`) !== -1,
'Incorrect gas cost')
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(100)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content')
})
// No test file is added in upgradeable contract template
},
// WORKSPACE TEMPLATES E2E END // WORKSPACE TEMPLATES E2E END
'Should create two workspace and switch to the first one #group1': function (browser: NightwatchBrowser) { 'Should create two workspace and switch to the first one #group1': function (browser: NightwatchBrowser) {

@ -6,6 +6,7 @@ import { CompilationResult } from '@remix-project/remix-solidity'
import CodeParserGasService from './services/code-parser-gas-service' import CodeParserGasService from './services/code-parser-gas-service'
import CodeParserCompiler from './services/code-parser-compiler' import CodeParserCompiler from './services/code-parser-compiler'
import CodeParserAntlrService from './services/code-parser-antlr-service' import CodeParserAntlrService from './services/code-parser-antlr-service'
import CodeParserImports, { CodeParserImportsData } from './services/code-parser-imports'
import React from 'react' import React from 'react'
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
import { ContractDefinitionAstNode, EventDefinitionAstNode, FunctionCallAstNode, FunctionDefinitionAstNode, IdentifierAstNode, ImportDirectiveAstNode, ModifierDefinitionAstNode, SourceUnitAstNode, StructDefinitionAstNode, VariableDeclarationAstNode } from 'dist/libs/remix-analyzer/src/types' import { ContractDefinitionAstNode, EventDefinitionAstNode, FunctionCallAstNode, FunctionDefinitionAstNode, IdentifierAstNode, ImportDirectiveAstNode, ModifierDefinitionAstNode, SourceUnitAstNode, StructDefinitionAstNode, VariableDeclarationAstNode } from 'dist/libs/remix-analyzer/src/types'
@ -15,7 +16,7 @@ import { ParseResult } from './types/antlr-types'
const profile: Profile = { const profile: Profile = {
name: 'codeParser', name: 'codeParser',
methods: ['nodesAtPosition', 'getContractNodes', 'getCurrentFileNodes', 'getLineColumnOfNode', 'getLineColumnOfPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getANTLRBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'getGasEstimates'], methods: ['nodesAtPosition', 'getContractNodes', 'getCurrentFileNodes', 'getLineColumnOfNode', 'getLineColumnOfPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getANTLRBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'getGasEstimates', 'getImports'],
events: [], events: [],
version: '0.0.1' version: '0.0.1'
} }
@ -68,12 +69,15 @@ export class CodeParser extends Plugin {
gasService: CodeParserGasService gasService: CodeParserGasService
compilerService: CodeParserCompiler compilerService: CodeParserCompiler
antlrService: CodeParserAntlrService antlrService: CodeParserAntlrService
importService: CodeParserImports
parseSolidity: (text: string) => Promise<antlr.ParseResult> parseSolidity: (text: string) => Promise<antlr.ParseResult>
getLastNodeInLine: (ast: string) => Promise<any> getLastNodeInLine: (ast: string) => Promise<any>
listAstNodes: () => Promise<any> listAstNodes: () => Promise<any>
getANTLRBlockAtPosition: (position: any, text?: string) => Promise<any> getANTLRBlockAtPosition: (position: any, text?: string) => Promise<any>
setCurrentFileAST: (text?: string) => Promise<ParseResult> setCurrentFileAST: (text?: string) => Promise<ParseResult>
getImports: () => Promise<CodeParserImportsData[]>
constructor(astWalker: any) { constructor(astWalker: any) {
super(profile) super(profile)
@ -104,13 +108,14 @@ export class CodeParser extends Plugin {
this.gasService = new CodeParserGasService(this) this.gasService = new CodeParserGasService(this)
this.compilerService = new CodeParserCompiler(this) this.compilerService = new CodeParserCompiler(this)
this.antlrService = new CodeParserAntlrService(this) this.antlrService = new CodeParserAntlrService(this)
this.importService = new CodeParserImports(this)
this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService) this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService)
this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService) this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService)
this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService) this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService)
this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.bind(this.antlrService) this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.bind(this.antlrService)
this.setCurrentFileAST = this.antlrService.setCurrentFileAST.bind(this.antlrService) this.setCurrentFileAST = this.antlrService.setCurrentFileAST.bind(this.antlrService)
this.getImports = this.importService.getImports.bind(this.importService)
this.on('editor', 'didChangeFile', async (file) => { this.on('editor', 'didChangeFile', async (file) => {
await this.call('editor', 'discardLineTexts') await this.call('editor', 'discardLineTexts')
@ -119,7 +124,24 @@ export class CodeParser extends Plugin {
this.on('filePanel', 'setWorkspace', async () => { this.on('filePanel', 'setWorkspace', async () => {
await this.call('fileDecorator', 'clearFileDecorators') await this.call('fileDecorator', 'clearFileDecorators')
await this.importService.setFileTree()
})
this.on('fileManager', 'fileAdded', async () => {
await this.importService.setFileTree()
})
this.on('fileManager', 'fileRemoved', async () => {
await this.importService.setFileTree()
})
this.on('fileManager', 'fileAdded', async () => {
await this.importService.setFileTree()
}) })
this.on('fileManager', 'fileRemoved', async () => {
await this.importService.setFileTree()
})
this.on('fileManager', 'currentFileChanged', async () => { this.on('fileManager', 'currentFileChanged', async () => {
@ -139,8 +161,6 @@ export class CodeParser extends Plugin {
} }
/** /**
* *
* @returns * @returns
@ -149,10 +169,6 @@ export class CodeParser extends Plugin {
return this.compilerAbstract return this.compilerAbstract
} }
getSubNodes<T extends genericASTNode>(node: T): number[] { getSubNodes<T extends genericASTNode>(node: T): number[] {
return node.nodeType == "ContractDefinition" && node.contractDependencies; return node.nodeType == "ContractDefinition" && node.contractDependencies;
} }

@ -8,22 +8,23 @@ import { fileDecoration, fileDecorationType } from '@remix-ui/file-decorators'
import { sourceMappingDecoder } from '@remix-project/remix-debug' import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { CompilerRetriggerMode } from '@remix-project/remix-solidity-ts'; import { CompilerRetriggerMode } from '@remix-project/remix-solidity-ts';
import { MarkerSeverity } from 'monaco-editor'; import { MarkerSeverity } from 'monaco-editor';
import { findLinesInStringWithMatch, SearchResultLine } from '@remix-ui/search'
type errorMarker = { type errorMarker = {
message: string message: string
severity: MarkerSeverity severity: MarkerSeverity
position: { position: {
start: { start: {
line: number line: number
column: number column: number
}, },
end: { end: {
line: number line: number
column: number column: number
} }
}, },
file: string file: string
} }
export default class CodeParserCompiler { export default class CodeParserCompiler {
plugin: CodeParser plugin: CodeParser
compiler: any // used to compile the current file seperately from the main compiler compiler: any // used to compile the current file seperately from the main compiler
@ -43,36 +44,47 @@ export default class CodeParserCompiler {
this.errorState = true this.errorState = true
const result = new CompilerAbstract('soljson', data, source, input) const result = new CompilerAbstract('soljson', data, source, input)
let allErrors: errorMarker[] = [] let allErrors: errorMarker[] = []
if (data.errors) { if (data.errors || data.error) {
const sources = result.getSourceCode().sources const file = await this.plugin.call('fileManager', 'getCurrentFile')
for (const error of data.errors) { const currentFileContent = await this.plugin.call('fileManager', 'readFile', file)
const sources = result.getSourceCode().sources || []
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content) if (data.error) {
const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({ if (data.error.formattedMessage) {
start: error.sourceLocation.start, // mark this file as error
length: error.sourceLocation.end - error.sourceLocation.start const errorMarker = await this.createErrorMarker(data.error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } })
}, lineBreaks) allErrors = [...allErrors, errorMarker]
}
const filePath = error.sourceLocation.file } else {
for (const error of data.errors) {
allErrors = [...allErrors, { if (!error.sourceLocation) {
message: error.formattedMessage, // mark this file as error
severity: error.severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning, const errorMarker = await this.createErrorMarker(error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } })
position: { allErrors = [...allErrors, errorMarker]
start: { } else {
line: ((lineColumn.start && lineColumn.start.line) || 0) + 1, const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content)
column: ((lineColumn.start && lineColumn.start.column) || 0) + 1 const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({
}, start: error.sourceLocation.start,
end: { length: error.sourceLocation.end - error.sourceLocation.start
line: ((lineColumn.end && lineColumn.end.line) || 0) + 1, }, lineBreaks)
column: ((lineColumn.end && lineColumn.end.column) || 0) + 1
const filePath = error.sourceLocation.file
const fileTarget = await this.plugin.call('fileManager', 'getUrlFromPath', filePath)
const importFilePositions = await this.getPositionForImportErrors(fileTarget.file, currentFileContent)
for (const importFilePosition of importFilePositions) {
for (const line of importFilePosition.lines) {
allErrors = [...allErrors, await this.createErrorMarker(error, file, line.position)]
}
} }
allErrors = [...allErrors, await this.createErrorMarker(error, filePath, lineColumn)]
} }
, file: filePath }
}]
} }
const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors')
if(displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors) if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors)
this.addDecorators(allErrors, sources) this.addDecorators(allErrors, sources)
} else { } else {
await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources) await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources)
@ -143,14 +155,14 @@ export default class CodeParserCompiler {
this.compiler.compile(sources, this.plugin.currentFile) this.compiler.compile(sources, this.plugin.currentFile)
} }
} catch (e) { } catch (e) {
// do nothing // do nothing
} }
} }
async addDecorators(allErrors: errorMarker[], sources: any) { async addDecorators(allErrors: errorMarker[], sources: any) {
const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors')
if(!displayErrors) return if (!displayErrors) return
const errorsPerFiles: {[fileName: string]: errorMarker[]} = {} const errorsPerFiles: { [fileName: string]: errorMarker[] } = {}
for (const error of allErrors) { for (const error of allErrors) {
if (!errorsPerFiles[error.file]) { if (!errorsPerFiles[error.file]) {
errorsPerFiles[error.file] = [] errorsPerFiles[error.file] = []
@ -164,7 +176,7 @@ export default class CodeParserCompiler {
} }
// sort errorPerFiles by error priority // sort errorPerFiles by error priority
const sortedErrorsPerFiles: {[fileName: string]: errorMarker[]} = {} const sortedErrorsPerFiles: { [fileName: string]: errorMarker[] } = {}
for (const fileName in errorsPerFiles) { for (const fileName in errorsPerFiles) {
const errors = errorsPerFiles[fileName] const errors = errorsPerFiles[fileName]
errors.sort((a, b) => { errors.sort((a, b) => {
@ -182,7 +194,7 @@ export default class CodeParserCompiler {
const decorator: fileDecoration = { const decorator: fileDecoration = {
path: fileTarget.file, path: fileTarget.file,
isDirectory: false, isDirectory: false,
fileStateType: errors[0].severity == MarkerSeverity.Error? fileDecorationType.Error : fileDecorationType.Warning, fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning,
fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning', fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning',
fileStateIconClass: '', fileStateIconClass: '',
fileStateIcon: '', fileStateIcon: '',
@ -213,8 +225,27 @@ export default class CodeParserCompiler {
} }
async createErrorMarker(error: any, filePath: string, lineColumn): Promise<errorMarker> {
return {
message: error.formattedMessage,
severity: error.severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning,
position: {
start: {
line: ((lineColumn.start && lineColumn.start.line) || 0) + 1,
column: ((lineColumn.start && lineColumn.start.column) || 0) + 1
},
end: {
line: ((lineColumn.end && lineColumn.end.line) || 0) + 1,
column: ((lineColumn.end && lineColumn.end.column) || 0) + 1
}
}
, file: filePath
}
}
async clearDecorators(sources: any) { async clearDecorators(sources: any) {
const decorators: fileDecoration[] = [] const decorators: fileDecoration[] = []
if (!sources) return
for (const fileName of Object.keys(sources)) { for (const fileName of Object.keys(sources)) {
const decorator: fileDecoration = { const decorator: fileDecoration = {
path: fileName, path: fileName,
@ -234,4 +265,13 @@ export default class CodeParserCompiler {
await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) await this.plugin.call('fileDecorator', 'setFileDecorators', decorators)
} }
async getPositionForImportErrors(importedFileName: string, text: string) {
const re = new RegExp(importedFileName, 'gi')
const result: SearchResultLine[] = findLinesInStringWithMatch(
text,
re
)
return result
}
} }

@ -0,0 +1,82 @@
'use strict'
import { CodeParser } from "../code-parser";
export type CodeParserImportsData= {
files?: string[],
modules?: string[],
packages?: string[],
}
export default class CodeParserImports {
plugin: CodeParser
data: CodeParserImportsData = {}
constructor(plugin: CodeParser) {
this.plugin = plugin
this.init()
}
async getImports(){
return this.data
}
async init() {
// @ts-ignore
const txt = await import('raw-loader!libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt')
this.data.modules = txt.default.split('\n')
.filter(x => x !== '')
.map(x => x.replace('./node_modules/', ''))
.filter(x => {
if(x.includes('@openzeppelin')) {
return !x.includes('mock')
}else{
return true
}
})
// get unique first words of the values in the array
this.data.packages = [...new Set(this.data.modules.map(x => x.split('/')[0]))]
}
setFileTree = async () => {
this.data.files = await this.getDirectory('/')
this.data.files = this.data.files.filter(x => x.endsWith('.sol') && !x.startsWith('.deps') && !x.startsWith('.git'))
}
getDirectory = async (dir: string) => {
let result = []
const files = await this.plugin.call('fileManager', 'readdir', dir)
const fileArray = this.normalize(files)
for (const fi of fileArray) {
if (fi) {
const type = fi.data.isDirectory
if (type === true) {
result = [...result, ...(await this.getDirectory(`${fi.filename}`))]
} else {
result = [...result, fi.filename]
}
}
}
return result
}
normalize = filesList => {
const folders = []
const files = []
Object.keys(filesList || {}).forEach(key => {
if (filesList[key].isDirectory) {
folders.push({
filename: key,
data: filesList[key]
})
} else {
files.push({
filename: key,
data: filesList[key]
})
}
})
return [...folders, ...files]
}
}

@ -25,7 +25,7 @@
--gray:#555; --gray:#555;
--gray-dark:#222; --gray-dark:#222;
--primary:#2a9fd6; --primary:#2a9fd6;
--secondary:#555; --secondary:#3c3939;
--success:#77b300; --success:#77b300;
--info:#93c; --info:#93c;
--warning:#f80; --warning:#f80;
@ -5595,7 +5595,7 @@ a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:
background-color:#2180ac!important background-color:#2180ac!important
} }
.bg-secondary { .bg-secondary {
background-color:#555!important background-color:#3c3939!important
} }
a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover { a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover {
background-color:#3c3c3c!important background-color:#3c3c3c!important
@ -8358,7 +8358,7 @@ a.text-warning:focus,a.text-warning:hover {
color:#b35f00!important color:#b35f00!important
} }
.text-danger { .text-danger {
color:#c00!important color:#ff4242!important
} }
a.text-danger:focus,a.text-danger:hover { a.text-danger:focus,a.text-danger:hover {
color:maroon!important color:maroon!important
@ -8594,7 +8594,7 @@ legend {
color:#555 color:#555
} }
.nav-pills .nav-link.active,.nav-tabs .nav-link.active { .nav-pills .nav-link.active,.nav-tabs .nav-link.active {
background-color:#2a9fd6 background-color:#034767
} }
.breadcrumb a { .breadcrumb a {
color:#fff color:#fff

@ -24,7 +24,7 @@
--gray:#95a5a6; --gray:#95a5a6;
--gray-dark:#343a40; --gray-dark:#343a40;
--primary:#2c3e50; --primary:#2c3e50;
--secondary:#95a5a6; --secondary:#dadfe0;
--success:#18bc9c; --success:#18bc9c;
--info:#3498db; --info:#3498db;
--warning:#f39c12; --warning:#f39c12;
@ -4460,7 +4460,7 @@ a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:
background-color:#1a252f!important background-color:#1a252f!important
} }
.bg-secondary { .bg-secondary {
background-color:#95a5a6!important background-color:#dadfe0!important
} }
a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover { a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover {
background-color:#798d8f!important background-color:#798d8f!important

@ -25,7 +25,7 @@
--gray:#777; --gray:#777;
--gray-dark:#333; --gray-dark:#333;
--primary:#446e9b; --primary:#446e9b;
--secondary:#999; --secondary:#dadfe0;
--success:#3cb521; --success:#3cb521;
--info:#3399f3; --info:#3399f3;
--warning:#d47500; --warning:#d47500;
@ -5596,7 +5596,7 @@ a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:
background-color:#345578!important background-color:#345578!important
} }
.bg-secondary { .bg-secondary {
background-color:#999!important background-color:#dadfe0!important
} }
a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover { a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover {
background-color:gray!important background-color:gray!important

@ -3641,7 +3641,7 @@ input[type="submit"].btn-block {
.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-item.show .nav-link,
.nav-tabs .nav-link.active { .nav-tabs .nav-link.active {
color: #fff; color: #fff;
background-color: var(--body-bg); background-color: #2a2c3f;
border-color: #3f4455; border-color: #3f4455;
} }
.nav-tabs .dropdown-menu { .nav-tabs .dropdown-menu {

@ -16,6 +16,13 @@ module.exports = config => {
const nxWebpackConfig = nxWebpack(config) const nxWebpackConfig = nxWebpack(config)
const webpackConfig = { const webpackConfig = {
...nxWebpackConfig, ...nxWebpackConfig,
resolve: {
...nxWebpackConfig.resolve,
alias: {
path: require.resolve("path-browserify"),
}
},
node: { node: {
fs: 'empty', fs: 'empty',
tls: 'empty', tls: 'empty',

@ -281,7 +281,7 @@ export class Compiler {
} }
switch (data.cmd) { switch (data.cmd) {
case 'versionLoaded': case 'versionLoaded':
if (data.data && data.license) this.onCompilerLoaded(data.data, data.license) if (data.data) this.onCompilerLoaded(data.data, data.license)
break break
case 'compiled': case 'compiled':
{ {

@ -1,5 +1,12 @@
import { IRange } from "monaco-editor"; import { IRange } from "monaco-editor";
import monaco from "../../../types/monaco"; import monaco from "../../../types/monaco";
import path from "path";
type CodeParserImportsData = {
files?: string[],
modules?: string[],
packages?: string[],
}
export function getStringCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] { export function getStringCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [ return [
@ -176,13 +183,6 @@ export function getCompletionSnippets(range: IRange, monaco): monaco.languages.C
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range range
}, },
{
label: 'import',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: 'import "${1:library}";',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range
},
{ {
label: 'SPDX-License-Identifier', label: 'SPDX-License-Identifier',
kind: monaco.languages.CompletionItemKind.Snippet, kind: monaco.languages.CompletionItemKind.Snippet,
@ -330,7 +330,7 @@ function CreateCompletionItem(label: string, kind: monaco.languages.CompletionIt
export function GetCompletionKeywords(range: IRange, monaco): monaco.languages.CompletionItem[] { export function GetCompletionKeywords(range: IRange, monaco): monaco.languages.CompletionItem[] {
const completionItems = []; const completionItems = [];
const keywords = ['modifier', 'mapping', 'break', 'continue', 'delete', 'else', 'for', const keywords = ['modifier', 'mapping', 'break', 'continue', 'delete', 'else', 'for',
'after', 'promise', 'alias', 'apply','auto', 'copyof', 'default', 'define', 'final', 'implements', 'after', 'promise', 'alias', 'apply', 'auto', 'copyof', 'default', 'define', 'final', 'implements',
'inline', 'let', 'macro', 'match', 'mutable', 'null', 'of', 'partial', 'reference', 'relocatable', 'inline', 'let', 'macro', 'match', 'mutable', 'null', 'of', 'partial', 'reference', 'relocatable',
'sealed', 'sizeof', 'static', 'supports', 'switch', 'typedef', 'sealed', 'sizeof', 'static', 'supports', 'switch', 'typedef',
'if', 'new', 'return', 'returns', 'while', 'using', 'emit', 'anonymous', 'indexed', 'if', 'new', 'return', 'returns', 'while', 'using', 'emit', 'anonymous', 'indexed',
@ -390,6 +390,89 @@ export function GeCompletionUnits(range: IRange, monaco): monaco.languages.Compl
return completionItems; return completionItems;
} }
export function GetImports(range: IRange
, monaco, data: CodeParserImportsData
, word: string
): monaco.languages.CompletionItem[] {
let list = []
if (!word.startsWith('@')) {
word = word.replace('"', '');
const nextPaths = [...new Set(data.files
.filter((item) => item.startsWith(word))
.map((item) => item.replace(word, '').split('/')[0]))]
list = [...list, ...nextPaths
.filter((item) => !item.endsWith('.sol'))
.map((item) => {
return {
kind: monaco.languages.CompletionItemKind.Folder,
range: range,
label: `${item}`,
insertText: `${item}`,
}
})]
list = [...list,
...data.files
.filter((item) => item.startsWith(word))
.map((item) => {
return {
kind: monaco.languages.CompletionItemKind.File,
range: range,
label: `${item}`,
insertText: `${item.replace(word, '')}`,
}
})]
}
if (word === '@' || word === '') {
list = [...list, ...data.packages.map((item) => {
return {
kind: monaco.languages.CompletionItemKind.Module,
range: range,
label: `${item}`,
insertText: word === '@' ? `${item.replace('@', '')}` : `${item}`,
}
})]
}
if (word.startsWith('@') && word.length > 1) {
const nextPaths = [...new Set(data.modules
.filter((item) => item.startsWith(word))
.map((item) => item.replace(word, '').split('/')[0]))]
list = [...list, ...nextPaths
.filter((item) => !item.endsWith('.sol'))
.map((item) => {
return {
kind: monaco.languages.CompletionItemKind.Folder,
range: range,
label: `${item}`,
insertText: `${item}`,
}
})]
list = [...list
, ...data.modules
.filter((item) => item.startsWith(word))
.map((item) => {
// remove the first part if it starts with @
let label = item;
if (label.startsWith('@')) {
label = label.substring(label.indexOf('/') + 1);
}
const filename = path.basename(label)
return {
kind: monaco.languages.CompletionItemKind.Reference,
range: range,
label: `${filename}: ${label}`,
insertText: `${item.replace(word, '')}`,
}
})
]
}
return list;
};
export function GetGlobalVariable(range: IRange, monaco): monaco.languages.CompletionItem[] { export function GetGlobalVariable(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [ return [
{ {

@ -0,0 +1,198 @@
./node_modules/@openzeppelin/contracts/access/AccessControl.sol
./node_modules/@openzeppelin/contracts/access/AccessControlCrossChain.sol
./node_modules/@openzeppelin/contracts/access/AccessControlEnumerable.sol
./node_modules/@openzeppelin/contracts/access/IAccessControl.sol
./node_modules/@openzeppelin/contracts/access/IAccessControlEnumerable.sol
./node_modules/@openzeppelin/contracts/access/Ownable.sol
./node_modules/@openzeppelin/contracts/crosschain/amb/CrossChainEnabledAMB.sol
./node_modules/@openzeppelin/contracts/crosschain/amb/LibAMB.sol
./node_modules/@openzeppelin/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol
./node_modules/@openzeppelin/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol
./node_modules/@openzeppelin/contracts/crosschain/arbitrum/LibArbitrumL1.sol
./node_modules/@openzeppelin/contracts/crosschain/arbitrum/LibArbitrumL2.sol
./node_modules/@openzeppelin/contracts/crosschain/CrossChainEnabled.sol
./node_modules/@openzeppelin/contracts/crosschain/errors.sol
./node_modules/@openzeppelin/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol
./node_modules/@openzeppelin/contracts/crosschain/optimism/LibOptimism.sol
./node_modules/@openzeppelin/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol
./node_modules/@openzeppelin/contracts/finance/PaymentSplitter.sol
./node_modules/@openzeppelin/contracts/finance/VestingWallet.sol
./node_modules/@openzeppelin/contracts/governance/compatibility/GovernorCompatibilityBravo.sol
./node_modules/@openzeppelin/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorPreventLateQuorum.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorProposalThreshold.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorSettings.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorTimelockCompound.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorVotes.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorVotesComp.sol
./node_modules/@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol
./node_modules/@openzeppelin/contracts/governance/extensions/IGovernorTimelock.sol
./node_modules/@openzeppelin/contracts/governance/Governor.sol
./node_modules/@openzeppelin/contracts/governance/IGovernor.sol
./node_modules/@openzeppelin/contracts/governance/TimelockController.sol
./node_modules/@openzeppelin/contracts/governance/utils/IVotes.sol
./node_modules/@openzeppelin/contracts/governance/utils/Votes.sol
./node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol
./node_modules/@openzeppelin/contracts/interfaces/draft-IERC2612.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1155.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1155MetadataURI.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1155Receiver.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1271.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1363Receiver.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1363Spender.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC165.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1820Implementer.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC1820Registry.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC20.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC20Metadata.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC2981.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC3156.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC4626.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC721.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC721Enumerable.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC721Metadata.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC721Receiver.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC777.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC777Recipient.sol
./node_modules/@openzeppelin/contracts/interfaces/IERC777Sender.sol
./node_modules/@openzeppelin/contracts/metatx/ERC2771Context.sol
./node_modules/@openzeppelin/contracts/metatx/MinimalForwarder.sol
./node_modules/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol
./node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol
./node_modules/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol
./node_modules/@openzeppelin/contracts/proxy/Clones.sol
./node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol
./node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol
./node_modules/@openzeppelin/contracts/proxy/Proxy.sol
./node_modules/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol
./node_modules/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol
./node_modules/@openzeppelin/contracts/proxy/utils/Initializable.sol
./node_modules/@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol
./node_modules/@openzeppelin/contracts/security/Pausable.sol
./node_modules/@openzeppelin/contracts/security/PullPayment.sol
./node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol
./node_modules/@openzeppelin/contracts/token/common/ERC2981.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/ERC1155.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/IERC1155.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol
./node_modules/@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol
./node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20VotesComp.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol
./node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
./node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol
./node_modules/@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol
./node_modules/@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol
./node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
./node_modules/@openzeppelin/contracts/token/ERC20/utils/TokenTimelock.sol
./node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/draft-ERC721Votes.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol
./node_modules/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
./node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol
./node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
./node_modules/@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol
./node_modules/@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol
./node_modules/@openzeppelin/contracts/token/ERC777/ERC777.sol
./node_modules/@openzeppelin/contracts/token/ERC777/IERC777.sol
./node_modules/@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol
./node_modules/@openzeppelin/contracts/token/ERC777/IERC777Sender.sol
./node_modules/@openzeppelin/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol
./node_modules/@openzeppelin/contracts/utils/Address.sol
./node_modules/@openzeppelin/contracts/utils/Arrays.sol
./node_modules/@openzeppelin/contracts/utils/Base64.sol
./node_modules/@openzeppelin/contracts/utils/Checkpoints.sol
./node_modules/@openzeppelin/contracts/utils/Context.sol
./node_modules/@openzeppelin/contracts/utils/Counters.sol
./node_modules/@openzeppelin/contracts/utils/Create2.sol
./node_modules/@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol
./node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol
./node_modules/@openzeppelin/contracts/utils/cryptography/MerkleProof.sol
./node_modules/@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol
./node_modules/@openzeppelin/contracts/utils/escrow/ConditionalEscrow.sol
./node_modules/@openzeppelin/contracts/utils/escrow/Escrow.sol
./node_modules/@openzeppelin/contracts/utils/escrow/RefundEscrow.sol
./node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol
./node_modules/@openzeppelin/contracts/utils/introspection/ERC165Checker.sol
./node_modules/@openzeppelin/contracts/utils/introspection/ERC165Storage.sol
./node_modules/@openzeppelin/contracts/utils/introspection/ERC1820Implementer.sol
./node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol
./node_modules/@openzeppelin/contracts/utils/introspection/IERC1820Implementer.sol
./node_modules/@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol
./node_modules/@openzeppelin/contracts/utils/math/Math.sol
./node_modules/@openzeppelin/contracts/utils/math/SafeCast.sol
./node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol
./node_modules/@openzeppelin/contracts/utils/math/SignedMath.sol
./node_modules/@openzeppelin/contracts/utils/math/SignedSafeMath.sol
./node_modules/@openzeppelin/contracts/utils/Multicall.sol
./node_modules/@openzeppelin/contracts/utils/StorageSlot.sol
./node_modules/@openzeppelin/contracts/utils/Strings.sol
./node_modules/@openzeppelin/contracts/utils/structs/BitMaps.sol
./node_modules/@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol
./node_modules/@openzeppelin/contracts/utils/structs/EnumerableMap.sol
./node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol
./node_modules/@openzeppelin/contracts/utils/Timers.sol
./node_modules/@openzeppelin/contracts/vendor/amb/IAMB.sol
./node_modules/@openzeppelin/contracts/vendor/arbitrum/IArbSys.sol
./node_modules/@openzeppelin/contracts/vendor/arbitrum/IBridge.sol
./node_modules/@openzeppelin/contracts/vendor/arbitrum/IInbox.sol
./node_modules/@openzeppelin/contracts/vendor/arbitrum/IMessageProvider.sol
./node_modules/@openzeppelin/contracts/vendor/arbitrum/IOutbox.sol
./node_modules/@openzeppelin/contracts/vendor/compound/ICompoundTimelock.sol
./node_modules/@openzeppelin/contracts/vendor/optimism/ICrossDomainMessenger.sol
./node_modules/@openzeppelin/contracts/vendor/polygon/IFxMessageProcessor.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/IERC20Minimal.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/IUniswapV3PoolDeployer.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol
./node_modules/@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol
./node_modules/@uniswap/v3-core/contracts/libraries/BitMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/FixedPoint128.sol
./node_modules/@uniswap/v3-core/contracts/libraries/FixedPoint96.sol
./node_modules/@uniswap/v3-core/contracts/libraries/FullMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/LiquidityMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/Oracle.sol
./node_modules/@uniswap/v3-core/contracts/libraries/Position.sol
./node_modules/@uniswap/v3-core/contracts/libraries/SafeCast.sol
./node_modules/@uniswap/v3-core/contracts/libraries/SqrtPriceMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/SwapMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/Tick.sol
./node_modules/@uniswap/v3-core/contracts/libraries/TickBitmap.sol
./node_modules/@uniswap/v3-core/contracts/libraries/TickMath.sol
./node_modules/@uniswap/v3-core/contracts/libraries/TransferHelper.sol
./node_modules/@uniswap/v3-core/contracts/libraries/UnsafeMath.sol

@ -3,7 +3,7 @@ import { isArray } from "lodash"
import { editor, languages, Position } from "monaco-editor" import { editor, languages, Position } from "monaco-editor"
import monaco from "../../types/monaco" import monaco from "../../types/monaco"
import { EditorUIProps } from "../remix-ui-editor" import { EditorUIProps } from "../remix-ui-editor"
import { GeCompletionUnits, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteBTypeName, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable } from "./completion/completionGlobals" import { GeCompletionUnits, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteBTypeName, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable, GetImports } from "./completion/completionGlobals"
export class RemixCompletionProvider implements languages.CompletionItemProvider { export class RemixCompletionProvider implements languages.CompletionItemProvider {
@ -16,12 +16,15 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
this.monaco = monaco this.monaco = monaco
} }
triggerCharacters = ['.', ''] triggerCharacters = ['.', '', '"', '@', '/']
async provideCompletionItems(model: editor.ITextModel, position: Position, context: monaco.languages.CompletionContext): Promise<monaco.languages.CompletionList | undefined> { async provideCompletionItems(model: editor.ITextModel, position: Position, context: monaco.languages.CompletionContext): Promise<monaco.languages.CompletionList | undefined> {
const completionSettings = await this.props.plugin.call('config', 'getAppParameter', 'settings/auto-completion') const completionSettings = await this.props.plugin.call('config', 'getAppParameter', 'settings/auto-completion')
if (!completionSettings) return if (!completionSettings) return
<<<<<<< HEAD
=======
>>>>>>> a82c40d86150cabc4d774c312ed442613d04e8bc
const word = model.getWordUntilPosition(position); const word = model.getWordUntilPosition(position);
const range = { const range = {
startLineNumber: position.lineNumber, startLineNumber: position.lineNumber,
@ -34,13 +37,10 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
let nodes: AstNode[] = [] let nodes: AstNode[] = []
let suggestions: monaco.languages.CompletionItem[] = [] let suggestions: monaco.languages.CompletionItem[] = []
if (context.triggerCharacter === '.') {
const lineTextBeforeCursor: string = line.substring(0, position.column - 1)
const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor)
const expressionElements = lineTextBeforeCursor.split('.')
let dotCompleted = false if (context.triggerCharacter === '"' || context.triggerCharacter === '@' || context.triggerCharacter === '/') {
<<<<<<< HEAD
// handles completion from for builtin types // handles completion from for builtin types
if (lastNodeInExpression.memberName === 'sender') { // exception for this member if (lastNodeInExpression.memberName === 'sender') { // exception for this member
lastNodeInExpression.name = 'sender' lastNodeInExpression.name = 'sender'
@ -62,9 +62,28 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
const dotCompletions = await this.getDotCompletions(nameOfLastTypedExpression, range) const dotCompletions = await this.getDotCompletions(nameOfLastTypedExpression, range)
nodes = [...nodes, ...dotCompletions.nodes] nodes = [...nodes, ...dotCompletions.nodes]
suggestions = [...suggestions, ...dotCompletions.suggestions] suggestions = [...suggestions, ...dotCompletions.suggestions]
=======
const lastpart = line.substring(0, position.column - 1).split(';').pop()
if (lastpart.startsWith('import')) {
const imports = await this.props.plugin.call('codeParser', 'getImports')
if (context.triggerCharacter === '"' || context.triggerCharacter === '@') {
suggestions = [...suggestions,
...GetImports(range, this.monaco, imports, context.triggerCharacter),
]
} else if (context.triggerCharacter === '/') {
const word = line.split('"')[1]
suggestions = [...suggestions,
...GetImports(range, this.monaco, imports, word),
]
} else {
return
}
>>>>>>> a82c40d86150cabc4d774c312ed442613d04e8bc
} }
} else { } else
<<<<<<< HEAD
// handles contract completions and other suggestions // handles contract completions and other suggestions
suggestions = [...suggestions, suggestions = [...suggestions,
...GetGlobalVariable(range, this.monaco), ...GetGlobalVariable(range, this.monaco),
@ -76,18 +95,71 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
] ]
let contractCompletions = await this.getContractCompletions() let contractCompletions = await this.getContractCompletions()
=======
// we can't have external nodes without using this. if (context.triggerCharacter === '.') {
contractCompletions = contractCompletions.filter(node => { const lineTextBeforeCursor: string = line.substring(0, position.column - 1)
if (node.visibility && node.visibility === 'external') { const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor)
return false const expressionElements = lineTextBeforeCursor.split('.')
let dotCompleted = false
>>>>>>> a82c40d86150cabc4d774c312ed442613d04e8bc
// handles completion from for builtin types
if (lastNodeInExpression.memberName === 'sender') { // exception for this member
lastNodeInExpression.name = 'sender'
} }
<<<<<<< HEAD
return true return true
}) })
nodes = [...nodes, ...contractCompletions] nodes = [...nodes, ...contractCompletions]
=======
const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco)
if (globalCompletion) {
dotCompleted = true
suggestions = [...suggestions, ...globalCompletion]
setTimeout(() => {
// eslint-disable-next-line no-debugger
// debugger
}, 2000)
}
// handle completion for global THIS.
if (lastNodeInExpression.name === 'this') {
dotCompleted = true
nodes = [...nodes, ...await this.getThisCompletions(position)]
}
// handle completion for other dot completions
if (expressionElements.length > 1 && !dotCompleted) {
>>>>>>> a82c40d86150cabc4d774c312ed442613d04e8bc
const nameOfLastTypedExpression = lastNodeInExpression.name || lastNodeInExpression.memberName
const dotCompletions = await this.getDotCompletions(position, nameOfLastTypedExpression, range)
nodes = [...nodes, ...dotCompletions.nodes]
suggestions = [...suggestions, ...dotCompletions.suggestions]
}
} else {
// handles contract completions and other suggestions
suggestions = [...suggestions,
...GetGlobalVariable(range, this.monaco),
...getCompletionSnippets(range, this.monaco),
...GetCompletionTypes(range, this.monaco),
...GetCompletionKeywords(range, this.monaco),
...GetGlobalFunctions(range, this.monaco),
...GeCompletionUnits(range, this.monaco),
]
let contractCompletions = await this.getContractCompletions(position)
// we can't have external nodes without using this.
contractCompletions = contractCompletions.filter(node => {
if (node.visibility && node.visibility === 'external') {
return false
}
return true
})
nodes = [...nodes, ...contractCompletions]
} }
// remove duplicates // remove duplicates
const nodeIds = {}; const nodeIds = {};

@ -13,8 +13,22 @@ export class RemixDefinitionProvider implements monaco.languages.DefinitionProvi
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
async provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise<monaco.languages.Definition | monaco.languages.LocationLink[]> { async provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise<monaco.languages.Definition | monaco.languages.LocationLink[]> {
const cursorPosition = this.props.editorAPI.getCursorPosition() const cursorPosition = this.props.editorAPI.getCursorPosition()
const jumpLocation = await this.jumpToDefinition(cursorPosition) let jumpLocation = await this.jumpToDefinition(cursorPosition)
if(!jumpLocation || !jumpLocation.fileName) return [] if (!jumpLocation || !jumpLocation.fileName) {
const line = model.getLineContent(position.lineNumber)
const lastpart = line.substring(0, position.column - 1).split(';').pop()
if (lastpart.startsWith('import')) {
const importPath = line.substring(lastpart.indexOf('"') + 1)
const importPath2 = importPath.substring(0, importPath.indexOf('"'))
jumpLocation = {
startLineNumber: 1,
startColumn: 1,
endColumn: 1,
endLineNumber: 1,
fileName: importPath2
}
}
}
return [{ return [{
uri: this.monaco.Uri.parse(jumpLocation.fileName), uri: this.monaco.Uri.parse(jumpLocation.fileName),
range: { range: {

@ -158,6 +158,7 @@ export const EditorUI = (props: EditorUIProps) => {
const infoColor = formatColor('--info') const infoColor = formatColor('--info')
const darkColor = formatColor('--dark') const darkColor = formatColor('--dark')
const secondaryColor = formatColor('--secondary') const secondaryColor = formatColor('--secondary')
const primaryColor = formatColor('--primary')
const textColor = formatColor('--text') || darkColor const textColor = formatColor('--text') || darkColor
const textbackground = formatColor('--text-background') || lightColor const textbackground = formatColor('--text-background') || lightColor
@ -266,7 +267,8 @@ export const EditorUI = (props: EditorUIProps) => {
'editorSuggestWidget.background': lightColor, 'editorSuggestWidget.background': lightColor,
'editorSuggestWidget.selectedBackground': secondaryColor, 'editorSuggestWidget.selectedBackground': secondaryColor,
'editorSuggestWidget.selectedForeground': textColor, 'editorSuggestWidget.selectedForeground': textColor,
'editorSuggestWidget.highlightForeground': infoColor, 'editorSuggestWidget.highlightForeground': primaryColor,
'editorSuggestWidget.focusHighlightForeground': infoColor,
'editor.lineHighlightBorder': secondaryColor, 'editor.lineHighlightBorder': secondaryColor,
'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor, 'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor,
'editorGutter.background': lightColor, 'editorGutter.background': lightColor,

@ -15,7 +15,7 @@ export function RemixPluginPanel (props: RemixPanelProps) {
<> <>
{props.header} {props.header}
<div className="pluginsContainer"> <div className="pluginsContainer">
<div className='plugins pb-2' id='plugins'> <div className='plugins' id='plugins'>
{Object.values(props.plugins).map((pluginRecord) => { {Object.values(props.plugins).map((pluginRecord) => {
return <RemixUIPanelPlugin key={pluginRecord.profile.name} pluginRecord={pluginRecord} /> return <RemixUIPanelPlugin key={pluginRecord.profile.name} pluginRecord={pluginRecord} />
})} })}

@ -52,7 +52,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
<h6>{to.displayName} :</h6> <h6>{to.displayName} :</h6>
<p> {to.description || <i>No description Provided</i>}</p> <p> {to.description || <i>No description Provided</i>}</p>
{pluginMessage()} {pluginMessage()}
{ sensitiveCall ? <p className='text-warning'><i className="fas fa-exclamation-triangle mr-2" aria-hidden="true"></i>You are going to process a sensitive call. Please make sure you trust this plugin.</p> : '' } { sensitiveCall ? <p className='text-warning'><i className="fas fa-exclamation-triangle mr-2" aria-hidden="true"></i>Make sure you trust this plugin before processing this call.</p> : '' }
</article> </article>
<article className='remember'> <article className='remember'>
{ !sensitiveCall && <div className='form-check'> { !sensitiveCall && <div className='form-check'>

@ -1 +1,3 @@
export { SearchTab } from './lib/components/Search'; export { SearchTab } from './lib/components/Search';
export * from './lib/components/results/SearchHelper';
export * from './lib/types';

@ -80,9 +80,9 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version) const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version)
return tmp ? tmp[1] : version return tmp ? tmp[1] : version
} }
if (version != '' && !semver.gt(truncateVersion(version), '0.4.12')) { if (version && version != '' && !semver.gt(truncateVersion(version), '0.4.12')) {
setIsSupportedVersion(false) setIsSupportedVersion(false)
setRunButtonTitle('Sselect Solidity compiler version greater than 0.4.12.') setRunButtonTitle('Select Solidity compiler version greater than 0.4.12.')
} else { } else {
setIsSupportedVersion(true) setIsSupportedVersion(true)
setRunButtonTitle('Run static analysis') setRunButtonTitle('Run static analysis')

@ -186,7 +186,7 @@ export const TabsUI = (props: TabsUIProps) => {
}} }}
> >
<TabList className="d-flex flex-row align-items-center"> <TabList className="d-flex flex-row align-items-center">
{props.tabs.map((tab, i) => <Tab className="py-1" key={tab.name}>{renderTab(tab, i)}</Tab>)} {props.tabs.map((tab, i) => <Tab className="" key={tab.name}>{renderTab(tab, i)}</Tab>)}
</TabList> </TabList>
{props.tabs.map((tab) => <TabPanel key={tab.name} ></TabPanel>)} {props.tabs.map((tab) => <TabPanel key={tab.name} ></TabPanel>)}
</Tabs> </Tabs>

@ -13,7 +13,7 @@ export const listenOnPluginEvents = (filePanelPlugin) => {
plugin = filePanelPlugin plugin = filePanelPlugin
plugin.on('filePanel', 'createWorkspaceReducerEvent', (name: string, workspaceTemplateName: WorkspaceTemplate, isEmpty = false, cb: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => { plugin.on('filePanel', 'createWorkspaceReducerEvent', (name: string, workspaceTemplateName: WorkspaceTemplate, isEmpty = false, cb: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {
createWorkspace(name, workspaceTemplateName, isEmpty, cb) createWorkspace(name, workspaceTemplateName, null, isEmpty, cb)
}) })
plugin.on('filePanel', 'renameWorkspaceReducerEvent', (oldName: string, workspaceName: string, cb: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => { plugin.on('filePanel', 'renameWorkspaceReducerEvent', (oldName: string, workspaceName: string, cb: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {

@ -43,17 +43,19 @@ export const addInputField = async (type: 'file' | 'folder', path: string, cb?:
return promise return promise
} }
export const createWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, isEmpty = false, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void, isGitRepo: boolean = false) => { export const createWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, opts = null, isEmpty = false, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void, isGitRepo: boolean = false) => {
await plugin.fileManager.closeAllFiles() await plugin.fileManager.closeAllFiles()
const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName) const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName)
dispatch(createWorkspaceRequest(promise)) dispatch(createWorkspaceRequest(promise))
promise.then(async () => { promise.then(async () => {
dispatch(createWorkspaceSuccess({ name: workspaceName, isGitRepo })) dispatch(createWorkspaceSuccess({ name: workspaceName, isGitRepo }))
await plugin.setWorkspace({ name: workspaceName, isLocalhost: false }) await plugin.setWorkspace({ name: workspaceName, isLocalhost: false })
await plugin.setWorkspaces(await getWorkspaces()) await plugin.setWorkspaces(await getWorkspaces())
await plugin.workspaceCreated(workspaceName) await plugin.workspaceCreated(workspaceName)
if (!isEmpty) await loadWorkspacePreset(workspaceTemplateName)
if (isGitRepo) await plugin.call('dGitProvider', 'init')
if (!isEmpty) await loadWorkspacePreset(workspaceTemplateName, opts)
cb && cb(null, workspaceName) cb && cb(null, workspaceName)
}).catch((error) => { }).catch((error) => {
dispatch(createWorkspaceError({ error })) dispatch(createWorkspaceError({ error }))
@ -80,7 +82,7 @@ export type UrlParametersType = {
language: string language: string
} }
export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault') => { export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault', opts?) => {
const workspaceProvider = plugin.fileProviders.workspace const workspaceProvider = plugin.fileProviders.workspace
const params = queryParams.get() as UrlParametersType const params = queryParams.get() as UrlParametersType
@ -159,7 +161,7 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
if (!templateList.includes(template)) break if (!templateList.includes(template)) break
_paq.push(['trackEvent', 'workspace', 'template', template]) _paq.push(['trackEvent', 'workspace', 'template', template])
// @ts-ignore // @ts-ignore
const files = await templateWithContent[template]() const files = await templateWithContent[template](opts)
for (const file in files) { for (const file in files) {
try { try {
await workspaceProvider.set(file, files[file]) await workspaceProvider.set(file, files[file])
@ -339,7 +341,7 @@ export const cloneRepository = async (url: string) => {
try { try {
const repoName = await getRepositoryTitle(url) const repoName = await getRepositoryTitle(url)
await createWorkspace(repoName, 'blank', true, null, true) await createWorkspace(repoName, 'blank', null, true, null, true)
const promise = plugin.call('dGitProvider', 'clone', repoConfig, repoName, true) const promise = plugin.call('dGitProvider', 'clone', repoConfig, repoName, true)
dispatch(cloneRepositoryRequest()) dispatch(cloneRepositoryRequest())

@ -9,7 +9,7 @@ export const FileSystemContext = createContext<{
dispatchFetchDirectory:(path: string) => Promise<void>, dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>, dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>,
dispatchRemoveInputField:(path: string) => Promise<void>, dispatchRemoveInputField:(path: string) => Promise<void>,
dispatchCreateWorkspace: (workspaceName: string, workspaceTemplateName: string) => Promise<void>, dispatchCreateWorkspace: (workspaceName: string, workspaceTemplateName: string, opts?, initGitRepo?: boolean) => Promise<void>,
toast: (toasterMsg: string) => void, toast: (toasterMsg: string) => void,
dispatchFetchWorkspaceDirectory: (path: string) => Promise<void>, dispatchFetchWorkspaceDirectory: (path: string) => Promise<void>,
dispatchSwitchToWorkspace: (name: string) => Promise<void>, dispatchSwitchToWorkspace: (name: string) => Promise<void>,

@ -7,7 +7,7 @@
} }
.remixui_fileexplorer { .remixui_fileexplorer {
position : relative; position : relative;
overflow-y : auto; overflow-y : hidden;
} }
.remixui_fileExplorerTree { .remixui_fileExplorerTree {
cursor : default; cursor : default;

@ -45,8 +45,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await removeInputField(path) await removeInputField(path)
} }
const dispatchCreateWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate) => { const dispatchCreateWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, opts?, initGitRepo?: boolean) => {
await createWorkspace(workspaceName, workspaceTemplateName) await createWorkspace(workspaceName, workspaceTemplateName, opts, null, null, initGitRepo)
} }
const dispatchFetchWorkspaceDirectory = async (path: string) => { const dispatchFetchWorkspaceDirectory = async (path: string) => {

@ -15,11 +15,15 @@ export function Workspace () {
const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE) const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE)
const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null) const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null)
const [showDropdown, setShowDropdown] = useState<boolean>(false) const [showDropdown, setShowDropdown] = useState<boolean>(false)
const displayOzCustomRef = useRef<HTMLDivElement>()
const ozFeatures = useRef({mintable: false, burnable: false, pausable: false})
const upgradeable = useRef()
const global = useContext(FileSystemContext) const global = useContext(FileSystemContext)
const workspaceRenameInput = useRef() const workspaceRenameInput = useRef()
const workspaceCreateInput = useRef() const workspaceCreateInput = useRef()
const workspaceCreateTemplateInput = useRef() const workspaceCreateTemplateInput = useRef()
const cloneUrlRef = useRef<HTMLInputElement>() const cloneUrlRef = useRef<HTMLInputElement>()
const initGitRepoRef = useRef<HTMLInputElement>()
useEffect(() => { useEffect(() => {
setCurrentWorkspace(localStorage.getItem('currentWorkspace') ? localStorage.getItem('currentWorkspace') : '') setCurrentWorkspace(localStorage.getItem('currentWorkspace') ? localStorage.getItem('currentWorkspace') : '')
@ -104,9 +108,15 @@ export function Workspace () {
const workspaceName = workspaceCreateInput.current.value const workspaceName = workspaceCreateInput.current.value
// @ts-ignore: Object is possibly 'null'. // @ts-ignore: Object is possibly 'null'.
const workspaceTemplateName = workspaceCreateTemplateInput.current.value || 'remixDefault' const workspaceTemplateName = workspaceCreateTemplateInput.current.value || 'remixDefault'
const initGitRepo = initGitRepoRef.current.checked
const features = ozFeatures.current
const opts = {
upgradeable: upgradeable.current,
features
}
try { try {
await global.dispatchCreateWorkspace(workspaceName, workspaceTemplateName) await global.dispatchCreateWorkspace(workspaceName, workspaceTemplateName, opts, initGitRepo)
} catch (e) { } catch (e) {
global.modal('Create Workspace', e.message, 'OK', () => {}, '') global.modal('Create Workspace', e.message, 'OK', () => {}, '')
console.error(e) console.error(e)
@ -138,6 +148,13 @@ export function Workspace () {
} }
const updateWsName = () => { const updateWsName = () => {
// @ts-ignore
if (workspaceCreateTemplateInput.current.value.startsWith('oz') && displayOzCustomRef && displayOzCustomRef.current) {
displayOzCustomRef.current.style.display = 'block'
upgradeable.current = undefined
ozFeatures.current = {mintable: false, burnable: false, pausable: false}
} else displayOzCustomRef.current.style.display = 'none'
// @ts-ignore // @ts-ignore
workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value || 'remixDefault'}_${Date.now()}` workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value || 'remixDefault'}_${Date.now()}`
} }
@ -156,19 +173,92 @@ export function Workspace () {
setShowDropdown(isOpen) setShowDropdown(isOpen)
} }
const handleUpgradeability = (e) => {
// @ts-ignore
upgradeable.current = e.target.value
// @ts-ignore
workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value + '_upgradeable'}_${Date.now()}`
}
const handleFeatures = (e) => {
// @ts-ignore
ozFeatures.current[e.target.value] = e.target.checked
}
const createModalMessage = () => { const createModalMessage = () => {
return ( return (
<> <>
<label id="wsName" className="form-check-label">Workspace name</label> <label id="selectWsTemplate" className="form-check-label" style={{fontWeight: "bolder"}}>Choose a template</label>
<input type="text" data-id="modalDialogCustomPromptTextCreate" defaultValue={`remixDefault_${Date.now()}`} ref={workspaceCreateInput} className="form-control" /><br/> <select name="wstemplate" className="mb-3 form-control custom-select" id="wstemplate" defaultValue='remixDefault' ref={workspaceCreateTemplateInput} onChange={updateWsName}>
<label id="selectWsTemplate" className="form-check-label">Choose a template</label> <optgroup style={{fontSize: "medium"}} label="General">
<select name="wstemplate" className="form-control custom-select" id="wstemplate" defaultValue='remixDefault' ref={workspaceCreateTemplateInput} onChange={updateWsName}> <option style={{fontSize: "small"}} value='remixDefault'>Default</option>
<option value='remixDefault'>Default</option> <option style={{fontSize: "small"}} value='blank'>Blank</option>
<option value='blank'>Blank</option> </optgroup>
<option value='ozerc20'>OpenZeppelin ERC20</option> <optgroup style={{fontSize: "medium"}} label="OpenZepplin">
<option value='zeroxErc20'>0xProject ERC20</option> <option style={{fontSize: "small"}} value='ozerc20'>ERC20</option>
<option value='ozerc721'>OpenZeppelin ERC721</option> <option style={{fontSize: "small"}} value='ozerc721'>ERC721</option>
<option style={{fontSize: "small"}} value='ozerc1155'>ERC1155</option>
</optgroup>
<optgroup style={{fontSize: "medium"}} label="0xProject">
<option style={{fontSize: "small"}} value='zeroxErc20'>ERC20</option>
</optgroup>
</select> </select>
<div id="ozcustomization" data-id="ozCustomization" ref={displayOzCustomRef} style={{display: 'none'}} className="mb-2">
<label className="form-check-label d-block mb-2" style={{fontWeight: "bolder"}}>Customize template</label>
<label id="wsName" className="form-check-label d-block mb-1">Features</label>
<div className="mb-2" onChange={(e) => handleFeatures(e)}>
<div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="mintable" id="mintable" />
<label className="form-check-label custom-control-label" htmlFor="mintable" data-id="featureTypeMintable" >Mintable</label>
</div>
<div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="burnable" id="burnable" />
<label className="form-check-label custom-control-label" htmlFor="burnable" data-id="featureTypeBurnable" >Burnable</label>
</div>
<div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="pausable" id="pausable" />
<label className="form-check-label custom-control-label" htmlFor="pausable" data-id="featureTypePausable" >Pausable</label>
</div>
</div>
<label id="wsName" className="form-check-label d-block mb-1">Upgradeability</label>
<div onChange={(e) => handleUpgradeability(e)}>
<div className="d-flex ml-2 custom-control custom-radio">
<input className="custom-control-input" type="radio" name="upgradeability" value="transparent" id="transparent" />
<label className="form-check-label custom-control-label" htmlFor="transparent" data-id="upgradeTypeTransparent" >Transparent</label>
</div>
<div className="d-flex ml-2 custom-control custom-radio">
<input className="custom-control-input" type="radio" name="upgradeability" value="uups" id="uups" />
<label className="form-check-label custom-control-label" htmlFor="uups" data-id="upgradeTypeUups" >UUPS</label>
</div>
</div>
</div>
<label id="wsName" className="form-check-label" style={{fontWeight: "bolder"}} >Workspace name</label>
<input type="text" data-id="modalDialogCustomPromptTextCreate" defaultValue={`remixDefault_${Date.now()}`} ref={workspaceCreateInput} className="form-control" />
<div className="d-flex py-2 align-items-center custom-control custom-checkbox">
<input
ref={initGitRepoRef}
id="initGitRepository"
data-id="initGitRepository"
className="form-check-input custom-control-input"
type="checkbox"
onChange={() => {}}
/>
<label
htmlFor="initGitRepository"
data-id="initGitRepositoryLabel"
className="m-0 form-check-label custom-control-label udapp_checkboxAlign"
title="Check option to initialize workspace as a new git repository"
>
Initialize workspace as a new git repository
</label>
</div>
</> </>
) )
} }
@ -279,9 +369,9 @@ export function Workspace () {
<Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' data-id="custom-dropdown-items"> <Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' data-id="custom-dropdown-items">
<Dropdown.Item <Dropdown.Item
onClick={() => { onClick={() => {
createWorkspace() createWorkspace()
}} }}
> >
{ {
<span className="pl-3"> - create a new workspace - </span> <span className="pl-3"> - create a new workspace - </span>

@ -1,5 +1,7 @@
export { default as remixDefault } from './templates/remixDefault' export { default as remixDefault } from './templates/remixDefault'
export { default as blank } from './templates/blank' export { default as blank } from './templates/blank'
export { default as ozerc20 } from './templates/ozerc20' export { default as ozerc20 } from './templates/ozerc20'
export { default as zeroxErc20 } from './templates/zeroxErc20'
export { default as ozerc721 } from './templates/ozerc721' export { default as ozerc721 } from './templates/ozerc721'
export { default as ozerc1155 } from './templates/ozerc1155'
export { default as zeroxErc20 } from './templates/zeroxErc20'

@ -0,0 +1,26 @@
import { erc1155 } from '@openzeppelin/wizard';
export default async (opts) => {
if (opts.features) {
erc1155.defaults.mintable = opts.features.mintable
erc1155.defaults.burnable = opts.features.burnable
erc1155.defaults.pausable = opts.features.pausable
}
const filesObj = {
'contracts/MyToken.sol': erc1155.print({ ...erc1155.defaults, upgradeable: opts.upgradeable}),
// @ts-ignore
'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default,
// @ts-ignore
'scripts/deploy_with_web3.ts': (await import('!!raw-loader!./scripts/deploy_with_web3.ts')).default,
// @ts-ignore
'scripts/ethers-lib.ts': (await import('!!raw-loader!./scripts/ethers-lib.ts')).default,
// @ts-ignore
'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default
}
// If no options is selected, opts.upgradeable will be undefined
// We do not show test file for upgradeable contract
// @ts-ignore
if (opts.upgradeable === undefined || !opts.upgradeable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
return filesObj
}

@ -0,0 +1,10 @@
import { deploy } from './ethers-lib'
(async () => {
try {
const result = await deploy('MyToken', [])
console.log(`address: ${result.address}`)
} catch (e) {
console.log(e.message)
}
})()

@ -0,0 +1,10 @@
import { deploy } from './web3-lib'
(async () => {
try {
const result = await deploy('MyToken', [])
console.log(`address: ${result.address}`)
} catch (e) {
console.log(e.message)
}
})()

@ -0,0 +1,29 @@
import { ethers } from 'ethers'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
const contract = await factory.deploy(...args)
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
return contract
}

@ -0,0 +1,36 @@
import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json`
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts()
const contract: Contract = new web3.eth.Contract(metadata.abi)
const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object,
arguments: args
})
const newContractInstance = await contractSend.send({
from: from || accounts[0],
gas: gas || 3600000
})
return newContractInstance.options
}

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol";
import "../contracts/MyToken.sol";
contract MyTokenTest {
MyToken s;
function beforeAll () public {
s = new MyToken();
}
function testSetURI () public {
string memory uri = "https://testuri.io/token";
s.setURI(uri);
Assert.equal(s.uri(1), uri, "uri did not match");
}
}

@ -1,8 +1,14 @@
import { erc20 } from '@openzeppelin/wizard'; import { erc20 } from '@openzeppelin/wizard';
export default async () => { export default async (opts) => {
return { if (opts.features) {
'contracts/MyToken.sol': erc20.print(), erc20.defaults.mintable = opts.features.mintable
erc20.defaults.burnable = opts.features.burnable
erc20.defaults.pausable = opts.features.pausable
}
const filesObj = {
'contracts/MyToken.sol': erc20.print({ ...erc20.defaults, upgradeable: opts.upgradeable}),
// @ts-ignore // @ts-ignore
'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default, 'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default,
// @ts-ignore // @ts-ignore
@ -10,8 +16,12 @@ export default async () => {
// @ts-ignore // @ts-ignore
'scripts/ethers-lib.ts': (await import('!!raw-loader!./scripts/ethers-lib.ts')).default, 'scripts/ethers-lib.ts': (await import('!!raw-loader!./scripts/ethers-lib.ts')).default,
// @ts-ignore // @ts-ignore
'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default, 'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default
// @ts-ignore
'tests/MyToken_test.sol': (await import('raw-loader!./tests/MyToken_test.sol')).default
} }
// If no options is selected, opts.upgradeable will be undefined
// We do not show test file for upgradeable contract
// @ts-ignore
if (opts.upgradeable === undefined || !opts.upgradeable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
return filesObj
} }

@ -1,8 +1,14 @@
import { erc721 } from '@openzeppelin/wizard'; import { erc721 } from '@openzeppelin/wizard';
export default async () => { export default async (opts) => {
return { if (opts.features) {
'contracts/MyToken.sol': erc721.print(), erc721.defaults.mintable = opts.features.mintable
erc721.defaults.burnable = opts.features.burnable
erc721.defaults.pausable = opts.features.pausable
}
const filesObj = {
'contracts/MyToken.sol': erc721.print({ ...erc721.defaults, upgradeable: opts.upgradeable}),
// @ts-ignore // @ts-ignore
'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default, 'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default,
// @ts-ignore // @ts-ignore
@ -10,8 +16,12 @@ export default async () => {
// @ts-ignore // @ts-ignore
'scripts/ethers-lib.ts': (await import('!!raw-loader!./scripts/ethers-lib.ts')).default, 'scripts/ethers-lib.ts': (await import('!!raw-loader!./scripts/ethers-lib.ts')).default,
// @ts-ignore // @ts-ignore
'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default, 'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default
// @ts-ignore
'tests/MyToken_test.sol': (await import('raw-loader!./tests/MyToken_test.sol')).default
} }
// If no options is selected, opts.upgradeable will be undefined
// We do not show test file for upgradeable contract
// @ts-ignore
if (opts.upgradeable === undefined || !opts.upgradeable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
return filesObj
} }

@ -108,7 +108,8 @@
"test-browser": "npm-run-all -lpr selenium make-mock-compiler serve browsertest", "test-browser": "npm-run-all -lpr selenium make-mock-compiler serve browsertest",
"watch": "watchify apps/remix-ide/src/index.js -dv -p browserify-reload -o apps/remix-ide/build/app.js --exclude solc", "watch": "watchify apps/remix-ide/src/index.js -dv -p browserify-reload -o apps/remix-ide/build/app.js --exclude solc",
"reinstall": "rm ./node-modules/ -rf && rm yarn.lock && rm ./build/ -rf && yarn install & yarn run build", "reinstall": "rm ./node-modules/ -rf && rm yarn.lock && rm ./build/ -rf && yarn install & yarn run build",
"ganache-cli": "npx ganache-cli" "ganache-cli": "npx ganache-cli",
"build-contracts": "find ./node_modules/@openzeppelin/contracts | grep -i '.sol' > libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt && find ./node_modules/@uniswap/v3-core/contracts | grep -i '.sol' >> libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [
@ -197,6 +198,7 @@
"merge": "^2.1.1", "merge": "^2.1.1",
"monaco-editor": "^0.30.1", "monaco-editor": "^0.30.1",
"npm-install-version": "^6.0.2", "npm-install-version": "^6.0.2",
"path-browserify": "^1.0.1",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-beta.24", "prettier-plugin-solidity": "^1.0.0-beta.24",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
@ -263,6 +265,8 @@
"@types/ws": "^7.2.4", "@types/ws": "^7.2.4",
"@typescript-eslint/eslint-plugin": "^4.32.0", "@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0", "@typescript-eslint/parser": "^4.32.0",
"@uniswap/v2-core": "^1.0.1",
"@uniswap/v3-core": "^1.0.1",
"ace-mode-lexon": "^1.*.*", "ace-mode-lexon": "^1.*.*",
"ace-mode-move": "0.0.1", "ace-mode-move": "0.0.1",
"ace-mode-solidity": "^0.1.0", "ace-mode-solidity": "^0.1.0",

@ -4813,6 +4813,16 @@
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
"@uniswap/v2-core@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425"
integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==
"@uniswap/v3-core@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.1.tgz#b6d2bdc6ba3c3fbd610bdc502395d86cd35264a0"
integrity sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ==
"@webassemblyjs/ast@1.8.5": "@webassemblyjs/ast@1.8.5":
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@ -18513,6 +18523,11 @@ path-browserify@0.0.1, path-browserify@~0.0.0:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-case@^3.0.4: path-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"

Loading…
Cancel
Save