From a40038834466cd5a0c4289465c8d7e45a4a2f117 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Sun, 23 Jul 2023 09:25:35 +0200 Subject: [PATCH] lint ide fix --- apps/remix-ide/ci/download_e2e_assets.js | 88 +- apps/remix-ide/ci/lint-targets.js | 12 +- apps/remix-ide/ci/splice_tests.js | 32 +- .../src/app/components/main-panel.tsx | 86 +- apps/remix-ide/src/app/components/preload.tsx | 206 ++-- .../src/app/components/vertical-icons.tsx | 4 +- apps/remix-ide/src/app/editor/editor.js | 6 +- apps/remix-ide/src/app/files/dgitProvider.js | 4 +- apps/remix-ide/src/app/files/fileManager.ts | 22 +- apps/remix-ide/src/app/files/fileSystem.ts | 104 +- .../files/filesystems/fileSystemUtility.ts | 308 +++--- .../src/app/files/filesystems/indexedDB.ts | 156 +-- .../src/app/files/filesystems/localStorage.ts | 98 +- apps/remix-ide/src/app/panels/layout.ts | 6 +- apps/remix-ide/src/app/panels/terminal.js | 6 +- apps/remix-ide/src/app/plugins/code-format.ts | 482 ++++----- .../src/app/plugins/code-format/index.ts | 64 +- .../src/app/plugins/code-format/parser.ts | 210 ++-- .../src/app/plugins/contractFlattener.tsx | 4 +- .../src/app/plugins/file-decorator.ts | 118 +-- .../src/app/plugins/parser/code-parser.tsx | 976 +++++++++--------- .../plugins/parser/services/antlr-worker.ts | 72 +- .../services/code-parser-antlr-service.ts | 410 ++++---- .../parser/services/code-parser-compiler.ts | 432 ++++---- .../services/code-parser-gas-service.ts | 112 +- .../parser/services/code-parser-imports.ts | 130 +-- .../app/plugins/parser/types/antlr-types.ts | 232 ++--- .../app/plugins/permission-handler-plugin.tsx | 8 +- .../src/app/plugins/solidity-script.tsx | 28 +- .../src/app/plugins/solidity-umlgen.tsx | 262 ++--- .../app/providers/custom-vm-fork-provider.tsx | 32 +- apps/remix-ide/src/app/state/registry.ts | 48 +- apps/remix-ide/src/app/tabs/analysis-tab.js | 8 +- apps/remix-ide/src/app/tabs/search.tsx | 44 +- apps/remix-ide/src/app/tabs/theme-module.js | 2 +- apps/remix-ide/src/app/tabs/web3-provider.js | 4 +- apps/remix-ide/src/app/udapp/run-tab.js | 4 +- .../src/app/ui/landing-page/landing-page.js | 6 +- apps/remix-ide/src/blockchain/blockchain.tsx | 122 +-- apps/remix-ide/src/blockchain/helper.ts | 24 +- .../src/blockchain/providers/injected.ts | 2 +- .../src/blockchain/providers/node.ts | 2 +- apps/remix-ide/src/blockchain/providers/vm.ts | 10 +- .../src/blockchain/providers/worker-vm.ts | 116 +-- 44 files changed, 2551 insertions(+), 2551 deletions(-) diff --git a/apps/remix-ide/ci/download_e2e_assets.js b/apps/remix-ide/ci/download_e2e_assets.js index 51c161b895..99a089174f 100644 --- a/apps/remix-ide/ci/download_e2e_assets.js +++ b/apps/remix-ide/ci/download_e2e_assets.js @@ -5,8 +5,8 @@ const { exit } = require('process'); const child = child_process.spawnSync('grep -r --include="*.json" --include="*.ts" --include="*.tsx" "+commit" apps/**/* libs/**/*', [], { encoding: 'utf8', cwd: process.cwd(), shell: true }); if (child.error) { - console.log("ERROR: ", child); - exit(1); + console.log("ERROR: ", child); + exit(1); } @@ -15,63 +15,63 @@ let soljson =[]; const quotedVersionsRegex = /['"v]\d*\.\d*\.\d*\+commit\.[\d\w]*/g; let quotedVersionsRegexMatch = child.stdout.match(quotedVersionsRegex) if(quotedVersionsRegexMatch){ - let soljson2 = quotedVersionsRegexMatch.map((item) => item.replace('\'', 'v').replace('"', 'v')) - console.log('non nightly soljson versions found: ', soljson2); - if(soljson2) soljson = soljson.concat(soljson2); + let soljson2 = quotedVersionsRegexMatch.map((item) => item.replace('\'', 'v').replace('"', 'v')) + console.log('non nightly soljson versions found: ', soljson2); + if(soljson2) soljson = soljson.concat(soljson2); } const nightlyVersionsRegex = /\d*\.\d*\.\d-nightly.*\+commit\.[\d\w]*/g const nightlyVersionsRegexMatch = child.stdout.match(nightlyVersionsRegex) if(nightlyVersionsRegexMatch){ - let soljson3 = nightlyVersionsRegexMatch.map((item) => 'v' + item); - console.log('nightly soljson versions found: ', soljson3); - if(soljson3) soljson = soljson.concat(soljson3); + let soljson3 = nightlyVersionsRegexMatch.map((item) => 'v' + item); + console.log('nightly soljson versions found: ', soljson3); + if(soljson3) soljson = soljson.concat(soljson3); } if (soljson) { - // filter out duplicates - soljson = soljson.filter((item, index) => soljson.indexOf(item) === index); + // filter out duplicates + soljson = soljson.filter((item, index) => soljson.indexOf(item) === index); - // manually add some versions - soljson.push('v0.7.6+commit.7338295f'); + // manually add some versions + soljson.push('v0.7.6+commit.7338295f'); - console.log('soljson versions found: ', soljson, soljson.length); + console.log('soljson versions found: ', soljson, soljson.length); - for (let i = 0; i < soljson.length; i++) { - const version = soljson[i]; - if (version) { - let url = '' - - // if nightly - if (version.includes('nightly')) { - url = `https://binaries.soliditylang.org/bin/soljson-${version}.js`; - }else{ - url = `https://binaries.soliditylang.org/wasm/soljson-${version}.js`; - } - - const dir = './dist/apps/remix-ide/assets/js/soljson'; - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - - const path = `./dist/apps/remix-ide/assets/js/soljson/soljson-${version}.js`; - // check if the file exists - const exists = fs.existsSync(path); - if (!exists) { - console.log('URL:', url) - try { - // use curl to download the file - child_process.exec(`curl -o ${path} ${url}`, { encoding: 'utf8', cwd: process.cwd(), shell: true }) - } catch (e) { - console.log('Failed to download soljson' + version + ' from ' + url) - } - } + for (let i = 0; i < soljson.length; i++) { + const version = soljson[i]; + if (version) { + let url = '' + + // if nightly + if (version.includes('nightly')) { + url = `https://binaries.soliditylang.org/bin/soljson-${version}.js`; + }else{ + url = `https://binaries.soliditylang.org/wasm/soljson-${version}.js`; + } + + const dir = './dist/apps/remix-ide/assets/js/soljson'; + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + + const path = `./dist/apps/remix-ide/assets/js/soljson/soljson-${version}.js`; + // check if the file exists + const exists = fs.existsSync(path); + if (!exists) { + console.log('URL:', url) + try { + // use curl to download the file + child_process.exec(`curl -o ${path} ${url}`, { encoding: 'utf8', cwd: process.cwd(), shell: true }) + } catch (e) { + console.log('Failed to download soljson' + version + ' from ' + url) + } + } - } + } - } + } } diff --git a/apps/remix-ide/ci/lint-targets.js b/apps/remix-ide/ci/lint-targets.js index 7330cc9a3e..44e71d8c0f 100644 --- a/apps/remix-ide/ci/lint-targets.js +++ b/apps/remix-ide/ci/lint-targets.js @@ -5,15 +5,15 @@ const file = fs.readFileSync('projects.json') const projects = JSON.parse(file) console.log(Object.keys(projects.graph.nodes)) for(let node of Object.keys(projects.graph.nodes)){ - if(projects.graph.nodes[node].data.targets.lint){ + if(projects.graph.nodes[node].data.targets.lint){ console.log(projects.graph.nodes[node].data.name) const result = spawnSync('yarn', ['lint', projects.graph.nodes[node].data.name]) if(result.status == 0){ - console.log('success') + console.log('success') }else{ - console.log(result.stdout.toString()) - console.log(result.stderr.toString()) - exit(1) + console.log(result.stdout.toString()) + console.log(result.stderr.toString()) + exit(1) } - } + } } \ No newline at end of file diff --git a/apps/remix-ide/ci/splice_tests.js b/apps/remix-ide/ci/splice_tests.js index 3a51c3eb0c..57cc1db31b 100644 --- a/apps/remix-ide/ci/splice_tests.js +++ b/apps/remix-ide/ci/splice_tests.js @@ -7,21 +7,21 @@ let args = process.argv.slice(2) const jobsize = args[0] || 10; const job = args[1] || 0; exec(cmd, (error, stdout, stderr) => { - if (error) { - console.error(`error: ${error.message}`); - return; - } + if (error) { + console.error(`error: ${error.message}`); + return; + } - if (stderr) { - console.error(`stderr: ${stderr}`); - return; - } + if (stderr) { + console.error(`stderr: ${stderr}`); + return; + } - let files = stdout.split('\n').filter(f => f.includes('.test')).map(f => f.replace('dist/apps/remix-ide-e2e/src/tests/', '')).map(f => f.replace('.js', '')) - let splitIndex = Math.ceil(files.length / jobsize); - const parts = [] - for (let i = 0; i < jobsize; i++) { - parts.push(files.slice(i * splitIndex, (i + 1) * splitIndex)) - } - console.log(parts[job].join('\n')) - }); + let files = stdout.split('\n').filter(f => f.includes('.test')).map(f => f.replace('dist/apps/remix-ide-e2e/src/tests/', '')).map(f => f.replace('.js', '')) + let splitIndex = Math.ceil(files.length / jobsize); + const parts = [] + for (let i = 0; i < jobsize; i++) { + parts.push(files.slice(i * splitIndex, (i + 1) * splitIndex)) + } + console.log(parts[job].join('\n')) +}); diff --git a/apps/remix-ide/src/app/components/main-panel.tsx b/apps/remix-ide/src/app/components/main-panel.tsx index d6dee613fd..0eb7487b94 100644 --- a/apps/remix-ide/src/app/components/main-panel.tsx +++ b/apps/remix-ide/src/app/components/main-panel.tsx @@ -13,56 +13,56 @@ const profile = { } export class MainPanel extends AbstractPanel { - element: HTMLDivElement - dispatch: React.Dispatch = () => {} - constructor (config) { - super(profile) - this.element = document.createElement('div') - this.element.setAttribute('data-id', 'mainPanelPluginsContainer') - this.element.setAttribute('style', 'height: 100%; width: 100%;') - // this.config = config - } + element: HTMLDivElement + dispatch: React.Dispatch = () => {} + constructor (config) { + super(profile) + this.element = document.createElement('div') + this.element.setAttribute('data-id', 'mainPanelPluginsContainer') + this.element.setAttribute('style', 'height: 100%; width: 100%;') + // this.config = config + } - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - } + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + } - onActivation () { - this.renderComponent() - } + onActivation () { + this.renderComponent() + } - focus (name) { - this.emit('focusChanged', name) - super.focus(name) - this.renderComponent() - } + focus (name) { + this.emit('focusChanged', name) + super.focus(name) + this.renderComponent() + } - addView (profile, view) { - super.addView(profile, view) - this.renderComponent() - } + addView (profile, view) { + super.addView(profile, view) + this.renderComponent() + } - removeView (profile) { - super.removeView(profile) - this.renderComponent() - } + removeView (profile) { + super.removeView(profile) + this.renderComponent() + } - async showContent (name) { - super.showContent(name) - this.renderComponent() - } + async showContent (name) { + super.showContent(name) + this.renderComponent() + } - renderComponent () { - this.dispatch({ - plugins: this.plugins - }) - } + renderComponent () { + this.dispatch({ + plugins: this.plugins + }) + } - render() { - return
- } + render() { + return
+ } - updateComponent (state: any) { - return } plugins={state.plugins}/> - } + updateComponent (state: any) { + return } plugins={state.plugins}/> + } } diff --git a/apps/remix-ide/src/app/components/preload.tsx b/apps/remix-ide/src/app/components/preload.tsx index f607264d63..4cca83a00a 100644 --- a/apps/remix-ide/src/app/components/preload.tsx +++ b/apps/remix-ide/src/app/components/preload.tsx @@ -11,134 +11,134 @@ const _paq = window._paq = window._paq || [] export const Preload = () => { - const [supported, setSupported] = useState(true) - const [error, setError] = useState(false) - const [showDownloader, setShowDownloader] = useState(false) - const remixFileSystems = useRef(new fileSystems()) - const remixIndexedDB = useRef(new indexedDBFileSystem()) - const localStorageFileSystem = useRef(new localStorageFS()) - // url parameters to e2e test the fallbacks and error warnings - const testmigrationFallback = useRef(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') - const testmigrationResult = useRef(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') - const testBlockStorage = useRef(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') + const [supported, setSupported] = useState(true) + const [error, setError] = useState(false) + const [showDownloader, setShowDownloader] = useState(false) + const remixFileSystems = useRef(new fileSystems()) + const remixIndexedDB = useRef(new indexedDBFileSystem()) + const localStorageFileSystem = useRef(new localStorageFS()) + // url parameters to e2e test the fallbacks and error warnings + const testmigrationFallback = useRef(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') + const testmigrationResult = useRef(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') + const testBlockStorage = useRef(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') - function loadAppComponent() { - import('../../app').then((AppComponent) => { - const appComponent = new AppComponent.default() - appComponent.run().then(() => { - render( - <> - - , - document.getElementById('root') - ) - }) - }).catch(err => { - _paq.push(['trackEvent', 'Preload', 'error', err && err.message]) - console.error('Error loading Remix:', err) - setError(true) - }) - } + function loadAppComponent() { + import('../../app').then((AppComponent) => { + const appComponent = new AppComponent.default() + appComponent.run().then(() => { + render( + <> + + , + document.getElementById('root') + ) + }) + }).catch(err => { + _paq.push(['trackEvent', 'Preload', 'error', err && err.message]) + console.error('Error loading Remix:', err) + setError(true) + }) + } - const downloadBackup = async () => { - setShowDownloader(false) - const fsUtility = new fileSystemUtility() - await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) - await migrateAndLoad() - } + const downloadBackup = async () => { + setShowDownloader(false) + const fsUtility = new fileSystemUtility() + await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) + await migrateAndLoad() + } - const migrateAndLoad = async () => { - setShowDownloader(false) - const fsUtility = new fileSystemUtility() - const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) - _paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail']) - await setFileSystems() - } + const migrateAndLoad = async () => { + setShowDownloader(false) + const fsUtility = new fileSystemUtility() + const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) + _paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail']) + await setFileSystems() + } - const setFileSystems = async() => { - const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) - if (fsLoaded) { - console.log(fsLoaded.name + ' activated') - _paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) - loadAppComponent() - } else { - _paq.push(['trackEvent', 'Storage', 'error', 'no supported storage']) - setSupported(false) - } + const setFileSystems = async() => { + const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) + if (fsLoaded) { + console.log(fsLoaded.name + ' activated') + _paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) + loadAppComponent() + } else { + _paq.push(['trackEvent', 'Storage', 'error', 'no supported storage']) + setSupported(false) } + } - const testmigration = async() => { - if (testmigrationResult.current) { - const fsUtility = new fileSystemUtility() - fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs) - } + const testmigration = async() => { + if (testmigrationResult.current) { + const fsUtility = new fileSystemUtility() + fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs) } + } - useEffect(() => { - async function loadStorage() { - await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported']) - await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported']) - await testmigration() - remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() - localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() - remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) - !remixIndexedDB.current.loaded && await setFileSystems() - } - loadStorage() - }, []) + useEffect(() => { + async function loadStorage() { + await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported']) + await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported']) + await testmigration() + remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() + localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() + remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) + !remixIndexedDB.current.loaded && await setFileSystems() + } + loadStorage() + }, []) - return <> -
-
- {logo} -
+ return <> +
+
+ {logo} +
REMIX IDE -
- v{packageJson.version} -
-
- {!supported ? -
+
+ v{packageJson.version} +
+
+ {!supported ? +
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser. -
: null} - {error ? -
+
: null} + {error ? +
An unknown error has occurred while loading the application.

Doing a hard refresh might fix this issue:

-
+
Windows:

- Chrome: CTRL + F5 or CTRL + Reload Button

- Firefox: CTRL + SHIFT + R or CTRL + F5

-
-
+
+
MacOS:

- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button

-
-
+
+
Linux:

- Chrome & FireFox: CTRL + SHIFT + R

-
-
: null} - {showDownloader ? -
+
+
: null} + {showDownloader ? +
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work. -

+

You don't need to do anything else, your files will be available when the app loads. -
{ await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup
-
{ await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup
-
: null} - {(supported && !error && !showDownloader) ? -
- -
: null} -
- +
{ await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup
+
{ await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup
+
: null} + {(supported && !error && !showDownloader) ? +
+ +
: null} +
+ } const logo = - - - + + + diff --git a/apps/remix-ide/src/app/components/vertical-icons.tsx b/apps/remix-ide/src/app/components/vertical-icons.tsx index 24d5b93b2b..87f78ffd40 100644 --- a/apps/remix-ide/src/app/components/vertical-icons.tsx +++ b/apps/remix-ide/src/app/components/vertical-icons.tsx @@ -113,8 +113,8 @@ export class VerticalIcons extends Plugin { updateComponent(state: any){ return } diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 9a0d58e2e5..2de1353d15 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -99,8 +99,8 @@ class Editor extends Plugin { this.ref.clearDecorationsByPlugin = (filePath, plugin, typeOfDecoration) => this.clearDecorationsByPlugin(filePath, plugin, typeOfDecoration) this.ref.keepDecorationsFor = (name, typeOfDecoration) => this.keepDecorationsFor(name, typeOfDecoration) }} id='editorView'> - - + + } renderComponent () { @@ -293,7 +293,7 @@ class Editor extends Plugin { * Get the text in the current session, if any. * @param {string} url Address of the content to retrieve. */ - getText (url) { + getText (url) { if (this.sessions[url]) { return this.sessions[url].getValue() } diff --git a/apps/remix-ide/src/app/files/dgitProvider.js b/apps/remix-ide/src/app/files/dgitProvider.js index 9fd3fa7e37..d4c78d8099 100644 --- a/apps/remix-ide/src/app/files/dgitProvider.js +++ b/apps/remix-ide/src/app/files/dgitProvider.js @@ -110,7 +110,7 @@ class DGitProvider extends Plugin { ...cmd }) if (refresh) { - setTimeout(async () => { + setTimeout(async () => { await this.call('fileManager', 'refresh') }, 1000) } @@ -141,7 +141,7 @@ class DGitProvider extends Plugin { ...cmd }) if (refresh) { - setTimeout(async () => { + setTimeout(async () => { await this.call('fileManager', 'refresh') }, 1000) } diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index db42bc3335..2c3dcccda0 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -361,15 +361,15 @@ class FileManager extends Plugin { try { const downloadFileName = helper.extractNameFromKey(path) if (await this.isDirectory(path)) { - const zip = new JSZip() - await this.zipDir(path, zip) - const content = await zip.generateAsync({type: 'blob'}) - saveAs(content, `${downloadFileName}.zip`) - } else { - path = this.normalize(path) - const content: any = await this.readFile(path) - saveAs(new Blob([content]), downloadFileName) - } + const zip = new JSZip() + await this.zipDir(path, zip) + const content = await zip.generateAsync({type: 'blob'}) + saveAs(content, `${downloadFileName}.zip`) + } else { + path = this.normalize(path) + const content: any = await this.readFile(path) + saveAs(new Blob([content]), downloadFileName) + } } catch (e) { throw new Error(e) } @@ -777,7 +777,7 @@ class FileManager extends Plugin { provider.set(currentFile, oldContent) return console.error(error) } else { - this.emit('fileSaved', currentFile) + this.emit('fileSaved', currentFile) } }) }) @@ -868,7 +868,7 @@ class FileManager extends Plugin { * @returns {void} */ - async moveFile(src: string, dest: string) { + async moveFile(src: string, dest: string) { try { src = this.normalize(src) dest = this.normalize(dest) diff --git a/apps/remix-ide/src/app/files/fileSystem.ts b/apps/remix-ide/src/app/files/fileSystem.ts index 80b7663680..80840f617f 100644 --- a/apps/remix-ide/src/app/files/fileSystem.ts +++ b/apps/remix-ide/src/app/files/fileSystem.ts @@ -1,71 +1,71 @@ export class fileSystem { - name: string - enabled: boolean - available: boolean - fs: any - fsCallBack: any; - hasWorkSpaces: boolean - loaded: boolean - load: () => Promise - test: () => Promise + name: string + enabled: boolean + available: boolean + fs: any + fsCallBack: any; + hasWorkSpaces: boolean + loaded: boolean + load: () => Promise + test: () => Promise - constructor() { - this.available = false - this.enabled = false - this.hasWorkSpaces = false - this.loaded = false - } + constructor() { + this.available = false + this.enabled = false + this.hasWorkSpaces = false + this.loaded = false + } - checkWorkspaces = async () => { - try { - await this.fs.stat('.workspaces') - this.hasWorkSpaces = true - } catch (e) { + checkWorkspaces = async () => { + try { + await this.fs.stat('.workspaces') + this.hasWorkSpaces = true + } catch (e) { - } } + } - set = async () => { - const w = (window as any) - if (!this.loaded) return false - w.remixFileSystem = this.fs - w.remixFileSystem.name = this.name - w.remixFileSystemCallback = this.fsCallBack - return true - } + set = async () => { + const w = (window as any) + if (!this.loaded) return false + w.remixFileSystem = this.fs + w.remixFileSystem.name = this.name + w.remixFileSystemCallback = this.fsCallBack + return true + } } export class fileSystems { - fileSystems: Record - constructor() { - this.fileSystems = {} - } + fileSystems: Record + constructor() { + this.fileSystems = {} + } - addFileSystem = async (fs: fileSystem): Promise => { - try { - this.fileSystems[fs.name] = fs - await fs.test() && await fs.load() - console.log(fs.name + ' is loaded...') - return true - } catch (e) { - console.log(fs.name + ' not available...') - return false - } + addFileSystem = async (fs: fileSystem): Promise => { + try { + this.fileSystems[fs.name] = fs + await fs.test() && await fs.load() + console.log(fs.name + ' is loaded...') + return true + } catch (e) { + console.log(fs.name + ' not available...') + return false } - /** + } + /** * sets filesystem using list as fallback * @param {string[]} names * @returns {Promise} */ - setFileSystem = async (filesystems?: fileSystem[]): Promise => { - for (const fs of filesystems) { - if (fs && this.fileSystems[fs.name]) { - const result = await this.fileSystems[fs.name].set() - if (result) return this.fileSystems[fs.name] - } - } - return null + setFileSystem = async (filesystems?: fileSystem[]): Promise => { + for (const fs of filesystems) { + if (fs && this.fileSystems[fs.name]) { + const result = await this.fileSystems[fs.name].set() + if (result) return this.fileSystems[fs.name] + } } + return null + } } diff --git a/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts b/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts index 538679c671..9fc716b5c1 100644 --- a/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts +++ b/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts @@ -3,189 +3,189 @@ import JSZip from "jszip" import { fileSystem } from "../fileSystem" const _paq = window._paq = window._paq || [] export class fileSystemUtility { - migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { - try { - await fsFrom.checkWorkspaces() - await fsTo.checkWorkspaces() - - if (fsTo.hasWorkSpaces) { - console.log(`${fsTo.name} already has files`) - return true - } - - if (!fsFrom.hasWorkSpaces) { - console.log('no files to migrate') - return true - } - - const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) - await this.populateWorkspace(fromFiles, fsTo.fs) - const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) - - if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { - console.log('file migration successful') - return true - } else { - _paq.push(['trackEvent', 'Migrate', 'error', 'hash mismatch']) - console.log('file migration failed falling back to ' + fsFrom.name) - fsTo.loaded = false - return false - } - } catch (err) { - console.log(err) - _paq.push(['trackEvent', 'Migrate', 'error', err && err.message]) - console.log('file migration failed falling back to ' + fsFrom.name) - fsTo.loaded = false - return false - } + migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { + try { + await fsFrom.checkWorkspaces() + await fsTo.checkWorkspaces() + + if (fsTo.hasWorkSpaces) { + console.log(`${fsTo.name} already has files`) + return true + } + + if (!fsFrom.hasWorkSpaces) { + console.log('no files to migrate') + return true + } + + const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) + await this.populateWorkspace(fromFiles, fsTo.fs) + const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) + + if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { + console.log('file migration successful') + return true + } else { + _paq.push(['trackEvent', 'Migrate', 'error', 'hash mismatch']) + console.log('file migration failed falling back to ' + fsFrom.name) + fsTo.loaded = false + return false + } + } catch (err) { + console.log(err) + _paq.push(['trackEvent', 'Migrate', 'error', err && err.message]) + console.log('file migration failed falling back to ' + fsFrom.name) + fsTo.loaded = false + return false } - - downloadBackup = async (fs: fileSystem) => { - try { - const zip = new JSZip() - zip.file("readme.txt", "This is a Remix backup file.\nThis zip should be used by the restore backup tool in Remix.\nThe .workspaces directory contains your workspaces.") - await fs.checkWorkspaces() - await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { - zip.file(path, content) - }) - const blob = await zip.generateAsync({ type: 'blob' }) - const today = new Date() - const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() - const time = today.getHours() + 'h' + today.getMinutes() + 'min' - this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) - _paq.push(['trackEvent','Backup','download','preload']) - } catch (err) { - _paq.push(['trackEvent','Backup','error',err && err.message]) - console.log(err) - } + } + + downloadBackup = async (fs: fileSystem) => { + try { + const zip = new JSZip() + zip.file("readme.txt", "This is a Remix backup file.\nThis zip should be used by the restore backup tool in Remix.\nThe .workspaces directory contains your workspaces.") + await fs.checkWorkspaces() + await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { + zip.file(path, content) + }) + const blob = await zip.generateAsync({ type: 'blob' }) + const today = new Date() + const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() + const time = today.getHours() + 'h' + today.getMinutes() + 'min' + this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) + _paq.push(['trackEvent','Backup','download','preload']) + } catch (err) { + _paq.push(['trackEvent','Backup','error',err && err.message]) + console.log(err) } - - populateWorkspace = async (json, fs) => { - for (const item in json) { - const isFolder = json[item].content === undefined - if (isFolder) { - await this.createDir(item, fs) - await this.populateWorkspace(json[item].children, fs) - } else { - await fs.writeFile(item, json[item].content, 'utf8') - } - } + } + + populateWorkspace = async (json, fs) => { + for (const item in json) { + const isFolder = json[item].content === undefined + if (isFolder) { + await this.createDir(item, fs) + await this.populateWorkspace(json[item].children, fs) + } else { + await fs.writeFile(item, json[item].content, 'utf8') + } } + } - /** + /** * copy the folder recursively * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files * @param {Function} visitFolder is a function called for each visited folders */ - copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { - visitFile = visitFile || (() => { }) - visitFolder = visitFolder || (() => { }) - return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) - } + copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { + visitFile = visitFile || (() => { }) + visitFolder = visitFolder || (() => { }) + return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) + } - /** + /** * copy the folder recursively (internal use) * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files * @param {Function} visitFolder is a function called for each visited folders */ - async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { - visitFile = visitFile || function () { /* do nothing. */ } - visitFolder = visitFolder || function () { /* do nothing. */ } - - const json = {} - // path = this.removePrefix(path) - if (await fs.exists(path)) { - const items = await fs.readdir(path) - visitFolder({ path }) - if (items.length !== 0) { - for (const item of items) { - const file: any = {} - const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` - if ((await fs.stat(curPath)).isDirectory()) { - file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) - } else { - file.content = await fs.readFile(curPath, 'utf8') - if (cb) cb({ path: curPath, content: file.content }) - visitFile({ path: curPath, content: file.content }) - - } - json[curPath] = file - } - } - } - return json - } - - createDir = async (path, fs) => { - const paths = path.split('/') - if (paths.length && paths[0] === '') paths.shift() - let currentCheck = '' - for (const value of paths) { - currentCheck = currentCheck + (currentCheck ? '/' : '') + value - if (!await fs.exists(currentCheck)) { - await fs.mkdir(currentCheck) - } + async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { + visitFile = visitFile || function () { /* do nothing. */ } + visitFolder = visitFolder || function () { /* do nothing. */ } + + const json = {} + // path = this.removePrefix(path) + if (await fs.exists(path)) { + const items = await fs.readdir(path) + visitFolder({ path }) + if (items.length !== 0) { + for (const item of items) { + const file: any = {} + const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` + if ((await fs.stat(curPath)).isDirectory()) { + file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) + } else { + file.content = await fs.readFile(curPath, 'utf8') + if (cb) cb({ path: curPath, content: file.content }) + visitFile({ path: curPath, content: file.content }) + + } + json[curPath] = file } + } } - - saveAs = (blob, name) => { - const node = document.createElement('a') - node.download = name - node.rel = 'noopener' - node.href = URL.createObjectURL(blob) - setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s - setTimeout(function () { - try { - node.dispatchEvent(new MouseEvent('click')) - } catch (e) { - const evt = document.createEvent('MouseEvents') - evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, - 20, false, false, false, false, 0, null) - node.dispatchEvent(evt) - } - }, 0) // 40s + return json + } + + createDir = async (path, fs) => { + const paths = path.split('/') + if (paths.length && paths[0] === '') paths.shift() + let currentCheck = '' + for (const value of paths) { + currentCheck = currentCheck + (currentCheck ? '/' : '') + value + if (!await fs.exists(currentCheck)) { + await fs.mkdir(currentCheck) + } } + } + + saveAs = (blob, name) => { + const node = document.createElement('a') + node.download = name + node.rel = 'noopener' + node.href = URL.createObjectURL(blob) + setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s + setTimeout(function () { + try { + node.dispatchEvent(new MouseEvent('click')) + } catch (e) { + const evt = document.createEvent('MouseEvents') + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, + 20, false, false, false, false, 0, null) + node.dispatchEvent(evt) + } + }, 0) // 40s + } } /* eslint-disable no-template-curly-in-string */ export const migrationTestData = { - '.workspaces': { + '.workspaces': { + children: { + '.workspaces/default_workspace': { children: { - '.workspaces/default_workspace': { - children: { - '.workspaces/default_workspace/README.txt': { - content: 'TEST README' - } - } - }, - '.workspaces/emptyspace': { + '.workspaces/default_workspace/README.txt': { + content: 'TEST README' + } + } + }, + '.workspaces/emptyspace': { - }, - '.workspaces/workspace_test': { + }, + '.workspaces/workspace_test': { + children: { + '.workspaces/workspace_test/TEST_README.txt': { + content: 'TEST README' + }, + '.workspaces/workspace_test/test_contracts': { + children: { + '.workspaces/workspace_test/test_contracts/1_Storage.sol': { + content: 'testing' + }, + '.workspaces/workspace_test/test_contracts/artifacts': { children: { - '.workspaces/workspace_test/TEST_README.txt': { - content: 'TEST README' - }, - '.workspaces/workspace_test/test_contracts': { - children: { - '.workspaces/workspace_test/test_contracts/1_Storage.sol': { - content: 'testing' - }, - '.workspaces/workspace_test/test_contracts/artifacts': { - children: { - '.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { - content: '{ "test": "data" }' - } - } - } - } - } + '.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { + content: '{ "test": "data" }' + } } + } } + } } + } } + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/files/filesystems/indexedDB.ts b/apps/remix-ide/src/app/files/filesystems/indexedDB.ts index 5d20a52061..aae0d6a5eb 100644 --- a/apps/remix-ide/src/app/files/filesystems/indexedDB.ts +++ b/apps/remix-ide/src/app/files/filesystems/indexedDB.ts @@ -2,90 +2,90 @@ import LightningFS from "@isomorphic-git/lightning-fs" import { fileSystem } from "../fileSystem" export class IndexedDBStorage extends LightningFS { - base: LightningFS.PromisifedFS - addSlash: (file: string) => string - extended: { exists: (path: string) => Promise; rmdir: (path: any) => Promise; readdir: (path: any) => Promise; unlink: (path: any) => Promise; mkdir: (path: any) => Promise; readFile: (path: any, options: any) => Promise; rename: (from: any, to: any) => Promise; writeFile: (path: any, content: any, options: any) => Promise; stat: (path: any) => Promise; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise; deactivate(): Promise; lstat(filePath: string): Promise; readlink(filePath: string): Promise; symlink(target: string, filePath: string): Promise } - constructor(name: string) { - super(name) - this.addSlash = (file) => { - if (!file.startsWith('/')) file = '/' + file - return file - } - this.base = this.promises - this.extended = { - ...this.promises, - exists: async (path: string) => { - return new Promise((resolve) => { - this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) - }) - }, - rmdir: async (path) => { - return this.base.rmdir(this.addSlash(path)) - }, - readdir: async (path) => { - return this.base.readdir(this.addSlash(path)) - }, - unlink: async (path) => { - return this.base.unlink(this.addSlash(path)) - }, - mkdir: async (path) => { - return this.base.mkdir(this.addSlash(path)) - }, - readFile: async (path, options) => { - return this.base.readFile(this.addSlash(path), options) - }, - rename: async (from, to) => { - return this.base.rename(this.addSlash(from), this.addSlash(to)) - }, - writeFile: async (path, content, options) => { - return this.base.writeFile(this.addSlash(path), content, options) - }, - stat: async (path) => { - return this.base.stat(this.addSlash(path)) - } - } + base: LightningFS.PromisifedFS + addSlash: (file: string) => string + extended: { exists: (path: string) => Promise; rmdir: (path: any) => Promise; readdir: (path: any) => Promise; unlink: (path: any) => Promise; mkdir: (path: any) => Promise; readFile: (path: any, options: any) => Promise; rename: (from: any, to: any) => Promise; writeFile: (path: any, content: any, options: any) => Promise; stat: (path: any) => Promise; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise; deactivate(): Promise; lstat(filePath: string): Promise; readlink(filePath: string): Promise; symlink(target: string, filePath: string): Promise } + constructor(name: string) { + super(name) + this.addSlash = (file) => { + if (!file.startsWith('/')) file = '/' + file + return file } + this.base = this.promises + this.extended = { + ...this.promises, + exists: async (path: string) => { + return new Promise((resolve) => { + this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) + }) + }, + rmdir: async (path) => { + return this.base.rmdir(this.addSlash(path)) + }, + readdir: async (path) => { + return this.base.readdir(this.addSlash(path)) + }, + unlink: async (path) => { + return this.base.unlink(this.addSlash(path)) + }, + mkdir: async (path) => { + return this.base.mkdir(this.addSlash(path)) + }, + readFile: async (path, options) => { + return this.base.readFile(this.addSlash(path), options) + }, + rename: async (from, to) => { + return this.base.rename(this.addSlash(from), this.addSlash(to)) + }, + writeFile: async (path, content, options) => { + return this.base.writeFile(this.addSlash(path), content, options) + }, + stat: async (path) => { + return this.base.stat(this.addSlash(path)) + } + } + } } export class indexedDBFileSystem extends fileSystem { - constructor() { - super() - this.name = 'indexedDB' - } + constructor() { + super() + this.name = 'indexedDB' + } - load = async () => { - return new Promise((resolve, reject) => { - try { - const fs = new IndexedDBStorage('RemixFileSystem') - fs.init('RemixFileSystem') - this.fs = fs.extended - this.fsCallBack = fs - this.loaded = true - resolve(true) - } catch (e) { - reject(e) - } - }) - } + load = async () => { + return new Promise((resolve, reject) => { + try { + const fs = new IndexedDBStorage('RemixFileSystem') + fs.init('RemixFileSystem') + this.fs = fs.extended + this.fsCallBack = fs + this.loaded = true + resolve(true) + } catch (e) { + reject(e) + } + }) + } - test = async () => { - return new Promise((resolve, reject) => { - if (!window.indexedDB) { - this.available = false - reject('No indexedDB on window') - } - const request = window.indexedDB.open("RemixTestDataBase"); - request.onerror = () => { - this.available = false - reject('Error creating test database') - }; - request.onsuccess = () => { - window.indexedDB.deleteDatabase("RemixTestDataBase"); - this.available = true - resolve(true) - }; - }) - } + test = async () => { + return new Promise((resolve, reject) => { + if (!window.indexedDB) { + this.available = false + reject('No indexedDB on window') + } + const request = window.indexedDB.open("RemixTestDataBase"); + request.onerror = () => { + this.available = false + reject('Error creating test database') + }; + request.onsuccess = () => { + window.indexedDB.deleteDatabase("RemixTestDataBase"); + this.available = true + resolve(true) + }; + }) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/files/filesystems/localStorage.ts b/apps/remix-ide/src/app/files/filesystems/localStorage.ts index 8346a37976..d99c3ae296 100644 --- a/apps/remix-ide/src/app/files/filesystems/localStorage.ts +++ b/apps/remix-ide/src/app/files/filesystems/localStorage.ts @@ -2,56 +2,56 @@ import { fileSystem } from "../fileSystem"; export class localStorageFS extends fileSystem { - constructor() { - super() - this.name = 'localstorage' - } - load = async () => { - const me = this - return new Promise((resolve, reject) => { - try { - const w = window as any - w.BrowserFS.install(window) - w.BrowserFS.configure({ - fs: 'LocalStorage' - }, async function (e) { - if (e) { - console.log('BrowserFS Error: ' + e) - reject(e) - } else { - me.fs = { ...window.require('fs') } - me.fsCallBack = window.require('fs') - me.fs.readdir = me.fs.readdirSync - me.fs.readFile = me.fs.readFileSync - me.fs.writeFile = me.fs.writeFileSync - me.fs.stat = me.fs.statSync - me.fs.unlink = me.fs.unlinkSync - me.fs.rmdir = me.fs.rmdirSync - me.fs.mkdir = me.fs.mkdirSync - me.fs.rename = me.fs.renameSync - me.fs.exists = me.fs.existsSync - me.loaded = true - resolve(true) - } - }) - } catch (e) { - console.log('BrowserFS is not ready!') - reject(e) - } + constructor() { + super() + this.name = 'localstorage' + } + load = async () => { + const me = this + return new Promise((resolve, reject) => { + try { + const w = window as any + w.BrowserFS.install(window) + w.BrowserFS.configure({ + fs: 'LocalStorage' + }, async function (e) { + if (e) { + console.log('BrowserFS Error: ' + e) + reject(e) + } else { + me.fs = { ...window.require('fs') } + me.fsCallBack = window.require('fs') + me.fs.readdir = me.fs.readdirSync + me.fs.readFile = me.fs.readFileSync + me.fs.writeFile = me.fs.writeFileSync + me.fs.stat = me.fs.statSync + me.fs.unlink = me.fs.unlinkSync + me.fs.rmdir = me.fs.rmdirSync + me.fs.mkdir = me.fs.mkdirSync + me.fs.rename = me.fs.renameSync + me.fs.exists = me.fs.existsSync + me.loaded = true + resolve(true) + } }) - } + } catch (e) { + console.log('BrowserFS is not ready!') + reject(e) + } + }) + } - test = async () => { - return new Promise((resolve, reject) => { - const test = 'test'; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - resolve(true) - } catch(e) { - reject(e) - } - }) - } + test = async () => { + return new Promise((resolve, reject) => { + const test = 'test'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + resolve(true) + } catch(e) { + reject(e) + } + }) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/panels/layout.ts b/apps/remix-ide/src/app/panels/layout.ts index a88fb97584..3a44d1f83b 100644 --- a/apps/remix-ide/src/app/panels/layout.ts +++ b/apps/remix-ide/src/app/panels/layout.ts @@ -61,9 +61,9 @@ export class Layout extends Plugin { }) this.on('manager', 'activate', (profile: Profile) => { switch (profile.name) { - case 'filePanel': - this.call('menuicons', 'select', 'filePanel') - break + case 'filePanel': + this.call('menuicons', 'select', 'filePanel') + break } }) this.on('sidePanel', 'focusChanged', async (name) => { diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index dac831c0c8..83620b9264 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -114,9 +114,9 @@ class Terminal extends Plugin { updateComponent(state) { return + plugin={state.plugin} + onReady={state.onReady} + /> } renderComponent () { diff --git a/apps/remix-ide/src/app/plugins/code-format.ts b/apps/remix-ide/src/app/plugins/code-format.ts index 1d74cff422..04266c1615 100644 --- a/apps/remix-ide/src/app/plugins/code-format.ts +++ b/apps/remix-ide/src/app/plugins/code-format.ts @@ -8,279 +8,279 @@ import toml from 'toml' import { filePathFilter, AnyFilter } from '@jsdevtools/file-path-filter' const profile = { - name: 'codeFormatter', - desciption: 'prettier plugin for Remix', - methods: ['format'], - events: [''], - version: '0.0.1' + name: 'codeFormatter', + desciption: 'prettier plugin for Remix', + methods: ['format'], + events: [''], + version: '0.0.1' } const defaultOptions = { - "overrides": [ - { - "files": "*.sol", - "options": { - "printWidth": 80, - "tabWidth": 4, - "useTabs": false, - "singleQuote": false, - "bracketSpacing": false, - } - }, - { - "files": "*.yml", - "options": { - } - }, - { - "files": "*.yaml", - "options": { - } - }, - { - "files": "*.toml", - "options": { - } - }, - { - "files": "*.json", - "options": { - } - }, - { - "files": "*.js", - "options": { - } - }, - { - "files": "*.ts", - "options": { - } - } - ] + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false, + } + }, + { + "files": "*.yml", + "options": { + } + }, + { + "files": "*.yaml", + "options": { + } + }, + { + "files": "*.toml", + "options": { + } + }, + { + "files": "*.json", + "options": { + } + }, + { + "files": "*.js", + "options": { + } + }, + { + "files": "*.ts", + "options": { + } + } + ] } export class CodeFormat extends Plugin { - prettier: any - ts: any - babel: any - espree: any - yml: any - sol: any + prettier: any + ts: any + babel: any + espree: any + yml: any + sol: any - constructor() { - super(profile) - } + constructor() { + super(profile) + } - async format(file: string) { + async format(file: string) { - // lazy load - if (!this.prettier) { - this.prettier = await import('prettier/standalone') - this.ts = await import('prettier/parser-typescript') - this.babel = await import('prettier/parser-babel') - this.espree = await import('prettier/parser-espree') - this.yml = await import('prettier/parser-yaml') - } + // lazy load + if (!this.prettier) { + this.prettier = await import('prettier/standalone') + this.ts = await import('prettier/parser-typescript') + this.babel = await import('prettier/parser-babel') + this.espree = await import('prettier/parser-espree') + this.yml = await import('prettier/parser-yaml') + } - try { - const content = await this.call('fileManager', 'readFile', file) - if (!content) return - let parserName = '' - let options: Options = { - } - switch (path.extname(file)) { - case '.sol': - parserName = 'solidity-parse' - break - case '.ts': - parserName = 'typescript' - options = { - ...options, - trailingComma: 'all', - semi: false, - singleQuote: true, - quoteProps: 'as-needed', - bracketSpacing: true, - arrowParens: 'always', - } - break - case '.js': - parserName = "espree" - options = { - ...options, - semi: false, - singleQuote: true, - } - break - case '.json': - parserName = 'json' - break - case '.yml': - parserName = 'yaml' - break - case '.yaml': - parserName = 'yaml' - break - } + try { + const content = await this.call('fileManager', 'readFile', file) + if (!content) return + let parserName = '' + let options: Options = { + } + switch (path.extname(file)) { + case '.sol': + parserName = 'solidity-parse' + break + case '.ts': + parserName = 'typescript' + options = { + ...options, + trailingComma: 'all', + semi: false, + singleQuote: true, + quoteProps: 'as-needed', + bracketSpacing: true, + arrowParens: 'always', + } + break + case '.js': + parserName = "espree" + options = { + ...options, + semi: false, + singleQuote: true, + } + break + case '.json': + parserName = 'json' + break + case '.yml': + parserName = 'yaml' + break + case '.yaml': + parserName = 'yaml' + break + } - if (file === '.prettierrc') { - parserName = 'json' - } + if (file === '.prettierrc') { + parserName = 'json' + } - const possibleFileNames = [ - '.prettierrc', - '.prettierrc.json', - '.prettierrc.yaml', - '.prettierrc.yml', - '.prettierrc.toml', - '.prettierrc.js', - '.prettierrc.cjs', - 'prettier.config.js', - 'prettier.config.cjs', - '.prettierrc.json5', - ] + const possibleFileNames = [ + '.prettierrc', + '.prettierrc.json', + '.prettierrc.yaml', + '.prettierrc.yml', + '.prettierrc.toml', + '.prettierrc.js', + '.prettierrc.cjs', + 'prettier.config.js', + 'prettier.config.cjs', + '.prettierrc.json5', + ] - const prettierConfigFile = await findAsync(possibleFileNames, async (fileName) => { - const exists = await this.call('fileManager', 'exists', fileName) - return exists - }) + const prettierConfigFile = await findAsync(possibleFileNames, async (fileName) => { + const exists = await this.call('fileManager', 'exists', fileName) + return exists + }) - let parsed = null - if (prettierConfigFile) { - let prettierConfig = await this.call('fileManager', 'readFile', prettierConfigFile) - if (prettierConfig) { - if (prettierConfigFile.endsWith('.yaml') || prettierConfigFile.endsWith('.yml')) { - try { - parsed = yaml.load(prettierConfig) - } catch (e) { - // do nothing - } - } else if (prettierConfigFile.endsWith('.toml')) { - try { - parsed = toml.parse(prettierConfig) - } catch (e) { - // do nothing - } - } else if (prettierConfigFile.endsWith('.json') || prettierConfigFile.endsWith('.json5')) { - try { - parsed = JSON.parse(prettierConfig) - } catch (e) { - // do nothing - } - } else if (prettierConfigFile === '.prettierrc') { - try { - parsed = JSON.parse(prettierConfig) - } catch (e) { - // do nothing - } - if (!parsed) { - try { - parsed = yaml.load(prettierConfig) - } catch (e) { - // do nothing - } - } - } else if (prettierConfigFile.endsWith('.js') || prettierConfigFile.endsWith('.cjs')) { - // remove any comments - prettierConfig = prettierConfig.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '') - // add quotes to keys - prettierConfig = prettierConfig.replace(/([a-zA-Z0-9_]+)(\s*):/g, '"$1"$2:') - // remove comma from last key - prettierConfig = prettierConfig.replace(/,(\s*})/g, '$1') - // remove any semi-colons - prettierConfig = prettierConfig.replace(/;/g, '') - // convert single quotes to double quotes - prettierConfig = prettierConfig.replace(/'/g, '"') - try { - parsed = JSON.parse(prettierConfig.replace('module.exports = ', '').replace('module.exports=', '')) - } catch (e) { - // do nothing - } - } - } - } else { - parsed = defaultOptions - await this.call('fileManager', 'writeFile', '.prettierrc.json', JSON.stringify(parsed, null, 2)) - await this.call('notification', 'toast', 'A prettier config file has been created in the workspace.') + let parsed = null + if (prettierConfigFile) { + let prettierConfig = await this.call('fileManager', 'readFile', prettierConfigFile) + if (prettierConfig) { + if (prettierConfigFile.endsWith('.yaml') || prettierConfigFile.endsWith('.yml')) { + try { + parsed = yaml.load(prettierConfig) + } catch (e) { + // do nothing } - - if (!parsed && prettierConfigFile) { - this.call('notification', 'toast', `Error parsing prettier config file: ${prettierConfigFile}`) + } else if (prettierConfigFile.endsWith('.toml')) { + try { + parsed = toml.parse(prettierConfig) + } catch (e) { + // do nothing + } + } else if (prettierConfigFile.endsWith('.json') || prettierConfigFile.endsWith('.json5')) { + try { + parsed = JSON.parse(prettierConfig) + } catch (e) { + // do nothing + } + } else if (prettierConfigFile === '.prettierrc') { + try { + parsed = JSON.parse(prettierConfig) + } catch (e) { + // do nothing + } + if (!parsed) { + try { + parsed = yaml.load(prettierConfig) + } catch (e) { + // do nothing + } + } + } else if (prettierConfigFile.endsWith('.js') || prettierConfigFile.endsWith('.cjs')) { + // remove any comments + prettierConfig = prettierConfig.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '') + // add quotes to keys + prettierConfig = prettierConfig.replace(/([a-zA-Z0-9_]+)(\s*):/g, '"$1"$2:') + // remove comma from last key + prettierConfig = prettierConfig.replace(/,(\s*})/g, '$1') + // remove any semi-colons + prettierConfig = prettierConfig.replace(/;/g, '') + // convert single quotes to double quotes + prettierConfig = prettierConfig.replace(/'/g, '"') + try { + parsed = JSON.parse(prettierConfig.replace('module.exports = ', '').replace('module.exports=', '')) + } catch (e) { + // do nothing } + } + } + } else { + parsed = defaultOptions + await this.call('fileManager', 'writeFile', '.prettierrc.json', JSON.stringify(parsed, null, 2)) + await this.call('notification', 'toast', 'A prettier config file has been created in the workspace.') + } + if (!parsed && prettierConfigFile) { + this.call('notification', 'toast', `Error parsing prettier config file: ${prettierConfigFile}`) + } - // merge options - if (parsed) { - options = { - ...options, - ...parsed, - } - } - // search for overrides - if (parsed && parsed.overrides) { - const override = parsed.overrides.find((override) => { - if (override.files) { - const pathFilter: AnyFilter = {} - pathFilter.include = setGlobalExpression(override.files) - const filteredFiles = [file] - .filter(filePathFilter(pathFilter)) - if (filteredFiles.length) { - return true - } - } - }) - const validParsers = ['typescript', 'babel', 'espree', 'solidity-parse', 'json', 'yaml', 'solidity-parse'] - if (override && override.options && override.options.parser) { - if (validParsers.includes(override.options.parser)) { - parserName = override.options.parser - } else { - this.call('notification', 'toast', `Invalid parser: ${override.options.parser}! Valid options are ${validParsers.join(', ')}`) - } - delete override.options.parser - } + // merge options + if (parsed) { + options = { + ...options, + ...parsed, + } + } - if (override) { - options = { - ...options, - ...override.options, - } - } + // search for overrides + if (parsed && parsed.overrides) { + const override = parsed.overrides.find((override) => { + if (override.files) { + const pathFilter: AnyFilter = {} + pathFilter.include = setGlobalExpression(override.files) + const filteredFiles = [file] + .filter(filePathFilter(pathFilter)) + if (filteredFiles.length) { + return true } + } + }) + const validParsers = ['typescript', 'babel', 'espree', 'solidity-parse', 'json', 'yaml', 'solidity-parse'] + if (override && override.options && override.options.parser) { + if (validParsers.includes(override.options.parser)) { + parserName = override.options.parser + } else { + this.call('notification', 'toast', `Invalid parser: ${override.options.parser}! Valid options are ${validParsers.join(', ')}`) + } + delete override.options.parser + } - - const result = this.prettier.format(content, { - plugins: [sol as any, this.ts, this.babel, this.espree, this.yml], - parser: parserName, - ...options - }) - await this.call('fileManager', 'writeFile', file, result) - } catch (e) { - // do nothing + if (override) { + options = { + ...options, + ...override.options, + } } + } + + + const result = this.prettier.format(content, { + plugins: [sol as any, this.ts, this.babel, this.espree, this.yml], + parser: parserName, + ...options + }) + await this.call('fileManager', 'writeFile', file, result) + } catch (e) { + // do nothing } + } } //*.sol, **/*.txt, contracts/* const setGlobalExpression = (paths: string) => { - const results = [] - paths.split(',').forEach(path => { - path = path.trim() - if (path.startsWith('*.')) path = path.replace(/(\*\.)/g, '**/*.') - if (path.endsWith('/*') && !path.endsWith('/**/*')) - path = path.replace(/(\*)/g, '**/*.*') - results.push(path) - }) - return results + const results = [] + paths.split(',').forEach(path => { + path = path.trim() + if (path.startsWith('*.')) path = path.replace(/(\*\.)/g, '**/*.') + if (path.endsWith('/*') && !path.endsWith('/**/*')) + path = path.replace(/(\*)/g, '**/*.*') + results.push(path) + }) + return results } async function findAsync(arr, asyncCallback) { - const promises = arr.map(asyncCallback); - const results = await Promise.all(promises); - const index = results.findIndex(result => result); - return arr[index]; + const promises = arr.map(asyncCallback); + const results = await Promise.all(promises); + const index = results.findIndex(result => result); + return arr[index]; } diff --git a/apps/remix-ide/src/app/plugins/code-format/index.ts b/apps/remix-ide/src/app/plugins/code-format/index.ts index 777009974c..32f785574c 100644 --- a/apps/remix-ide/src/app/plugins/code-format/index.ts +++ b/apps/remix-ide/src/app/plugins/code-format/index.ts @@ -8,54 +8,54 @@ import { parse } from './parser' // https://prettier.io/docs/en/plugins.html#languages // https://github.com/ikatyang/linguist-languages/blob/master/data/Solidity.json const languages = [ - { - linguistLanguageId: 237469032, - name: 'Solidity', - type: 'programming', - color: '#AA6746', - aceMode: 'text', - tmScope: 'source.solidity', - extensions: ['.sol'], - parsers: ['solidity-parse'], - vscodeLanguageIds: ['solidity'] - } + { + linguistLanguageId: 237469032, + name: 'Solidity', + type: 'programming', + color: '#AA6746', + aceMode: 'text', + tmScope: 'source.solidity', + extensions: ['.sol'], + parsers: ['solidity-parse'], + vscodeLanguageIds: ['solidity'] + } ]; // https://prettier.io/docs/en/plugins.html#parsers const parser = { astFormat: 'solidity-ast', parse, ...loc }; const parsers = { - 'solidity-parse': parser + 'solidity-parse': parser }; const canAttachComment = (node) => - node.type && node.type !== 'BlockComment' && node.type !== 'LineComment'; + node.type && node.type !== 'BlockComment' && node.type !== 'LineComment'; // https://prettier.io/docs/en/plugins.html#printers const printers = { - 'solidity-ast': { - canAttachComment, - handleComments: { - ownLine: handleComments.handleOwnLineComment, - endOfLine: handleComments.handleEndOfLineComment, - remaining: handleComments.handleRemainingComment - }, - isBlockComment: handleComments.isBlockComment, - massageAstNode, - print, - printComment - } + 'solidity-ast': { + canAttachComment, + handleComments: { + ownLine: handleComments.handleOwnLineComment, + endOfLine: handleComments.handleEndOfLineComment, + remaining: handleComments.handleRemainingComment + }, + isBlockComment: handleComments.isBlockComment, + massageAstNode, + print, + printComment + } }; // https://prettier.io/docs/en/plugins.html#defaultoptions const defaultOptions = { - bracketSpacing: false, - tabWidth: 4 + bracketSpacing: false, + tabWidth: 4 }; export default { - languages, - parsers, - printers, - options, - defaultOptions + languages, + parsers, + printers, + options, + defaultOptions }; diff --git a/apps/remix-ide/src/app/plugins/code-format/parser.ts b/apps/remix-ide/src/app/plugins/code-format/parser.ts index 15ef38f871..0fb913fed7 100644 --- a/apps/remix-ide/src/app/plugins/code-format/parser.ts +++ b/apps/remix-ide/src/app/plugins/code-format/parser.ts @@ -80,113 +80,113 @@ export function parse(text, _parsers, options) { }, BinaryOperation(ctx) { switch (ctx.operator) { - case '+': - case '-': - ctx.left = tryHug(ctx.left, ['%']); - ctx.right = tryHug(ctx.right, ['%']); - break; - case '*': - ctx.left = tryHug(ctx.left, ['/', '%']); - break; - case '/': - ctx.left = tryHug(ctx.left, ['*', '%']); - break; - case '%': - ctx.left = tryHug(ctx.left, ['*', '/', '%']); - break; - case '**': - // If the compiler has not been given as an option using we leave a**b**c. - if (!compiler) break; + case '+': + case '-': + ctx.left = tryHug(ctx.left, ['%']); + ctx.right = tryHug(ctx.right, ['%']); + break; + case '*': + ctx.left = tryHug(ctx.left, ['/', '%']); + break; + case '/': + ctx.left = tryHug(ctx.left, ['*', '%']); + break; + case '%': + ctx.left = tryHug(ctx.left, ['*', '/', '%']); + break; + case '**': + // If the compiler has not been given as an option using we leave a**b**c. + if (!compiler) break; - if (semver.satisfies(compiler, '<0.8.0')) { - // If the compiler is less than 0.8.0 then a**b**c is formatted as - // (a**b)**c. - ctx.left = tryHug(ctx.left, ['**']); - break; - } - if ( - ctx.left.type === 'BinaryOperation' && - ctx.left.operator === '**' - ) { - // the parser still organizes the a**b**c as (a**b)**c so we need - // to restructure it. - ctx.right = { - type: 'TupleExpression', - components: [ - { - type: 'BinaryOperation', - operator: '**', - left: ctx.left.right, - right: ctx.right - } - ], - isArray: false - }; - ctx.left = ctx.left.left; - } - break; - case '<<': - case '>>': - ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); - ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**']); - break; - case '&': - ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); - ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**', '<<', '>>']); - break; - case '|': - ctx.left = tryHug(ctx.left, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&', - '^' - ]); - ctx.right = tryHug(ctx.right, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&', - '^' - ]); - break; - case '^': - ctx.left = tryHug(ctx.left, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&' - ]); - ctx.right = tryHug(ctx.right, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&' - ]); - break; - case '||': - ctx.left = tryHug(ctx.left, ['&&']); - ctx.right = tryHug(ctx.right, ['&&']); - break; - case '&&': - default: + if (semver.satisfies(compiler, '<0.8.0')) { + // If the compiler is less than 0.8.0 then a**b**c is formatted as + // (a**b)**c. + ctx.left = tryHug(ctx.left, ['**']); break; + } + if ( + ctx.left.type === 'BinaryOperation' && + ctx.left.operator === '**' + ) { + // the parser still organizes the a**b**c as (a**b)**c so we need + // to restructure it. + ctx.right = { + type: 'TupleExpression', + components: [ + { + type: 'BinaryOperation', + operator: '**', + left: ctx.left.right, + right: ctx.right + } + ], + isArray: false + }; + ctx.left = ctx.left.left; + } + break; + case '<<': + case '>>': + ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); + ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**']); + break; + case '&': + ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); + ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**', '<<', '>>']); + break; + case '|': + ctx.left = tryHug(ctx.left, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&', + '^' + ]); + ctx.right = tryHug(ctx.right, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&', + '^' + ]); + break; + case '^': + ctx.left = tryHug(ctx.left, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&' + ]); + ctx.right = tryHug(ctx.right, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&' + ]); + break; + case '||': + ctx.left = tryHug(ctx.left, ['&&']); + ctx.right = tryHug(ctx.right, ['&&']); + break; + case '&&': + default: + break; } } }); diff --git a/apps/remix-ide/src/app/plugins/contractFlattener.tsx b/apps/remix-ide/src/app/plugins/contractFlattener.tsx index b61915372d..4f7cad9212 100644 --- a/apps/remix-ide/src/app/plugins/contractFlattener.tsx +++ b/apps/remix-ide/src/app/plugins/contractFlattener.tsx @@ -61,8 +61,8 @@ export class ContractFlattener extends Plugin { try{ dependencyGraph = getDependencyGraph(ast, filePath) sorted = dependencyGraph.isEmpty() - ? [filePath] - : dependencyGraph.sort().reverse() + ? [filePath] + : dependencyGraph.sort().reverse() sources = source.sources result = concatSourceFiles(sorted, sources) }catch(err){ diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts index 31aaf109be..34829b027b 100644 --- a/apps/remix-ide/src/app/plugins/file-decorator.ts +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -6,79 +6,79 @@ import { Plugin } from '@remixproject/engine' import { fileDecoration } from '@remix-ui/file-decorators' const profile = { - name: 'fileDecorator', - desciption: 'Keeps decorators of the files', - methods: ['setFileDecorators', 'clearFileDecorators', 'clearAllFileDecorators'], - events: ['fileDecoratorsChanged'], - version: '0.0.1' + name: 'fileDecorator', + desciption: 'Keeps decorators of the files', + methods: ['setFileDecorators', 'clearFileDecorators', 'clearAllFileDecorators'], + events: ['fileDecoratorsChanged'], + version: '0.0.1' } export class FileDecorator extends Plugin { - private _fileStates: fileDecoration[] = [] - constructor() { - super(profile) - } + private _fileStates: fileDecoration[] = [] + constructor() { + super(profile) + } - onActivation(): void { - this.on('filePanel', 'setWorkspace', async () => { - await this.clearAllFileDecorators() - }) - } + onActivation(): void { + this.on('filePanel', 'setWorkspace', async () => { + await this.clearAllFileDecorators() + }) + } - /** + /** * @param fileStates Array of file states */ - async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { - const { from } = this.currentRequest - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] - // clear all file states in the previous state of this owner on the files called - fileStatesPayload.forEach((state) => { - state.workspace = workspace - state.owner = from - }) - const filteredState = this._fileStates.filter((state) => { - const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { - return from == state.owner && payloadFileState.path == state.path - }) - return index == -1 - }) - const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) + async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { + const { from } = this.currentRequest + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] + // clear all file states in the previous state of this owner on the files called + fileStatesPayload.forEach((state) => { + state.workspace = workspace + state.owner = from + }) + const filteredState = this._fileStates.filter((state) => { + const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { + return from == state.owner && payloadFileState.path == state.path + }) + return index == -1 + }) + const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) - if (!deepequal(newState, this._fileStates)) { - this._fileStates = newState - this.emit('fileDecoratorsChanged', this._fileStates) - } + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) } + } - async clearFileDecorators(path?: string) { - const { from } = this.currentRequest - if (!from) return + async clearFileDecorators(path?: string) { + const { from } = this.currentRequest + if (!from) return - const filteredState = this._fileStates.filter((state) => { - if(state.owner != from) return true - if(path && state.path != path) return true - }) - const newState = [...filteredState].sort(sortByPath) - - if (!deepequal(newState, this._fileStates)) { - this._fileStates = newState - this.emit('fileDecoratorsChanged', this._fileStates) - } + const filteredState = this._fileStates.filter((state) => { + if(state.owner != from) return true + if(path && state.path != path) return true + }) + const newState = [...filteredState].sort(sortByPath) + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) } - async clearAllFileDecorators() { - this._fileStates = [] - this.emit('fileDecoratorsChanged', []) - } + } + + async clearAllFileDecorators() { + this._fileStates = [] + this.emit('fileDecoratorsChanged', []) + } } const sortByPath = (a: fileDecoration, b: fileDecoration) => { - if (a.path < b.path) { - return -1; - } - if (a.path > b.path) { - return 1; - } - return 0; + if (a.path < b.path) { + return -1; + } + if (a.path > b.path) { + return 1; + } + return 0; } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx index cd0445f1b2..b3e0955ece 100644 --- a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx +++ b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx @@ -15,14 +15,14 @@ import { antlr } from './types' import { ParseResult } from './types/antlr-types' const profile: Profile = { - 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', 'getImports'], - events: [], - version: '0.0.1' + 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', 'getImports'], + events: [], + version: '0.0.1' } export function isNodeDefinition(node: genericASTNode) { - return node.nodeType === 'ContractDefinition' || + return node.nodeType === 'ContractDefinition' || node.nodeType === 'FunctionDefinition' || node.nodeType === 'ModifierDefinition' || node.nodeType === 'VariableDeclaration' || @@ -58,640 +58,640 @@ interface codeParserIndex { export class CodeParser extends Plugin { - compilerAbstract: CompilerAbstract - currentFile: string - nodeIndex: codeParserIndex - astWalker: any - errorState: boolean = false - - gastEstimateTimeOut: any - - gasService: CodeParserGasService - compilerService: CodeParserCompiler - antlrService: CodeParserAntlrService - importService: CodeParserImports - - parseSolidity: (text: string) => Promise - getLastNodeInLine: (ast: string) => Promise - listAstNodes: () => Promise - getANTLRBlockAtPosition: (position: any, text?: string) => Promise - setCurrentFileAST: (text?: string) => Promise - getImports: () => Promise - - debuggerIsOn: boolean = false - - - constructor(astWalker: any) { - super(profile) - this.astWalker = astWalker - this.nodeIndex = { - declarations: [[]], - flatReferences: [], - nodesPerFile: {} - } - } - - async handleChangeEvents() { - const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') - if (completionSettings) { - this.antlrService.enableWorker() - } else { - this.antlrService.disableWorker() - } - const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') - const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') - if (showGasSettings || showErrorSettings || completionSettings || this.debuggerIsOn) { - await this.compilerService.compile() - } - } - - async onActivation() { - - this.gasService = new CodeParserGasService(this) - this.compilerService = new CodeParserCompiler(this) - this.antlrService = new CodeParserAntlrService(this) - this.importService = new CodeParserImports(this) - - this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService) - this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService) - this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService) - this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.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) => { - await this.call('editor', 'discardLineTexts') - await this.handleChangeEvents() - }) - - this.on('filePanel', 'setWorkspace', async () => { - 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', 'currentFileChanged', async () => { - await this.call('editor', 'discardLineTexts') - const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') - if (completionSettings) { - this.antlrService.setCurrentFileAST() - } - await this.handleChangeEvents() - }) - - this.on('solidity', 'loadingCompiler', async (url) => { - this.compilerService.compiler.loadVersion(true, `${url}?t=${Date.now()}`) - }) - - this.on('config', 'configChanged', async (config) => { - await this.reload() - }) - - this.on('settings', 'configChanged', async (config) => { - await this.reload() - }) - - await this.compilerService.init() - this.on('solidity', 'compilerLoaded', async () => { - await this.reload() - }) - - this.on('debugger', 'startDebugging', async () => { - this.debuggerIsOn = true - await this.reload() - }) - - this.on('debugger', 'stopDebugging', async () => { - this.debuggerIsOn = false - await this.reload() - }) - } - - async reload(){ - await this.call('editor', 'discardLineTexts') - await this.call('fileDecorator', 'clearFileDecorators') - await this.call('editor', 'clearErrorMarkers', [this.currentFile]) - await this.handleChangeEvents() - } - - /** + compilerAbstract: CompilerAbstract + currentFile: string + nodeIndex: codeParserIndex + astWalker: any + errorState: boolean = false + + gastEstimateTimeOut: any + + gasService: CodeParserGasService + compilerService: CodeParserCompiler + antlrService: CodeParserAntlrService + importService: CodeParserImports + + parseSolidity: (text: string) => Promise + getLastNodeInLine: (ast: string) => Promise + listAstNodes: () => Promise + getANTLRBlockAtPosition: (position: any, text?: string) => Promise + setCurrentFileAST: (text?: string) => Promise + getImports: () => Promise + + debuggerIsOn: boolean = false + + + constructor(astWalker: any) { + super(profile) + this.astWalker = astWalker + this.nodeIndex = { + declarations: [[]], + flatReferences: [], + nodesPerFile: {} + } + } + + async handleChangeEvents() { + const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') + if (completionSettings) { + this.antlrService.enableWorker() + } else { + this.antlrService.disableWorker() + } + const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') + const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') + if (showGasSettings || showErrorSettings || completionSettings || this.debuggerIsOn) { + await this.compilerService.compile() + } + } + + async onActivation() { + + this.gasService = new CodeParserGasService(this) + this.compilerService = new CodeParserCompiler(this) + this.antlrService = new CodeParserAntlrService(this) + this.importService = new CodeParserImports(this) + + this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService) + this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService) + this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService) + this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.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) => { + await this.call('editor', 'discardLineTexts') + await this.handleChangeEvents() + }) + + this.on('filePanel', 'setWorkspace', async () => { + 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', 'currentFileChanged', async () => { + await this.call('editor', 'discardLineTexts') + const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') + if (completionSettings) { + this.antlrService.setCurrentFileAST() + } + await this.handleChangeEvents() + }) + + this.on('solidity', 'loadingCompiler', async (url) => { + this.compilerService.compiler.loadVersion(true, `${url}?t=${Date.now()}`) + }) + + this.on('config', 'configChanged', async (config) => { + await this.reload() + }) + + this.on('settings', 'configChanged', async (config) => { + await this.reload() + }) + + await this.compilerService.init() + this.on('solidity', 'compilerLoaded', async () => { + await this.reload() + }) + + this.on('debugger', 'startDebugging', async () => { + this.debuggerIsOn = true + await this.reload() + }) + + this.on('debugger', 'stopDebugging', async () => { + this.debuggerIsOn = false + await this.reload() + }) + } + + async reload(){ + await this.call('editor', 'discardLineTexts') + await this.call('fileDecorator', 'clearFileDecorators') + await this.call('editor', 'clearErrorMarkers', [this.currentFile]) + await this.handleChangeEvents() + } + + /** * * @returns */ - async getLastCompilationResult() { - return this.compilerAbstract - } + async getLastCompilationResult() { + return this.compilerAbstract + } - getSubNodes(node: T): number[] { - return node.nodeType == "ContractDefinition" && node.contractDependencies; - } + getSubNodes(node: T): number[] { + return node.nodeType == "ContractDefinition" && node.contractDependencies; + } - /** + /** * Builds a flat index and declarations of all the nodes in the compilation result * @param compilationResult * @param source */ - _buildIndex(compilationResult: CompilationResult, source) { - if (compilationResult && compilationResult.sources) { - const callback = (node: genericASTNode) => { - if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { - if (!this.nodeIndex.declarations[node.referencedDeclaration]) { - this.nodeIndex.declarations[node.referencedDeclaration] = [] - } - this.nodeIndex.declarations[node.referencedDeclaration].push(node) - } - this.nodeIndex.flatReferences[node.id] = node - } - for (const s in compilationResult.sources) { - this.astWalker.walkFull(compilationResult.sources[s].ast, callback) - } - + _buildIndex(compilationResult: CompilationResult, source) { + if (compilationResult && compilationResult.sources) { + const callback = (node: genericASTNode) => { + if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { + if (!this.nodeIndex.declarations[node.referencedDeclaration]) { + this.nodeIndex.declarations[node.referencedDeclaration] = [] + } + this.nodeIndex.declarations[node.referencedDeclaration].push(node) } + this.nodeIndex.flatReferences[node.id] = node + } + for (const s in compilationResult.sources) { + this.astWalker.walkFull(compilationResult.sources[s].ast, callback) + } } - // NODE HELPERS + } - _getInputParams(node: FunctionDefinitionAstNode) { - const params = [] - const target = node.parameters - if (target) { - const children = target.parameters - for (const j in children) { - if (children[j].nodeType === 'VariableDeclaration') { - params.push(children[j].typeDescriptions.typeString) - } - } + // NODE HELPERS + + _getInputParams(node: FunctionDefinitionAstNode) { + const params = [] + const target = node.parameters + if (target) { + const children = target.parameters + for (const j in children) { + if (children[j].nodeType === 'VariableDeclaration') { + params.push(children[j].typeDescriptions.typeString) } - return '(' + params.toString() + ')' + } } + return '(' + params.toString() + ')' + } - _flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) { - const index = {} - const contractName: string = contractNode.name - const callback = (node) => { - if (inScope && node.scope !== contractNode.id + _flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) { + const index = {} + const contractName: string = contractNode.name + const callback = (node) => { + if (inScope && node.scope !== contractNode.id && !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition')) - return - if (inScope) node.isClassNode = true; - node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) - node.functionName = node.name + this._getInputParams(node) - node.contractName = contractName - node.contractId = contractNode.id - index[node.id] = node - } - this.astWalker.walkFull(contractNode, callback) - return index - } - - _extractFileNodes(fileName: string, compilationResult: lastCompilationResult) { - if (compilationResult && compilationResult.data.sources && compilationResult.data.sources[fileName]) { - const source = compilationResult.data.sources[fileName] - const nodesByContract: any = {} - nodesByContract.imports = {} - nodesByContract.contracts = {} - this.astWalker.walkFull(source.ast, async (node) => { - if (node.nodeType === 'ContractDefinition') { - const flatNodes = this._flatNodeList(node, fileName, false, compilationResult) - node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult) - nodesByContract.contracts[node.name] = { contractDefinition: node, contractNodes: flatNodes } - const baseNodes = {} - const baseNodesWithBaseContractScope = {} - if (node.linearizedBaseContracts) { - for (const id of node.linearizedBaseContracts) { - if (id !== node.id) { - const baseContract = await this.getNodeById(id) - const callback = (node) => { - node.contractName = (baseContract as any).name - node.contractId = (baseContract as any).id - node.isBaseNode = true; - baseNodes[node.id] = node - if ((node.scope && node.scope === baseContract.id) + return + if (inScope) node.isClassNode = true; + node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) + node.functionName = node.name + this._getInputParams(node) + node.contractName = contractName + node.contractId = contractNode.id + index[node.id] = node + } + this.astWalker.walkFull(contractNode, callback) + return index + } + + _extractFileNodes(fileName: string, compilationResult: lastCompilationResult) { + if (compilationResult && compilationResult.data.sources && compilationResult.data.sources[fileName]) { + const source = compilationResult.data.sources[fileName] + const nodesByContract: any = {} + nodesByContract.imports = {} + nodesByContract.contracts = {} + this.astWalker.walkFull(source.ast, async (node) => { + if (node.nodeType === 'ContractDefinition') { + const flatNodes = this._flatNodeList(node, fileName, false, compilationResult) + node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult) + nodesByContract.contracts[node.name] = { contractDefinition: node, contractNodes: flatNodes } + const baseNodes = {} + const baseNodesWithBaseContractScope = {} + if (node.linearizedBaseContracts) { + for (const id of node.linearizedBaseContracts) { + if (id !== node.id) { + const baseContract = await this.getNodeById(id) + const callback = (node) => { + node.contractName = (baseContract as any).name + node.contractId = (baseContract as any).id + node.isBaseNode = true; + baseNodes[node.id] = node + if ((node.scope && node.scope === baseContract.id) || node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' - ) { - baseNodesWithBaseContractScope[node.id] = node - } - if (node.members) { - for (const member of node.members) { - member.contractName = (baseContract as any).name - member.contractId = (baseContract as any).id - member.isBaseNode = true; - } - } - } - this.astWalker.walkFull(baseContract, callback) - } - } + ) { + baseNodesWithBaseContractScope[node.id] = node + } + if (node.members) { + for (const member of node.members) { + member.contractName = (baseContract as any).name + member.contractId = (baseContract as any).id + member.isBaseNode = true; } - nodesByContract.contracts[node.name].baseNodes = baseNodes - nodesByContract.contracts[node.name].baseNodesWithBaseContractScope = baseNodesWithBaseContractScope - nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult) + } } - if (node.nodeType === 'ImportDirective') { + this.astWalker.walkFull(baseContract, callback) + } + } + } + nodesByContract.contracts[node.name].baseNodes = baseNodes + nodesByContract.contracts[node.name].baseNodesWithBaseContractScope = baseNodesWithBaseContractScope + nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult) + } + if (node.nodeType === 'ImportDirective') { - const imported = await this.resolveImports(node, {}) + const imported = await this.resolveImports(node, {}) - for (const importedNode of (Object.values(imported) as any)) { - if (importedNode.nodes) - for (const subNode of importedNode.nodes) { - nodesByContract.imports[subNode.id] = subNode - } - } - } - }) - return nodesByContract + for (const importedNode of (Object.values(imported) as any)) { + if (importedNode.nodes) + for (const subNode of importedNode.nodes) { + nodesByContract.imports[subNode.id] = subNode + } + } } - } - - _getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) { - - const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile] - for (const name in contracts) { - if (name === contractName) { - const contract = contracts[name] - const estimationObj = contract.evm && contract.evm.gasEstimates - - let executionCost = null - if (node.nodeType === 'FunctionDefinition') { - const visibility = node.visibility - if (node.kind !== 'constructor') { - const fnName = node.name - const fn = fnName + this._getInputParams(node) - - if (visibility === 'public' || visibility === 'external') { - executionCost = estimationObj === null ? '-' : estimationObj.external[fn] - } else if (visibility === 'private' || visibility === 'internal') { - executionCost = estimationObj === null ? '-' : estimationObj.internal[fn] - } - return { executionCost } - } else { - return { - creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost, - codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost, - } - } - } + }) + return nodesByContract + } + } + + _getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) { + + const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile] + for (const name in contracts) { + if (name === contractName) { + const contract = contracts[name] + const estimationObj = contract.evm && contract.evm.gasEstimates + + let executionCost = null + if (node.nodeType === 'FunctionDefinition') { + const visibility = node.visibility + if (node.kind !== 'constructor') { + const fnName = node.name + const fn = fnName + this._getInputParams(node) + + if (visibility === 'public' || visibility === 'external') { + executionCost = estimationObj === null ? '-' : estimationObj.external[fn] + } else if (visibility === 'private' || visibility === 'internal') { + executionCost = estimationObj === null ? '-' : estimationObj.internal[fn] + } + return { executionCost } + } else { + return { + creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost, + codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost, } + } } + } } + } - /** + /** * Nodes at position where position is a number, offset * @param position * @param type * @returns */ - async nodesAtPosition(position: number, type = ''): Promise { - let lastCompilationResult = this.compilerAbstract - if(this.debuggerIsOn) { - lastCompilationResult = await this.call('compilerArtefacts', 'get', '__last') - this.currentFile = await this.call('fileManager', 'file') - } - if (!lastCompilationResult) return [] - const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile) - if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data && lastCompilationResult.data.sources && lastCompilationResult.data.sources[this.currentFile]) { - const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file]) - return nodes - } - return [] - } - - /** + async nodesAtPosition(position: number, type = ''): Promise { + let lastCompilationResult = this.compilerAbstract + if(this.debuggerIsOn) { + lastCompilationResult = await this.call('compilerArtefacts', 'get', '__last') + this.currentFile = await this.call('fileManager', 'file') + } + if (!lastCompilationResult) return [] + const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile) + if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data && lastCompilationResult.data.sources && lastCompilationResult.data.sources[this.currentFile]) { + const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file]) + return nodes + } + return [] + } + + /** * * @param id * @returns */ - async getNodeById(id: number) { - for (const key in this.nodeIndex.flatReferences) { - if (this.nodeIndex.flatReferences[key].id === id) { - return this.nodeIndex.flatReferences[key] - } - } + async getNodeById(id: number) { + for (const key in this.nodeIndex.flatReferences) { + if (this.nodeIndex.flatReferences[key].id === id) { + return this.nodeIndex.flatReferences[key] + } } + } - /** + /** * * @param id * @returns */ - async getDeclaration(id: number) { - if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) return this.nodeIndex.declarations[id] - } + async getDeclaration(id: number) { + if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) return this.nodeIndex.declarations[id] + } - /** + /** * * @param scope * @returns */ - async getNodesWithScope(scope: number) { - const nodes = [] - for (const node of Object.values(this.nodeIndex.flatReferences)) { - if (node.scope === scope) nodes.push(node) - } - return nodes + async getNodesWithScope(scope: number) { + const nodes = [] + for (const node of Object.values(this.nodeIndex.flatReferences)) { + if (node.scope === scope) nodes.push(node) } + return nodes + } - /** + /** * * @param name * @returns */ - async getNodesWithName(name: string) { - const nodes: genericASTNode[] = [] - for (const node of Object.values(this.nodeIndex.flatReferences)) { - if (node.name === name) nodes.push(node) - } - return nodes - } - /** + async getNodesWithName(name: string) { + const nodes: genericASTNode[] = [] + for (const node of Object.values(this.nodeIndex.flatReferences)) { + if (node.name === name) nodes.push(node) + } + return nodes + } + /** * * @param node * @returns */ - declarationOf(node: T) { - if (node && ('referencedDeclaration' in node) && node.referencedDeclaration) { - return this.nodeIndex.flatReferences[node.referencedDeclaration] - } - return null + declarationOf(node: T) { + if (node && ('referencedDeclaration' in node) && node.referencedDeclaration) { + return this.nodeIndex.flatReferences[node.referencedDeclaration] } + return null + } - /** + /** * * @param position * @returns */ - async definitionAtPosition(position: number) { - const nodes = await this.nodesAtPosition(position) - let nodeDefinition: any - let node: genericASTNode - if (nodes && nodes.length && !this.errorState) { - node = nodes[nodes.length - 1] - nodeDefinition = node - if (!isNodeDefinition(node)) { - nodeDefinition = await this.declarationOf(node) || node - } - if (node.nodeType === 'ImportDirective') { - for (const key in this.nodeIndex.flatReferences) { - if (this.nodeIndex.flatReferences[key].id === node.sourceUnit) { - nodeDefinition = this.nodeIndex.flatReferences[key] - } - } - } - return nodeDefinition - } else { - const astNodes = await this.antlrService.listAstNodes() - if (astNodes && astNodes.length) { - for (const node of astNodes) { - if (node.range[0] <= position && node.range[1] >= position) { - if (nodeDefinition && nodeDefinition.range[0] < node.range[0]) { - nodeDefinition = node - } - if (!nodeDefinition) nodeDefinition = node - } - } - if (nodeDefinition && nodeDefinition.type && nodeDefinition.type === 'Identifier') { - const nodeForIdentifier = await this.findIdentifier(nodeDefinition) - if (nodeForIdentifier) nodeDefinition = nodeForIdentifier - } - return nodeDefinition + async definitionAtPosition(position: number) { + const nodes = await this.nodesAtPosition(position) + let nodeDefinition: any + let node: genericASTNode + if (nodes && nodes.length && !this.errorState) { + node = nodes[nodes.length - 1] + nodeDefinition = node + if (!isNodeDefinition(node)) { + nodeDefinition = await this.declarationOf(node) || node + } + if (node.nodeType === 'ImportDirective') { + for (const key in this.nodeIndex.flatReferences) { + if (this.nodeIndex.flatReferences[key].id === node.sourceUnit) { + nodeDefinition = this.nodeIndex.flatReferences[key] + } + } + } + return nodeDefinition + } else { + const astNodes = await this.antlrService.listAstNodes() + if (astNodes && astNodes.length) { + for (const node of astNodes) { + if (node.range[0] <= position && node.range[1] >= position) { + if (nodeDefinition && nodeDefinition.range[0] < node.range[0]) { + nodeDefinition = node } + if (!nodeDefinition) nodeDefinition = node + } } - + if (nodeDefinition && nodeDefinition.type && nodeDefinition.type === 'Identifier') { + const nodeForIdentifier = await this.findIdentifier(nodeDefinition) + if (nodeForIdentifier) nodeDefinition = nodeForIdentifier + } + return nodeDefinition + } } - async getContractNodes(contractName: string) { - if (this.nodeIndex.nodesPerFile + } + + async getContractNodes(contractName: string) { + if (this.nodeIndex.nodesPerFile && this.nodeIndex.nodesPerFile[this.currentFile] && this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] && this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName].contractNodes) { - return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] - } - return false + return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] } + return false + } - async getCurrentFileNodes() { - if (this.nodeIndex.nodesPerFile + async getCurrentFileNodes() { + if (this.nodeIndex.nodesPerFile && this.nodeIndex.nodesPerFile[this.currentFile]) { - return this.nodeIndex.nodesPerFile[this.currentFile] - } - return false + return this.nodeIndex.nodesPerFile[this.currentFile] } + return false + } - /** + /** * * @param identifierNode * @returns */ - async findIdentifier(identifierNode: any) { - const astNodes = await this.antlrService.listAstNodes() - for (const node of astNodes) { - if (node.name === identifierNode.name && node.nodeType !== 'Identifier') { - return node - } - } + async findIdentifier(identifierNode: any) { + const astNodes = await this.antlrService.listAstNodes() + for (const node of astNodes) { + if (node.name === identifierNode.name && node.nodeType !== 'Identifier') { + return node + } } + } - /** + /** * * @param node * @returns */ - async positionOfDefinition(node: genericASTNode): Promise { - if (node) { - if (node.src) { - const position = sourceMappingDecoder.decode(node.src) - if (position) { - return position - } - } + async positionOfDefinition(node: genericASTNode): Promise { + if (node) { + if (node.src) { + const position = sourceMappingDecoder.decode(node.src) + if (position) { + return position } - return null + } } + return null + } - /** + /** * * @param node * @param imported * @returns */ - async resolveImports(node: any, imported = {}) { - if (node.nodeType === 'ImportDirective' && !imported[node.sourceUnit]) { - const importNode: any = await this.getNodeById(node.sourceUnit) - imported[importNode.id] = importNode - if (importNode.nodes) { - for (const child of importNode.nodes) { - imported = await this.resolveImports(child, imported) - } - } + async resolveImports(node: any, imported = {}) { + if (node.nodeType === 'ImportDirective' && !imported[node.sourceUnit]) { + const importNode: any = await this.getNodeById(node.sourceUnit) + imported[importNode.id] = importNode + if (importNode.nodes) { + for (const child of importNode.nodes) { + imported = await this.resolveImports(child, imported) } - return imported + } } + return imported + } - /** + /** * * @param node * @returns */ - referencesOf(node: genericASTNode) { - const results: genericASTNode[] = [] - const highlights = (id: number) => { - if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) { - const refs = this.nodeIndex.declarations[id] - for (const ref in refs) { - const node = refs[ref] - results.push(node) - } - } - } - if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { - highlights(node.referencedDeclaration) - const current = this.nodeIndex.flatReferences[node.referencedDeclaration] - results.push(current) - } else { - highlights(node.id) + referencesOf(node: genericASTNode) { + const results: genericASTNode[] = [] + const highlights = (id: number) => { + if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) { + const refs = this.nodeIndex.declarations[id] + for (const ref in refs) { + const node = refs[ref] + results.push(node) } - return results + } + } + if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { + highlights(node.referencedDeclaration) + const current = this.nodeIndex.flatReferences[node.referencedDeclaration] + results.push(current) + } else { + highlights(node.id) } + return results + } - /** + /** * * @param position * @returns */ - async referrencesAtPosition(position: any): Promise { - const nodes = await this.nodesAtPosition(position) - if (nodes && nodes.length) { - const node = nodes[nodes.length - 1] - if (node) { - return this.referencesOf(node) - } - } + async referrencesAtPosition(position: any): Promise { + const nodes = await this.nodesAtPosition(position) + if (nodes && nodes.length) { + const node = nodes[nodes.length - 1] + if (node) { + return this.referencesOf(node) + } } + } - /** + /** * * @returns */ - async getNodes(): Promise { - return this.nodeIndex.flatReferences - } + async getNodes(): Promise { + return this.nodeIndex.flatReferences + } - /** + /** * * @param node * @returns */ - async getNodeLink(node: genericASTNode) { - const lineColumn = await this.getLineColumnOfNode(node) - const position = await this.positionOfDefinition(node) - if (this.compilerAbstract && this.compilerAbstract.source && position) { - const fileName = this.compilerAbstract.getSourceName(position.file) - return lineColumn ? `${fileName} ${lineColumn.start.line}:${lineColumn.start.column}` : null - } - return '' + async getNodeLink(node: genericASTNode) { + const lineColumn = await this.getLineColumnOfNode(node) + const position = await this.positionOfDefinition(node) + if (this.compilerAbstract && this.compilerAbstract.source && position) { + const fileName = this.compilerAbstract.getSourceName(position.file) + return lineColumn ? `${fileName} ${lineColumn.start.line}:${lineColumn.start.column}` : null } + return '' + } - /* + /* * @param node */ - async getLineColumnOfNode(node: any) { - const position = await this.positionOfDefinition(node) - return this.getLineColumnOfPosition(position) - } + async getLineColumnOfNode(node: any) { + const position = await this.positionOfDefinition(node) + return this.getLineColumnOfPosition(position) + } - /* + /* * @param position */ - async getLineColumnOfPosition(position: any) { - if (position) { - const fileName = this.compilerAbstract.getSourceName(position.file) - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(this.compilerAbstract.source.sources[fileName].content) - const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(position, lineBreaks) - return lineColumn - } + async getLineColumnOfPosition(position: any) { + if (position) { + const fileName = this.compilerAbstract.getSourceName(position.file) + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(this.compilerAbstract.source.sources[fileName].content) + const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(position, lineBreaks) + return lineColumn } + } - /** + /** * * @param node * @returns */ - async getNodeDocumentation(node: genericASTNode) { - if (("documentation" in node) && node.documentation && (node.documentation as any).text) { - let text = ''; - (node.documentation as any).text.split('\n').forEach(line => { - text += `${line.trim()}\n` - }) - return text - } + async getNodeDocumentation(node: genericASTNode) { + if (("documentation" in node) && node.documentation && (node.documentation as any).text) { + let text = ''; + (node.documentation as any).text.split('\n').forEach(line => { + text += `${line.trim()}\n` + }) + return text } + } - /** + /** * * @param node * @returns */ - async getVariableDeclaration(node: any) { - const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : '' - const nodeName = node.name && node.name.length ? node.name : '' - if (node.typeDescriptions && node.typeDescriptions.typeString) { - return `${node.typeDescriptions.typeString} ${nodeVisibility}${nodeName}` - } else { - if (node.typeName && node.typeName.name) { - return `${node.typeName.name} ${nodeVisibility}${nodeName}` - } - else if (node.typeName && node.typeName.namePath) { - return `${node.typeName.namePath} ${nodeVisibility}${nodeName}` - } - else { - return `${nodeName}${nodeName}` - } - } - } - - /** + async getVariableDeclaration(node: any) { + const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : '' + const nodeName = node.name && node.name.length ? node.name : '' + if (node.typeDescriptions && node.typeDescriptions.typeString) { + return `${node.typeDescriptions.typeString} ${nodeVisibility}${nodeName}` + } else { + if (node.typeName && node.typeName.name) { + return `${node.typeName.name} ${nodeVisibility}${nodeName}` + } + else if (node.typeName && node.typeName.namePath) { + return `${node.typeName.namePath} ${nodeVisibility}${nodeName}` + } + else { + return `${nodeName}${nodeName}` + } + } + } + + /** * * @param node * @returns */ - async getFunctionParamaters(node: any) { - const localParam = (node.parameters && node.parameters.parameters) || (node.parameters) - if (localParam) { - const params = [] - for (const param of localParam) { - params.push(await this.getVariableDeclaration(param)) - } - return `(${params.join(', ')})` - } - } - - /** + async getFunctionParamaters(node: any) { + const localParam = (node.parameters && node.parameters.parameters) || (node.parameters) + if (localParam) { + const params = [] + for (const param of localParam) { + params.push(await this.getVariableDeclaration(param)) + } + return `(${params.join(', ')})` + } + } + + /** * * @param node * @returns */ - async getFunctionReturnParameters(node: any) { - const localParam = (node.returnParameters && node.returnParameters.parameters) - if (localParam) { - const params = [] - for (const param of localParam) { - params.push(await this.getVariableDeclaration(param)) - } - return `(${params.join(', ')})` - } - } + async getFunctionReturnParameters(node: any) { + const localParam = (node.returnParameters && node.returnParameters.parameters) + if (localParam) { + const params = [] + for (const param of localParam) { + params.push(await this.getVariableDeclaration(param)) + } + return `(${params.join(', ')})` + } + } diff --git a/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts b/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts index 0b2b843c7e..c9e026d085 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts @@ -1,45 +1,45 @@ let parser: any self.onmessage = (e: MessageEvent) => { - const data: any = e.data - switch (data.cmd) { - case 'load': - { - (self as any).importScripts(e.data.url) - // @ts-ignore - parser = SolidityParser as any; + const data: any = e.data + switch (data.cmd) { + case 'load': + { + (self as any).importScripts(e.data.url) + // @ts-ignore + parser = SolidityParser as any; - self.postMessage({ - cmd: 'loaded', - }) - break - } + self.postMessage({ + cmd: 'loaded', + }) + break + } - case 'parse': - if (data.text && parser) { + case 'parse': + if (data.text && parser) { - try { - let startTime = performance.now() - const blocks = parser.parseBlock(data.text, { loc: true, range: true, tolerant: true }) - const blockDuration = performance.now() - startTime - startTime = performance.now() - const ast = parser.parse(data.text, { loc: true, range: true, tolerant: true }) - const endTime = performance.now() + try { + let startTime = performance.now() + const blocks = parser.parseBlock(data.text, { loc: true, range: true, tolerant: true }) + const blockDuration = performance.now() - startTime + startTime = performance.now() + const ast = parser.parse(data.text, { loc: true, range: true, tolerant: true }) + const endTime = performance.now() - self.postMessage({ - cmd: 'parsed', - timestamp: data.timestamp, - ast, - text: data.text, - file: data.file, - duration: endTime - startTime, - blockDuration, - blocks - }) - } catch (e) { - // do nothing - } + self.postMessage({ + cmd: 'parsed', + timestamp: data.timestamp, + ast, + text: data.text, + file: data.file, + duration: endTime - startTime, + blockDuration, + blocks + }) + } catch (e) { + // do nothing + } - } - break } + break + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts index ee28b2cc1b..3585dda157 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts @@ -20,13 +20,13 @@ interface BlockDefinition { } export default class CodeParserAntlrService { - plugin: CodeParser - worker: Worker - parserStartTime: number = 0 - workerTimer: NodeJS.Timer - parserThreshold: number = 10 - parserThresholdSampleAmount = 3 - cache: { + plugin: CodeParser + worker: Worker + parserStartTime: number = 0 + workerTimer: NodeJS.Timer + parserThreshold: number = 10 + parserThresholdSampleAmount = 3 + cache: { [name: string]: { text: string, ast: antlr.ParseResult | null, @@ -36,253 +36,253 @@ export default class CodeParserAntlrService { blockDurations?: number[] } } = {}; - constructor(plugin: CodeParser) { - this.plugin = plugin - this.createWorker() - } + constructor(plugin: CodeParser) { + this.plugin = plugin + this.createWorker() + } - createWorker() { - this.worker = new Worker(new URL('./antlr-worker', import.meta.url)) - this.worker.postMessage({ - cmd: 'load', - url: document.location.protocol + '//' + document.location.host + '/assets/js/parser/antlr.js', - }); - const self = this + createWorker() { + this.worker = new Worker(new URL('./antlr-worker', import.meta.url)) + this.worker.postMessage({ + cmd: 'load', + url: document.location.protocol + '//' + document.location.host + '/assets/js/parser/antlr.js', + }); + const self = this - this.worker.addEventListener('message', function (ev) { - switch (ev.data.cmd) { - case 'parsed': - if (ev.data.ast && self.parserStartTime === ev.data.timestamp) { - self.cache[ev.data.file] = { - ...self.cache[ev.data.file], - text: ev.data.text, - ast: ev.data.ast, - duration: ev.data.duration, - blocks: ev.data.blocks, - blockDurations: self.cache[ev.data.file].blockDurations? [...self.cache[ev.data.file].blockDurations.slice(-self.parserThresholdSampleAmount), ev.data.blockDuration]: [ev.data.blockDuration] - } - self.setFileParsingState(ev.data.file) - } - break; - } + this.worker.addEventListener('message', function (ev) { + switch (ev.data.cmd) { + case 'parsed': + if (ev.data.ast && self.parserStartTime === ev.data.timestamp) { + self.cache[ev.data.file] = { + ...self.cache[ev.data.file], + text: ev.data.text, + ast: ev.data.ast, + duration: ev.data.duration, + blocks: ev.data.blocks, + blockDurations: self.cache[ev.data.file].blockDurations? [...self.cache[ev.data.file].blockDurations.slice(-self.parserThresholdSampleAmount), ev.data.blockDuration]: [ev.data.blockDuration] + } + self.setFileParsingState(ev.data.file) + } + break; + } - }); - } + }); + } - setFileParsingState(file: string) { - if (this.cache[file]) { - if (this.cache[file].blockDurations && this.cache[file].blockDurations.length > this.parserThresholdSampleAmount) { - // calculate average of durations to determine if the parsing should be disabled - const values = [...this.cache[file].blockDurations] - const average = values.reduce((a, b) => a + b, 0) / values.length - if (average > this.parserThreshold) { - this.cache[file].parsingEnabled = false - } else { - this.cache[file].parsingEnabled = true - } - } - } + setFileParsingState(file: string) { + if (this.cache[file]) { + if (this.cache[file].blockDurations && this.cache[file].blockDurations.length > this.parserThresholdSampleAmount) { + // calculate average of durations to determine if the parsing should be disabled + const values = [...this.cache[file].blockDurations] + const average = values.reduce((a, b) => a + b, 0) / values.length + if (average > this.parserThreshold) { + this.cache[file].parsingEnabled = false + } else { + this.cache[file].parsingEnabled = true + } + } } + } - enableWorker() { - if (!this.workerTimer) { - this.workerTimer = setInterval(() => { - this.setCurrentFileAST() - }, 5000) - } + enableWorker() { + if (!this.workerTimer) { + this.workerTimer = setInterval(() => { + this.setCurrentFileAST() + }, 5000) } + } - disableWorker() { - clearInterval(this.workerTimer) - } + disableWorker() { + clearInterval(this.workerTimer) + } - async parseWithWorker(text: string, file: string) { - this.parserStartTime = Date.now() - this.worker.postMessage({ - cmd: 'parse', - text, - timestamp: this.parserStartTime, - file, - parsingEnabled: (this.cache[file] && this.cache[file].parsingEnabled) || true - }); + async parseWithWorker(text: string, file: string) { + this.parserStartTime = Date.now() + this.worker.postMessage({ + cmd: 'parse', + text, + timestamp: this.parserStartTime, + file, + parsingEnabled: (this.cache[file] && this.cache[file].parsingEnabled) || true + }); - } + } - async parseSolidity(text: string) { - const ast: antlr.ParseResult = (SolidityParser as any).parse(text, { loc: true, range: true, tolerant: true }) - return ast - } + async parseSolidity(text: string) { + const ast: antlr.ParseResult = (SolidityParser as any).parse(text, { loc: true, range: true, tolerant: true }) + return ast + } - /** + /** * Tries to parse the current file or the given text and returns the AST * If the parsing fails it returns the last successful AST for this file * @param text * @returns */ - async setCurrentFileAST(text: string | null = null) { - try { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { - const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) - if (!this.cache[this.plugin.currentFile]) { - this.cache[this.plugin.currentFile] = { - text: '', - ast: null, - parsingEnabled: true, - blockDurations: [] - } - } - if (this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].text !== fileContent) { - try { - await this.parseWithWorker(fileContent, this.plugin.currentFile) - } catch (e) { - // do nothing - } - } - } - } catch (e) { + async setCurrentFileAST(text: string | null = null) { + try { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { + const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) + if (!this.cache[this.plugin.currentFile]) { + this.cache[this.plugin.currentFile] = { + text: '', + ast: null, + parsingEnabled: true, + blockDurations: [] + } + } + if (this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].text !== fileContent) { + try { + await this.parseWithWorker(fileContent, this.plugin.currentFile) + } catch (e) { // do nothing + } } + } + } catch (e) { + // do nothing } + } - /** + /** * Lists the AST nodes from the current file parser * These nodes need to be changed to match the node types returned by the compiler * @returns */ - async listAstNodes() { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (!this.cache[this.plugin.currentFile]) return - const nodes: AstNode[] = []; - (SolidityParser as any).visit(this.cache[this.plugin.currentFile].ast, { - StateVariableDeclaration: (node: antlr.StateVariableDeclaration) => { - if (node.variables) { - for (const variable of node.variables) { - nodes.push({ ...variable, nodeType: 'VariableDeclaration', id: null, src: null }) - } - } - }, - VariableDeclaration: (node: antlr.VariableDeclaration) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - UserDefinedTypeName: (node: antlr.UserDefinedTypeName) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - FunctionDefinition: (node: antlr.FunctionDefinition) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - ContractDefinition: (node: antlr.ContractDefinition) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - MemberAccess: function (node: antlr.MemberAccess) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - Identifier: function (node: antlr.Identifier) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - EventDefinition: function (node: antlr.EventDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - ModifierDefinition: function (node: antlr.ModifierDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - InvalidNode: function (node: antlr.InvalidNode) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - EnumDefinition: function (node: antlr.EnumDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - StructDefinition: function (node: antlr.StructDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - } + async listAstNodes() { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (!this.cache[this.plugin.currentFile]) return + const nodes: AstNode[] = []; + (SolidityParser as any).visit(this.cache[this.plugin.currentFile].ast, { + StateVariableDeclaration: (node: antlr.StateVariableDeclaration) => { + if (node.variables) { + for (const variable of node.variables) { + nodes.push({ ...variable, nodeType: 'VariableDeclaration', id: null, src: null }) + } + } + }, + VariableDeclaration: (node: antlr.VariableDeclaration) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + UserDefinedTypeName: (node: antlr.UserDefinedTypeName) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + FunctionDefinition: (node: antlr.FunctionDefinition) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + ContractDefinition: (node: antlr.ContractDefinition) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + MemberAccess: function (node: antlr.MemberAccess) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + Identifier: function (node: antlr.Identifier) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + EventDefinition: function (node: antlr.EventDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + ModifierDefinition: function (node: antlr.ModifierDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + InvalidNode: function (node: antlr.InvalidNode) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + EnumDefinition: function (node: antlr.EnumDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + StructDefinition: function (node: antlr.StructDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + } - }) - return nodes - } + }) + return nodes + } - /** + /** * * @param ast * @returns */ - async getLastNodeInLine(ast: string) { - let lastNode: any - const checkLastNode = (node: antlr.MemberAccess | antlr.Identifier) => { - if (lastNode && lastNode.range && lastNode.range[1]) { - if (node.range[1] > lastNode.range[1]) { - lastNode = node - } - } else { - lastNode = node - } + async getLastNodeInLine(ast: string) { + let lastNode: any + const checkLastNode = (node: antlr.MemberAccess | antlr.Identifier) => { + if (lastNode && lastNode.range && lastNode.range[1]) { + if (node.range[1] > lastNode.range[1]) { + lastNode = node } + } else { + lastNode = node + } + } - (SolidityParser as any).visit(ast, { - MemberAccess: function (node: antlr.MemberAccess) { - checkLastNode(node) - }, - Identifier: function (node: antlr.Identifier) { - checkLastNode(node) - } - }) - if (lastNode && lastNode.expression) { - return lastNode.expression - } - return lastNode + (SolidityParser as any).visit(ast, { + MemberAccess: function (node: antlr.MemberAccess) { + checkLastNode(node) + }, + Identifier: function (node: antlr.Identifier) { + checkLastNode(node) + } + }) + if (lastNode && lastNode.expression) { + return lastNode.expression } - /* + return lastNode + } + /* * get the code blocks of the current file */ - async getCurrentFileBlocks(text: string | null = null) { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (this.cache[this.plugin.currentFile]) { - if (!this.cache[this.plugin.currentFile].parsingEnabled) { - return - } - } - if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { - const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) - try { - const startTime = Date.now() - const blocks = (SolidityParser as any).parseBlock(fileContent, { loc: true, range: true, tolerant: true }) - if(this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].blockDurations){ - this.cache[this.plugin.currentFile].blockDurations = [...this.cache[this.plugin.currentFile].blockDurations.slice(-this.parserThresholdSampleAmount), Date.now() - startTime] - this.setFileParsingState(this.plugin.currentFile) - } - if (blocks) this.cache[this.plugin.currentFile].blocks = blocks - return blocks - } catch (e) { - // do nothing - } + async getCurrentFileBlocks(text: string | null = null) { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (this.cache[this.plugin.currentFile]) { + if (!this.cache[this.plugin.currentFile].parsingEnabled) { + return + } + } + if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { + const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) + try { + const startTime = Date.now() + const blocks = (SolidityParser as any).parseBlock(fileContent, { loc: true, range: true, tolerant: true }) + if(this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].blockDurations){ + this.cache[this.plugin.currentFile].blockDurations = [...this.cache[this.plugin.currentFile].blockDurations.slice(-this.parserThresholdSampleAmount), Date.now() - startTime] + this.setFileParsingState(this.plugin.currentFile) } + if (blocks) this.cache[this.plugin.currentFile].blocks = blocks + return blocks + } catch (e) { + // do nothing + } } + } - /** + /** * Returns the block surrounding the given position * For example if the position is in the middle of a function, it will return the function * @param {position} position * @param {string} text // optional * @return {any} * */ - async getANTLRBlockAtPosition(position: any, text: string = null) { - const blocks: any[] = await this.getCurrentFileBlocks(text) + async getANTLRBlockAtPosition(position: any, text: string = null) { + const blocks: any[] = await this.getCurrentFileBlocks(text) - const walkAst = (blocks) => { - let nodeFound = null - for (const object of blocks) { - if (object.start <= position) { - nodeFound = object - break - } - } - return nodeFound + const walkAst = (blocks) => { + let nodeFound = null + for (const object of blocks) { + if (object.start <= position) { + nodeFound = object + break } - if (!blocks) return - blocks.reverse() - const block = walkAst(blocks) - return block + } + return nodeFound } + if (!blocks) return + blocks.reverse() + const block = walkAst(blocks) + return block + } } diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts index 42c718db2a..b3a1a9c322 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts @@ -34,253 +34,253 @@ type errorMarker = { file: string } export default class CodeParserCompiler { + plugin: CodeParser + compiler: any // used to compile the current file seperately from the main compiler + onAstFinished: (success: any, data: CompilationResult, source: CompilationSourceCode, input: any, version: any) => Promise; + errorState: boolean; + gastEstimateTimeOut: any + constructor( plugin: CodeParser - compiler: any // used to compile the current file seperately from the main compiler - onAstFinished: (success: any, data: CompilationResult, source: CompilationSourceCode, input: any, version: any) => Promise; - errorState: boolean; - gastEstimateTimeOut: any - constructor( - plugin: CodeParser - ) { - this.plugin = plugin - } - - init() { + ) { + this.plugin = plugin + } - this.onAstFinished = async (success, data: CompilationResult, source: CompilationSourceCode, input: any, version) => { - this.plugin.call('editor', 'clearAnnotations') - this.errorState = true - const result = new CompilerAbstract('soljson', data, source, input) - let allErrors: errorMarker[] = [] - if (data.errors || data.error) { - const file = await this.plugin.call('fileManager', 'getCurrentFile') - const currentFileContent = await this.plugin.call('fileManager', 'readFile', file) - const sources = result.getSourceCode().sources || [] - if (data.error) { - if (data.error.formattedMessage) { - // mark this file as error - const errorMarker = await this.createErrorMarker(data.error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) - allErrors = [...allErrors, errorMarker] - } - } else { - for (const error of data.errors) { - if (!error.sourceLocation) { - // mark this file as error - const errorMarker = await this.createErrorMarker(error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) - allErrors = [...allErrors, errorMarker] - } else { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content) - const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({ - start: error.sourceLocation.start, - length: error.sourceLocation.end - error.sourceLocation.start - }, lineBreaks) + init() { + this.onAstFinished = async (success, data: CompilationResult, source: CompilationSourceCode, input: any, version) => { + this.plugin.call('editor', 'clearAnnotations') + this.errorState = true + const result = new CompilerAbstract('soljson', data, source, input) + let allErrors: errorMarker[] = [] + if (data.errors || data.error) { + const file = await this.plugin.call('fileManager', 'getCurrentFile') + const currentFileContent = await this.plugin.call('fileManager', 'readFile', file) + const sources = result.getSourceCode().sources || [] + if (data.error) { + if (data.error.formattedMessage) { + // mark this file as error + const errorMarker = await this.createErrorMarker(data.error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) + allErrors = [...allErrors, errorMarker] + } + } else { + for (const error of data.errors) { + if (!error.sourceLocation) { + // mark this file as error + const errorMarker = await this.createErrorMarker(error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) + allErrors = [...allErrors, errorMarker] + } else { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content) + const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({ + start: error.sourceLocation.start, + length: error.sourceLocation.end - error.sourceLocation.start + }, lineBreaks) - 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)] - } - } + const filePath = error.sourceLocation.file + const fileTarget = await this.plugin.call('fileManager', 'getUrlFromPath', filePath) - allErrors = [...allErrors, await this.createErrorMarker(error, filePath, lineColumn)] - } - } + 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)] } + } - const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') - if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors) - this.addDecorators(allErrors, sources) - } else { - await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources) - await this.clearDecorators(result.getSourceCode().sources) + allErrors = [...allErrors, await this.createErrorMarker(error, filePath, lineColumn)] } + } + } + const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') + if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors) + this.addDecorators(allErrors, sources) + } else { + await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources) + await this.clearDecorators(result.getSourceCode().sources) + } - if (!data.sources) return - if (data.sources && Object.keys(data.sources).length === 0) return - this.plugin.compilerAbstract = new CompilerAbstract('soljson', data, source, input) - this.errorState = false - this.plugin.nodeIndex = { - declarations: {}, - flatReferences: {}, - nodesPerFile: {}, - } + if (!data.sources) return + if (data.sources && Object.keys(data.sources).length === 0) return + this.plugin.compilerAbstract = new CompilerAbstract('soljson', data, source, input) + this.errorState = false + this.plugin.nodeIndex = { + declarations: {}, + flatReferences: {}, + nodesPerFile: {}, + } - this.plugin._buildIndex(data, source) - // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository - this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) - await this.plugin.gasService.showGasEstimates() - this.plugin.emit('astFinished') - } - - this.compiler = new Compiler((url, cb) => this.plugin.call('contentImport', 'resolveAndSave', url, undefined).then((result) => cb(null, result)).catch((error) => cb(error.message))) - this.compiler.event.register('compilationFinished', this.onAstFinished) + + this.plugin._buildIndex(data, source) + // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository + this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) + await this.plugin.gasService.showGasEstimates() + this.plugin.emit('astFinished') } + + this.compiler = new Compiler((url, cb) => this.plugin.call('contentImport', 'resolveAndSave', url, undefined).then((result) => cb(null, result)).catch((error) => cb(error.message))) + this.compiler.event.register('compilationFinished', this.onAstFinished) + } - // COMPILER + // COMPILER - /** + /** * * @returns */ - async compile() { - try { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { - const state = await this.plugin.call('solidity', 'getCompilerState') - this.compiler.set('optimize', state.optimize) - this.compiler.set('evmVersion', state.evmVersion) - this.compiler.set('language', state.language) - this.compiler.set('runs', state.runs) - this.compiler.set('useFileConfiguration', true) - this.compiler.set('compilerRetriggerMode', CompilerRetriggerMode.retrigger) - const configFileContent = { - "language": "Solidity", - "settings": { - "optimizer": { - "enabled": state.optimize, - "runs": state.runs - }, - "outputSelection": { - "*": { - "": ["ast"], - "*": ["evm.gasEstimates"] - } - }, - "evmVersion": state.evmVersion && state.evmVersion.toString() || "berlin", - } - } - - this.compiler.set('configFileContent', JSON.stringify(configFileContent)) - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (!this.plugin.currentFile) return - const content = await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) - const sources = { [this.plugin.currentFile]: { content } } - this.compiler.compile(sources, this.plugin.currentFile) - } - } catch (e) { - // do nothing + async compile() { + try { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { + const state = await this.plugin.call('solidity', 'getCompilerState') + this.compiler.set('optimize', state.optimize) + this.compiler.set('evmVersion', state.evmVersion) + this.compiler.set('language', state.language) + this.compiler.set('runs', state.runs) + this.compiler.set('useFileConfiguration', true) + this.compiler.set('compilerRetriggerMode', CompilerRetriggerMode.retrigger) + const configFileContent = { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": state.optimize, + "runs": state.runs + }, + "outputSelection": { + "*": { + "": ["ast"], + "*": ["evm.gasEstimates"] + } + }, + "evmVersion": state.evmVersion && state.evmVersion.toString() || "berlin", + } } - } - async addDecorators(allErrors: errorMarker[], sources: any) { - const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') - if (!displayErrors) return - const errorsPerFiles: { [fileName: string]: errorMarker[] } = {} - for (const error of allErrors) { - if (!errorsPerFiles[error.file]) { - errorsPerFiles[error.file] = [] - } - errorsPerFiles[error.file].push(error) - } + this.compiler.set('configFileContent', JSON.stringify(configFileContent)) + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (!this.plugin.currentFile) return + const content = await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) + const sources = { [this.plugin.currentFile]: { content } } + this.compiler.compile(sources, this.plugin.currentFile) + } + } catch (e) { + // do nothing + } + } - const errorPriority = { - 'error': 0, - 'warning': 1, - } + async addDecorators(allErrors: errorMarker[], sources: any) { + const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') + if (!displayErrors) return + const errorsPerFiles: { [fileName: string]: errorMarker[] } = {} + for (const error of allErrors) { + if (!errorsPerFiles[error.file]) { + errorsPerFiles[error.file] = [] + } + errorsPerFiles[error.file].push(error) + } - // sort errorPerFiles by error priority - const sortedErrorsPerFiles: { [fileName: string]: errorMarker[] } = {} - for (const fileName in errorsPerFiles) { - const errors = errorsPerFiles[fileName] - errors.sort((a, b) => { - return errorPriority[a.severity] - errorPriority[b.severity] - } - ) - sortedErrorsPerFiles[fileName] = errors - } - const filesWithOutErrors = Object.keys(sources).filter((fileName) => !sortedErrorsPerFiles[fileName]) - // add decorators - const decorators: fileDecoration[] = [] - for (const fileName in sortedErrorsPerFiles) { - const errors = sortedErrorsPerFiles[fileName] - const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) - const decorator: fileDecoration = { - path: fileTarget.file, - isDirectory: false, - fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning, - fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning', - fileStateIconClass: '', - fileStateIcon: '', - text: errors.length.toString(), - owner: 'code-parser', - bubble: true, - comment: errors.map((error) => error.message), - } - decorators.push(decorator) - } - for (const fileName of filesWithOutErrors) { - const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) - const decorator: fileDecoration = { - path: fileTarget.file, - isDirectory: false, - fileStateType: fileDecorationType.None, - fileStateLabelClass: '', - fileStateIconClass: '', - fileStateIcon: '', - text: '', - owner: 'code-parser', - bubble: false - } - decorators.push(decorator) - } - await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) - await this.plugin.call('editor', 'clearErrorMarkers', filesWithOutErrors) + const errorPriority = { + 'error': 0, + 'warning': 1, + } + // sort errorPerFiles by error priority + const sortedErrorsPerFiles: { [fileName: string]: errorMarker[] } = {} + for (const fileName in errorsPerFiles) { + const errors = errorsPerFiles[fileName] + errors.sort((a, b) => { + return errorPriority[a.severity] - errorPriority[b.severity] + } + ) + sortedErrorsPerFiles[fileName] = errors + } + const filesWithOutErrors = Object.keys(sources).filter((fileName) => !sortedErrorsPerFiles[fileName]) + // add decorators + const decorators: fileDecoration[] = [] + for (const fileName in sortedErrorsPerFiles) { + const errors = sortedErrorsPerFiles[fileName] + const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) + const decorator: fileDecoration = { + path: fileTarget.file, + isDirectory: false, + fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning, + fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning', + fileStateIconClass: '', + fileStateIcon: '', + text: errors.length.toString(), + owner: 'code-parser', + bubble: true, + comment: errors.map((error) => error.message), + } + decorators.push(decorator) } + for (const fileName of filesWithOutErrors) { + const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) + const decorator: fileDecoration = { + path: fileTarget.file, + isDirectory: false, + fileStateType: fileDecorationType.None, + fileStateLabelClass: '', + fileStateIconClass: '', + fileStateIcon: '', + text: '', + owner: 'code-parser', + bubble: false + } + decorators.push(decorator) + } + await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) + await this.plugin.call('editor', 'clearErrorMarkers', filesWithOutErrors) - async createErrorMarker(error: any, filePath: string, lineColumn): Promise { - 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 createErrorMarker(error: any, filePath: string, lineColumn): Promise { + 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) { - const decorators: fileDecoration[] = [] - if (!sources) return - for (const fileName of Object.keys(sources)) { - const decorator: fileDecoration = { - path: fileName, - isDirectory: false, - fileStateType: fileDecorationType.None, - fileStateLabelClass: '', - fileStateIconClass: '', - fileStateIcon: '', - text: '', - owner: 'code-parser', - bubble: false - } - decorators.push(decorator) - } + async clearDecorators(sources: any) { + const decorators: fileDecoration[] = [] + if (!sources) return + for (const fileName of Object.keys(sources)) { + const decorator: fileDecoration = { + path: fileName, + isDirectory: false, + fileStateType: fileDecorationType.None, + fileStateLabelClass: '', + fileStateIconClass: '', + fileStateIcon: '', + text: '', + owner: 'code-parser', + bubble: false + } + decorators.push(decorator) + } - 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 - } + async getPositionForImportErrors(importedFileName: string, text: string) { + const re = new RegExp(importedFileName, 'gi') + const result: SearchResultLine[] = findLinesInStringWithMatch( + text, + re + ) + return result + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts index 879d00f06d..274466fe9c 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts @@ -3,76 +3,76 @@ import { lineText } from '@remix-ui/editor' import { lastCompilationResult } from '@remixproject/plugin-api'; export default class CodeParserGasService { - plugin: CodeParser + plugin: CodeParser - constructor(plugin: CodeParser) { - this.plugin = plugin - } + constructor(plugin: CodeParser) { + this.plugin = plugin + } - async getGasEstimates(fileName: string) { - if (!fileName) { - fileName = await this.plugin.currentFile - } - if (this.plugin.nodeIndex.nodesPerFile && this.plugin.nodeIndex.nodesPerFile[fileName] && this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { - const estimates: any = [] - for (const contract in this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { - if (this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes) { - const nodes = this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes - for (const node of Object.values(nodes) as any[]) { - if (node.gasEstimate) { - estimates.push({ - node, - range: await this.plugin.getLineColumnOfNode(node) - }) - } - } - } + async getGasEstimates(fileName: string) { + if (!fileName) { + fileName = await this.plugin.currentFile + } + if (this.plugin.nodeIndex.nodesPerFile && this.plugin.nodeIndex.nodesPerFile[fileName] && this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { + const estimates: any = [] + for (const contract in this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { + if (this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes) { + const nodes = this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes + for (const node of Object.values(nodes) as any[]) { + if (node.gasEstimate) { + estimates.push({ + node, + range: await this.plugin.getLineColumnOfNode(node) + }) } - return estimates + } } - + } + return estimates } + } - async showGasEstimates() { - const showGasConfig = await this.plugin.call('config', 'getAppParameter', 'show-gas') - if(!showGasConfig) { - await this.plugin.call('editor', 'discardLineTexts') - return - } - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository - this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = await this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) - const gasEstimates = await this.getGasEstimates(this.plugin.currentFile) + async showGasEstimates() { + const showGasConfig = await this.plugin.call('config', 'getAppParameter', 'show-gas') + if(!showGasConfig) { + await this.plugin.call('editor', 'discardLineTexts') + return + } + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository + this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = await this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) + + const gasEstimates = await this.getGasEstimates(this.plugin.currentFile) - const friendlyNames = { - 'executionCost': 'Estimated execution cost', - 'codeDepositCost': 'Estimated code deposit cost', - 'creationCost': 'Estimated creation cost', + const friendlyNames = { + 'executionCost': 'Estimated execution cost', + 'codeDepositCost': 'Estimated code deposit cost', + 'creationCost': 'Estimated creation cost', + } + await this.plugin.call('editor', 'discardLineTexts') + if (gasEstimates) { + for (const estimate of gasEstimates) { + const linetext: lineText = { + content: Object.entries(estimate.node.gasEstimate).map(([, value]) => `${value} gas`).join(' '), + position: estimate.range, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + from: 'codeParser', + hoverMessage: [{ + value: `${Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${friendlyNames[key]}: ${value} gas`).join(' ')}`, + }, + ], } - await this.plugin.call('editor', 'discardLineTexts') - if (gasEstimates) { - for (const estimate of gasEstimates) { - const linetext: lineText = { - content: Object.entries(estimate.node.gasEstimate).map(([, value]) => `${value} gas`).join(' '), - position: estimate.range, - hide: false, - className: 'text-muted small', - afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', - from: 'codeParser', - hoverMessage: [{ - value: `${Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${friendlyNames[key]}: ${value} gas`).join(' ')}`, - }, - ], - } - this.plugin.call('editor', 'addLineText', linetext, estimate.range.fileName) + this.plugin.call('editor', 'addLineText', linetext, estimate.range.fileName) - } - } + } } + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts index bdc0837b45..73b162d95b 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts @@ -8,81 +8,81 @@ export type CodeParserImportsData= { } export default class CodeParserImports { - plugin: CodeParser + plugin: CodeParser - data: CodeParserImportsData = {} - constructor(plugin: CodeParser) { - this.plugin = plugin - this.init() - } + data: CodeParserImportsData = {} + constructor(plugin: CodeParser) { + this.plugin = plugin + this.init() + } - async getImports(){ - return this.data - } + 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 - } - }) + 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]))] - } + // 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')) - } + 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 = [] - let files = {} - try { - if (await this.plugin.call('fileManager', 'exists', dir)) { - files = await this.plugin.call('fileManager', 'readdir', dir) - } - } catch (e) {} + getDirectory = async (dir: string) => { + let result = [] + let files = {} + try { + if (await this.plugin.call('fileManager', 'exists', dir)) { + files = await this.plugin.call('fileManager', 'readdir', dir) + } + } catch (e) {} - 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] - } - } + 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 + } } + 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] - }) - } + normalize = filesList => { + const folders = [] + const files = [] + Object.keys(filesList || {}).forEach(key => { + if (filesList[key].isDirectory) { + folders.push({ + filename: key, + data: filesList[key] }) - return [...folders, ...files] - } + } else { + files.push({ + filename: key, + data: filesList[key] + }) + } + }) + return [...folders, ...files] + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts b/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts index ebe8a8ea7d..3e2675c43a 100644 --- a/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts +++ b/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts @@ -596,132 +596,132 @@ type ASTTypeMap = ASTMap export const astNodeTypes = [ - 'SourceUnit', - 'PragmaDirective', - 'ImportDirective', - 'ContractDefinition', - 'InheritanceSpecifier', - 'StateVariableDeclaration', - 'UsingForDeclaration', - 'StructDefinition', - 'ModifierDefinition', - 'ModifierInvocation', - 'FunctionDefinition', - 'EventDefinition', - 'CustomErrorDefinition', - 'RevertStatement', - 'EnumValue', - 'EnumDefinition', - 'VariableDeclaration', - 'UserDefinedTypeName', - 'Mapping', - 'ArrayTypeName', - 'FunctionTypeName', - 'Block', - 'ExpressionStatement', - 'IfStatement', - 'WhileStatement', - 'ForStatement', - 'InlineAssemblyStatement', - 'DoWhileStatement', - 'ContinueStatement', - 'Break', - 'Continue', - 'BreakStatement', - 'ReturnStatement', - 'EmitStatement', - 'ThrowStatement', - 'VariableDeclarationStatement', - 'ElementaryTypeName', - 'FunctionCall', - 'AssemblyBlock', - 'AssemblyCall', - 'AssemblyLocalDefinition', - 'AssemblyAssignment', - 'AssemblyStackAssignment', - 'LabelDefinition', - 'AssemblySwitch', - 'AssemblyCase', - 'AssemblyFunctionDefinition', - 'AssemblyFunctionReturns', - 'AssemblyFor', - 'AssemblyIf', - 'SubAssembly', - 'TupleExpression', - 'NameValueExpression', - 'BooleanLiteral', - 'NumberLiteral', - 'Identifier', - 'BinaryOperation', - 'UnaryOperation', - 'NewExpression', - 'Conditional', - 'StringLiteral', - 'HexLiteral', - 'HexNumber', - 'DecimalNumber', - 'MemberAccess', - 'IndexAccess', - 'IndexRangeAccess', - 'NameValueList', - 'UncheckedStatement', - 'TryStatement', - 'CatchClause', - 'FileLevelConstant', - 'AssemblyMemberAccess', - 'TypeDefinition', - 'InvalidNode' + 'SourceUnit', + 'PragmaDirective', + 'ImportDirective', + 'ContractDefinition', + 'InheritanceSpecifier', + 'StateVariableDeclaration', + 'UsingForDeclaration', + 'StructDefinition', + 'ModifierDefinition', + 'ModifierInvocation', + 'FunctionDefinition', + 'EventDefinition', + 'CustomErrorDefinition', + 'RevertStatement', + 'EnumValue', + 'EnumDefinition', + 'VariableDeclaration', + 'UserDefinedTypeName', + 'Mapping', + 'ArrayTypeName', + 'FunctionTypeName', + 'Block', + 'ExpressionStatement', + 'IfStatement', + 'WhileStatement', + 'ForStatement', + 'InlineAssemblyStatement', + 'DoWhileStatement', + 'ContinueStatement', + 'Break', + 'Continue', + 'BreakStatement', + 'ReturnStatement', + 'EmitStatement', + 'ThrowStatement', + 'VariableDeclarationStatement', + 'ElementaryTypeName', + 'FunctionCall', + 'AssemblyBlock', + 'AssemblyCall', + 'AssemblyLocalDefinition', + 'AssemblyAssignment', + 'AssemblyStackAssignment', + 'LabelDefinition', + 'AssemblySwitch', + 'AssemblyCase', + 'AssemblyFunctionDefinition', + 'AssemblyFunctionReturns', + 'AssemblyFor', + 'AssemblyIf', + 'SubAssembly', + 'TupleExpression', + 'NameValueExpression', + 'BooleanLiteral', + 'NumberLiteral', + 'Identifier', + 'BinaryOperation', + 'UnaryOperation', + 'NewExpression', + 'Conditional', + 'StringLiteral', + 'HexLiteral', + 'HexNumber', + 'DecimalNumber', + 'MemberAccess', + 'IndexAccess', + 'IndexRangeAccess', + 'NameValueList', + 'UncheckedStatement', + 'TryStatement', + 'CatchClause', + 'FileLevelConstant', + 'AssemblyMemberAccess', + 'TypeDefinition', + 'InvalidNode' ] as const export const binaryOpValues = [ - '+', - '-', - '*', - '/', - '**', - '%', - '<<', - '>>', - '&&', - '||', - ',,', - '&', - ',', - '^', - '<', - '>', - '<=', - '>=', - '==', - '!=', - '=', - ',=', - '^=', - '&=', - '<<=', - '>>=', - '+=', - '-=', - '*=', - '/=', - '%=', - '|', - '|=', + '+', + '-', + '*', + '/', + '**', + '%', + '<<', + '>>', + '&&', + '||', + ',,', + '&', + ',', + '^', + '<', + '>', + '<=', + '>=', + '==', + '!=', + '=', + ',=', + '^=', + '&=', + '<<=', + '>>=', + '+=', + '-=', + '*=', + '/=', + '%=', + '|', + '|=', ] as const export type BinOp = typeof binaryOpValues[number] export const unaryOpValues = [ - '-', - '+', - '++', - '--', - '~', - 'after', - 'delete', - '!', + '-', + '+', + '++', + '--', + '~', + 'after', + 'delete', + '!', ] as const export type UnaryOp = typeof unaryOpValues[number] diff --git a/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx b/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx index f4dbf5d095..c89b087eed 100644 --- a/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx +++ b/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx @@ -49,12 +49,12 @@ export class PermissionHandlerPlugin extends Plugin { switchMode (from: Profile, to: Profile, method: string, set: boolean, sensitiveCall: boolean) { if (sensitiveCall) { set - ? this.sessionPermissions[to.name][method][from.name] = {} - : delete this.sessionPermissions[to.name][method][from.name] + ? this.sessionPermissions[to.name][method][from.name] = {} + : delete this.sessionPermissions[to.name][method][from.name] } else { set - ? this.permissions[to.name][method][from.name] = {} - : delete this.permissions[to.name][method][from.name] + ? this.permissions[to.name][method][from.name] = {} + : delete this.permissions[to.name][method][from.name] } } diff --git a/apps/remix-ide/src/app/plugins/solidity-script.tsx b/apps/remix-ide/src/app/plugins/solidity-script.tsx index bf0be9ad53..88c3b1e962 100644 --- a/apps/remix-ide/src/app/plugins/solidity-script.tsx +++ b/apps/remix-ide/src/app/plugins/solidity-script.tsx @@ -84,20 +84,20 @@ export class SolidityScript extends Plugin { if (hhlogs && hhlogs.length) { const finalLogs =
console.log:
- { - hhlogs.map((log) => { - let formattedLog - // Hardhat implements the same formatting options that can be found in Node.js' console.log, - // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args - // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' - // We check first arg to determine if 'util.format' is needed - if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { - formattedLog = format(log[0], ...log.slice(1)) - } else { - formattedLog = log.join(' ') - } - return
{formattedLog}
- })} + { + hhlogs.map((log) => { + let formattedLog + // Hardhat implements the same formatting options that can be found in Node.js' console.log, + // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args + // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' + // We check first arg to determine if 'util.format' is needed + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') + } + return
{formattedLog}
+ })}
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) this.call('terminal', 'logHtml', finalLogs) diff --git a/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx b/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx index 5abacd0409..66e4ecbc87 100644 --- a/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx +++ b/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx @@ -16,39 +16,39 @@ const parser = (window as any).SolidityParser const _paq = window._paq = window._paq || [] const profile = { - name: 'solidityumlgen', - displayName: 'Solidity UML Generator', - description: 'Generates UML diagram in svg format from last compiled contract', - location: 'mainPanel', - methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'], - events: [], + name: 'solidityumlgen', + displayName: 'Solidity UML Generator', + description: 'Generates UML diagram in svg format from last compiled contract', + location: 'mainPanel', + methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'], + events: [], } const themeCollection = [ { themeName: 'HackerOwl', backgroundColor: '#011628', textColor: '#babbcc', - shapeColor: '#8694a1',fillColor: '#011C32'}, + shapeColor: '#8694a1',fillColor: '#011C32'}, { themeName: 'Cerulean', backgroundColor: '#ffffff', textColor: '#343a40', - shapeColor: '#343a40',fillColor: '#f8f9fa'}, + shapeColor: '#343a40',fillColor: '#f8f9fa'}, { themeName: 'Cyborg', backgroundColor: '#060606', textColor: '#adafae', - shapeColor: '#adafae', fillColor: '#222222'}, + shapeColor: '#adafae', fillColor: '#222222'}, { themeName: 'Dark', backgroundColor: '#222336', textColor: '#babbcc', - shapeColor: '#babbcc',fillColor: '#2a2c3f'}, + shapeColor: '#babbcc',fillColor: '#2a2c3f'}, { themeName: 'Flatly', backgroundColor: '#ffffff', textColor: '#343a40', - shapeColor: '#7b8a8b',fillColor: '#ffffff'}, + shapeColor: '#7b8a8b',fillColor: '#ffffff'}, { themeName: 'Black', backgroundColor: '#1a1a1a', textColor: '#babbcc', - shapeColor: '#b5b4bc',fillColor: '#1f2020'}, + shapeColor: '#b5b4bc',fillColor: '#1f2020'}, { themeName: 'Light', backgroundColor: '#eef1f6', textColor: '#3b445e', - shapeColor: '#343a40',fillColor: '#ffffff'}, + shapeColor: '#343a40',fillColor: '#ffffff'}, { themeName: 'Midcentury', backgroundColor: '#DBE2E0', textColor: '#11556c', - shapeColor: '#343a40',fillColor: '#eeede9'}, + shapeColor: '#343a40',fillColor: '#eeede9'}, { themeName: 'Spacelab', backgroundColor: '#ffffff', textColor: '#343a40', - shapeColor: '#333333', fillColor: '#eeeeee'}, + shapeColor: '#333333', fillColor: '#eeeeee'}, { themeName: 'Candy', backgroundColor: '#d5efff', textColor: '#11556c', - shapeColor: '#343a40',fillColor: '#fbe7f8' }, + shapeColor: '#343a40',fillColor: '#fbe7f8' }, { themeName: 'Violet', backgroundColor: '#f1eef6', textColor: '#3b445e', - shapeColor: '#343a40',fillColor: '#f8fafe' }, + shapeColor: '#343a40',fillColor: '#f8fafe' }, { themeName: 'Unicorn', backgroundColor: '#f1eef6', textColor: '#343a40', - shapeColor: '#343a40',fillColor: '#f8fafe' }, + shapeColor: '#343a40',fillColor: '#f8fafe' }, ] /** @@ -121,7 +121,7 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen { getThemeCssVariables(cssVars: string) { return window.getComputedStyle(document.documentElement) - .getPropertyValue(cssVars) + .getPropertyValue(cssVars) } private handleThemeChange() { @@ -246,10 +246,10 @@ interface Sol2umlClassOptions extends ClassOptions { import { dirname } from 'path' import { convertClass2Dot } from 'sol2uml/lib/converterClass2Dot' import { - Association, - ClassStereotype, - ReferenceType, - UmlClass, + Association, + ClassStereotype, + ReferenceType, + UmlClass, } from 'sol2uml/lib/umlClass' import { findAssociatedClass } from 'sol2uml/lib/associations' @@ -264,11 +264,11 @@ import { findAssociatedClass } from 'sol2uml/lib/associations' * @return dotString Graphviz's DOT format for defining nodes, edges and clusters. */ export function convertUmlClasses2Dot( - umlClasses: UmlClass[], - clusterFolders: boolean = false, - classOptions: Sol2umlClassOptions = {} + umlClasses: UmlClass[], + clusterFolders: boolean = false, + classOptions: Sol2umlClassOptions = {} ): string { - let dotString: string = ` + let dotString: string = ` digraph UmlClassDiagram { rankdir=BT arrowhead=open @@ -276,124 +276,124 @@ bgcolor="${classOptions.backColor}" edge [color="${classOptions.shapeColor}"] node [shape=record, style=filled, color="${classOptions.shapeColor}", fillcolor="${classOptions.fillColor}", fontcolor="${classOptions.textColor}"]` - // Sort UML Classes by folder of source file - const umlClassesSortedByCodePath = sortUmlClassesByCodePath(umlClasses) + // Sort UML Classes by folder of source file + const umlClassesSortedByCodePath = sortUmlClassesByCodePath(umlClasses) - let currentCodeFolder = '' - for (const umlClass of umlClassesSortedByCodePath) { - const codeFolder = dirname(umlClass.relativePath) - if (currentCodeFolder !== codeFolder) { - // Need to close off the last subgraph if not the first - if (currentCodeFolder != '') { - dotString += '\n}' - } + let currentCodeFolder = '' + for (const umlClass of umlClassesSortedByCodePath) { + const codeFolder = dirname(umlClass.relativePath) + if (currentCodeFolder !== codeFolder) { + // Need to close off the last subgraph if not the first + if (currentCodeFolder != '') { + dotString += '\n}' + } - dotString += ` + dotString += ` subgraph ${getSubGraphName(clusterFolders)} { label="${codeFolder}"` - currentCodeFolder = codeFolder - } - dotString += convertClass2Dot(umlClass, classOptions) + currentCodeFolder = codeFolder } + dotString += convertClass2Dot(umlClass, classOptions) + } - // Need to close off the last subgraph if not the first - if (currentCodeFolder != '') { - dotString += '\n}' - } + // Need to close off the last subgraph if not the first + if (currentCodeFolder != '') { + dotString += '\n}' + } - dotString += addAssociationsToDot(umlClasses, classOptions) + dotString += addAssociationsToDot(umlClasses, classOptions) - // Need to close off the last the digraph - dotString += '\n}' + // Need to close off the last the digraph + dotString += '\n}' - // debug(dotString) + // debug(dotString) - return dotString + return dotString } let subGraphCount = 0 function getSubGraphName(clusterFolders: boolean = false) { - if (clusterFolders) { - return ` cluster_${subGraphCount++}` - } - return ` graph_${subGraphCount++}` + if (clusterFolders) { + return ` cluster_${subGraphCount++}` + } + return ` graph_${subGraphCount++}` } function sortUmlClassesByCodePath(umlClasses: UmlClass[]): UmlClass[] { - return umlClasses.sort((a, b) => { - if (a.relativePath < b.relativePath) { - return -1 - } - if (a.relativePath > b.relativePath) { - return 1 - } - return 0 - }) + return umlClasses.sort((a, b) => { + if (a.relativePath < b.relativePath) { + return -1 + } + if (a.relativePath > b.relativePath) { + return 1 + } + return 0 + }) } export function addAssociationsToDot( - umlClasses: UmlClass[], - classOptions: ClassOptions = {} + umlClasses: UmlClass[], + classOptions: ClassOptions = {} ): string { - let dotString: string = '' - - // for each class - for (const sourceUmlClass of umlClasses) { - if (!classOptions.hideEnums) { - // for each enum in the class - sourceUmlClass.enums.forEach((enumId) => { - // Has the enum been filtered out? eg depth limited - const targetUmlClass = umlClasses.find((c) => c.id === enumId) - if (targetUmlClass) { - // Draw aggregated link from contract to contract level Enum - dotString += `\n${enumId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` - } - }) + let dotString: string = '' + + // for each class + for (const sourceUmlClass of umlClasses) { + if (!classOptions.hideEnums) { + // for each enum in the class + sourceUmlClass.enums.forEach((enumId) => { + // Has the enum been filtered out? eg depth limited + const targetUmlClass = umlClasses.find((c) => c.id === enumId) + if (targetUmlClass) { + // Draw aggregated link from contract to contract level Enum + dotString += `\n${enumId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` } - if (!classOptions.hideStructs) { - // for each struct in the class - sourceUmlClass.structs.forEach((structId) => { - // Has the struct been filtered out? eg depth limited - const targetUmlClass = umlClasses.find((c) => c.id === structId) - if (targetUmlClass) { - // Draw aggregated link from contract to contract level Struct - dotString += `\n${structId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` - } - }) + }) + } + if (!classOptions.hideStructs) { + // for each struct in the class + sourceUmlClass.structs.forEach((structId) => { + // Has the struct been filtered out? eg depth limited + const targetUmlClass = umlClasses.find((c) => c.id === structId) + if (targetUmlClass) { + // Draw aggregated link from contract to contract level Struct + dotString += `\n${structId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` } + }) + } - // for each association in that class - for (const association of Object.values(sourceUmlClass.associations)) { - const targetUmlClass = findAssociatedClass( - association, - sourceUmlClass, - umlClasses - ) - if (targetUmlClass) { - dotString += addAssociationToDot( - sourceUmlClass, - targetUmlClass, - association, - classOptions - ) - } - } + // for each association in that class + for (const association of Object.values(sourceUmlClass.associations)) { + const targetUmlClass = findAssociatedClass( + association, + sourceUmlClass, + umlClasses + ) + if (targetUmlClass) { + dotString += addAssociationToDot( + sourceUmlClass, + targetUmlClass, + association, + classOptions + ) + } } + } - return dotString + return dotString } function addAssociationToDot( - sourceUmlClass: UmlClass, - targetUmlClass: UmlClass, - association: Association, - classOptions: ClassOptions = {} + sourceUmlClass: UmlClass, + targetUmlClass: UmlClass, + association: Association, + classOptions: ClassOptions = {} ): string { - // do not include library or interface associations if hidden - // Or associations to Structs, Enums or Constants if they are hidden - if ( - (classOptions.hideLibraries && + // do not include library or interface associations if hidden + // Or associations to Structs, Enums or Constants if they are hidden + if ( + (classOptions.hideLibraries && (sourceUmlClass.stereotype === ClassStereotype.Library || targetUmlClass.stereotype === ClassStereotype.Library)) || (classOptions.hideInterfaces && @@ -408,28 +408,28 @@ function addAssociationToDot( targetUmlClass.stereotype === ClassStereotype.Enum) || (classOptions.hideConstants && targetUmlClass.stereotype === ClassStereotype.Constant) - ) { - return '' - } + ) { + return '' + } - let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [` + let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [` - if ( - association.referenceType == ReferenceType.Memory || + if ( + association.referenceType == ReferenceType.Memory || (association.realization && targetUmlClass.stereotype === ClassStereotype.Interface) - ) { - dotString += 'style=dashed, ' - } + ) { + dotString += 'style=dashed, ' + } - if (association.realization) { - dotString += 'arrowhead=empty, arrowsize=3, ' - if (!targetUmlClass.stereotype) { - dotString += 'weight=4, ' - } else { - dotString += 'weight=3, ' - } + if (association.realization) { + dotString += 'arrowhead=empty, arrowsize=3, ' + if (!targetUmlClass.stereotype) { + dotString += 'weight=4, ' + } else { + dotString += 'weight=3, ' } + } - return dotString + ']' + return dotString + ']' } diff --git a/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx index c923560eb1..d4f9c81297 100644 --- a/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx @@ -29,23 +29,23 @@ export class CustomForkVMProvider extends BasicVMProvider { const body = () => { return
Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state. -
- - +
+ + +
+
+ + +
+
+ + +
-
- - -
-
- - -
-
} const result = await ((): Promise => { return new Promise((resolve, reject) => { diff --git a/apps/remix-ide/src/app/state/registry.ts b/apps/remix-ide/src/app/state/registry.ts index dafe13654b..dc3655293f 100644 --- a/apps/remix-ide/src/app/state/registry.ts +++ b/apps/remix-ide/src/app/state/registry.ts @@ -4,35 +4,35 @@ type registryEntry = { } export default class Registry { - private static instance: Registry; - private state: any + private static instance: Registry; + private state: any - private constructor () { - this.state = {} - } - - public static getInstance (): Registry { - if (!Registry.instance) { - Registry.instance = new Registry() - } + private constructor () { + this.state = {} + } - return Registry.instance + public static getInstance (): Registry { + if (!Registry.instance) { + Registry.instance = new Registry() } - public put (entry: registryEntry) { - if (this.state[entry.name]) return this.state[entry.name] - const server = { + return Registry.instance + } + + public put (entry: registryEntry) { + if (this.state[entry.name]) return this.state[entry.name] + const server = { // uid: serveruid, - api: entry.api - } - this.state[entry.name] = { server } - return server + api: entry.api } + this.state[entry.name] = { server } + return server + } - public get (name: string) { - const state = this.state[name] - if (!state) return - const server = state.server - return server - } + public get (name: string) { + const state = this.state[name] + if (!state) return + const server = state.server + return server + } } diff --git a/apps/remix-ide/src/app/tabs/analysis-tab.js b/apps/remix-ide/src/app/tabs/analysis-tab.js index 53a20a5042..9a57c16516 100644 --- a/apps/remix-ide/src/app/tabs/analysis-tab.js +++ b/apps/remix-ide/src/app/tabs/analysis-tab.js @@ -81,10 +81,10 @@ class AnalysisTab extends ViewPlugin { updateComponent(state) { return + registry={state.registry} + analysisModule={state.analysisModule} + event={state.event} + /> } renderComponent () { diff --git a/apps/remix-ide/src/app/tabs/search.tsx b/apps/remix-ide/src/app/tabs/search.tsx index 6705428a5f..e742281d3e 100644 --- a/apps/remix-ide/src/app/tabs/search.tsx +++ b/apps/remix-ide/src/app/tabs/search.tsx @@ -3,31 +3,31 @@ import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line import { SearchTab } from '@remix-ui/search' const profile = { - name: 'search', - displayName: 'Search in files', - methods: [''], - events: [], - icon: 'assets/img/search_icon.webp', - description: 'Find and replace in file explorer', - kind: '', - location: 'sidePanel', - documentation: '', - version: packageJson.version, - maintainedBy: 'Remix' - } + name: 'search', + displayName: 'Search in files', + methods: [''], + events: [], + icon: 'assets/img/search_icon.webp', + description: 'Find and replace in file explorer', + kind: '', + location: 'sidePanel', + documentation: '', + version: packageJson.version, + maintainedBy: 'Remix' +} export class SearchPlugin extends ViewPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - render() { - return ( -
- -
- ); - } + render() { + return ( +
+ +
+ ); + } } diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js index f6e77705ba..e1e066c4b5 100644 --- a/apps/remix-ide/src/app/tabs/theme-module.js +++ b/apps/remix-ide/src/app/tabs/theme-module.js @@ -39,7 +39,7 @@ export class ThemeModule extends Plugin { this.themes = {} themes.map((theme) => { this.themes[theme.name.toLocaleLowerCase()] = { - ...theme, + ...theme, url: window.location.origin + ( window.location.pathname.startsWith('/address/') || window.location.pathname.endsWith('.sol') ? '/' : window.location.pathname ) + theme.url } }) diff --git a/apps/remix-ide/src/app/tabs/web3-provider.js b/apps/remix-ide/src/app/tabs/web3-provider.js index f375e52554..0158ff11f9 100644 --- a/apps/remix-ide/src/app/tabs/web3-provider.js +++ b/apps/remix-ide/src/app/tabs/web3-provider.js @@ -60,8 +60,8 @@ export class Web3ProviderModule extends Plugin { reject(new Error('User denied permission')) } }).catch((e) => { - reject(e) - }) + reject(e) + }) }) } diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index d90246464b..49983f5e39 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -136,8 +136,8 @@ export class RunTab extends ViewPlugin { if (window && window.ethereum && !(window.ethereum.isTrustWallet || window.ethereum.selectedProvider?.isTrustWallet)) { const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ? window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' : - window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' : - window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}` + window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' : + window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}` await addProvider('injected', displayNameInjected, true, false) } else if (window && !window.ethereum) { // we still add "injected" if there's no provider (just so it's visible to the user). diff --git a/apps/remix-ide/src/app/ui/landing-page/landing-page.js b/apps/remix-ide/src/app/ui/landing-page/landing-page.js index b07e2c9432..077d6507f3 100644 --- a/apps/remix-ide/src/app/ui/landing-page/landing-page.js +++ b/apps/remix-ide/src/app/ui/landing-page/landing-page.js @@ -31,7 +31,7 @@ export class LandingPage extends ViewPlugin { render () { return
- -
- } + + + } } diff --git a/apps/remix-ide/src/blockchain/blockchain.tsx b/apps/remix-ide/src/blockchain/blockchain.tsx index e6b719f8d1..61aeeb74df 100644 --- a/apps/remix-ide/src/blockchain/blockchain.tsx +++ b/apps/remix-ide/src/blockchain/blockchain.tsx @@ -312,52 +312,52 @@ export class Blockchain extends Plugin { } async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) { - const { contractName, implementationAddress } = contractObject - const networkName = networkInfo.name === 'custom' ? networkInfo.name + '-' + networkInfo.id : networkInfo.name - const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) - // TODO: make deploys folder read only. - if (hasPreviousDeploys) { - const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) - const parsedDeployments = JSON.parse(deployments) - const proxyDeployment = parsedDeployments.deployments[proxyAddress] - - if (proxyDeployment) { - const oldImplementationAddress = proxyDeployment.implementationAddress - const hasPreviousBuild = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) - - if (hasPreviousBuild) await this.call('fileManager', 'remove', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) - } - parsedDeployments.deployments[proxyAddress] = { - date: new Date().toISOString(), - contractName: contractName, - fork: networkInfo.currentFork, - implementationAddress: implementationAddress, - solcOutput: contractObject.compiler.data, - solcInput: contractObject.compiler.source - } - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ - solcInput: contractObject.compiler.source, - solcOutput: contractObject.compiler.data - }, null, 2)) - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2)) - } else { - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ - solcInput: contractObject.compiler.source, - solcOutput: contractObject.compiler.data - }, null, 2)) - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify({ - id: networkInfo.id, - network: networkInfo.name, - deployments: { - [proxyAddress]: { - date: new Date().toISOString(), - contractName: contractName, - fork: networkInfo.currentFork, - implementationAddress: implementationAddress - } - } - }, null, 2)) + const { contractName, implementationAddress } = contractObject + const networkName = networkInfo.name === 'custom' ? networkInfo.name + '-' + networkInfo.id : networkInfo.name + const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) + // TODO: make deploys folder read only. + if (hasPreviousDeploys) { + const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) + const parsedDeployments = JSON.parse(deployments) + const proxyDeployment = parsedDeployments.deployments[proxyAddress] + + if (proxyDeployment) { + const oldImplementationAddress = proxyDeployment.implementationAddress + const hasPreviousBuild = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) + + if (hasPreviousBuild) await this.call('fileManager', 'remove', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) + } + parsedDeployments.deployments[proxyAddress] = { + date: new Date().toISOString(), + contractName: contractName, + fork: networkInfo.currentFork, + implementationAddress: implementationAddress, + solcOutput: contractObject.compiler.data, + solcInput: contractObject.compiler.source } + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ + solcInput: contractObject.compiler.source, + solcOutput: contractObject.compiler.data + }, null, 2)) + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2)) + } else { + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ + solcInput: contractObject.compiler.source, + solcOutput: contractObject.compiler.data + }, null, 2)) + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify({ + id: networkInfo.id, + network: networkInfo.name, + deployments: { + [proxyAddress]: { + date: new Date().toISOString(), + contractName: contractName, + fork: networkInfo.currentFork, + implementationAddress: implementationAddress + } + } + }, null, 2)) + } } async getEncodedFunctionHex (args, funABI) { @@ -601,9 +601,9 @@ export class Blockchain extends Plugin { if (viewEtherScanLink) { this.call('terminal', 'logHtml', - ( + ( view on etherscan - )) + )) } }) }) @@ -808,20 +808,20 @@ export class Blockchain extends Plugin { if (hhlogs && hhlogs.length) { const finalLogs =
console.log:
- { - hhlogs.map((log) => { - let formattedLog - // Hardhat implements the same formatting options that can be found in Node.js' console.log, - // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args - // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' - // We check first arg to determine if 'util.format' is needed - if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { - formattedLog = format(log[0], ...log.slice(1)) - } else { - formattedLog = log.join(' ') - } - return
{formattedLog}
- })} + { + hhlogs.map((log) => { + let formattedLog + // Hardhat implements the same formatting options that can be found in Node.js' console.log, + // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args + // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' + // We check first arg to determine if 'util.format' is needed + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') + } + return
{formattedLog}
+ })}
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) this.call('terminal', 'logHtml', finalLogs) diff --git a/apps/remix-ide/src/blockchain/helper.ts b/apps/remix-ide/src/blockchain/helper.ts index 5fde384369..7df7546d20 100644 --- a/apps/remix-ide/src/blockchain/helper.ts +++ b/apps/remix-ide/src/blockchain/helper.ts @@ -1,14 +1,14 @@ const transactionDetailsLinks = { - Main: 'https://www.etherscan.io/tx/', - Rinkeby: 'https://rinkeby.etherscan.io/tx/', - Ropsten: 'https://ropsten.etherscan.io/tx/', - Kovan: 'https://kovan.etherscan.io/tx/', - Goerli: 'https://goerli.etherscan.io/tx/', - Sepolia: 'https://sepolia.etherscan.io/tx/' - } + Main: 'https://www.etherscan.io/tx/', + Rinkeby: 'https://rinkeby.etherscan.io/tx/', + Ropsten: 'https://ropsten.etherscan.io/tx/', + Kovan: 'https://kovan.etherscan.io/tx/', + Goerli: 'https://goerli.etherscan.io/tx/', + Sepolia: 'https://sepolia.etherscan.io/tx/' +} - export function etherScanLink (network: string, hash: string): string { - if (transactionDetailsLinks[network]) { - return transactionDetailsLinks[network] + hash - } - } \ No newline at end of file +export function etherScanLink (network: string, hash: string): string { + if (transactionDetailsLinks[network]) { + return transactionDetailsLinks[network] + hash + } +} \ No newline at end of file diff --git a/apps/remix-ide/src/blockchain/providers/injected.ts b/apps/remix-ide/src/blockchain/providers/injected.ts index 557ea32896..bc6ab49e7f 100644 --- a/apps/remix-ide/src/blockchain/providers/injected.ts +++ b/apps/remix-ide/src/blockchain/providers/injected.ts @@ -20,7 +20,7 @@ export class InjectedProvider { } async resetEnvironment () { - /* Do nothing. */ + /* Do nothing. */ } async getBalanceInEther (address) { diff --git a/apps/remix-ide/src/blockchain/providers/node.ts b/apps/remix-ide/src/blockchain/providers/node.ts index fbe0159c19..65116b4923 100644 --- a/apps/remix-ide/src/blockchain/providers/node.ts +++ b/apps/remix-ide/src/blockchain/providers/node.ts @@ -30,7 +30,7 @@ export class NodeProvider { } async resetEnvironment () { - /* Do nothing. */ + /* Do nothing. */ } async getBalanceInEther (address) { diff --git a/apps/remix-ide/src/blockchain/providers/vm.ts b/apps/remix-ide/src/blockchain/providers/vm.ts index 958eb9f6d4..4234e42616 100644 --- a/apps/remix-ide/src/blockchain/providers/vm.ts +++ b/apps/remix-ide/src/blockchain/providers/vm.ts @@ -59,12 +59,12 @@ export class VMProvider { reject(new Error(msg.data.error)) } } else if (msg.data.cmd === 'newAccountResult') { - if (this.newAccountCallback[msg.data.stamp]) { - this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result) - delete this.newAccountCallback[msg.data.stamp] + if (this.newAccountCallback[msg.data.stamp]) { + this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result) + delete this.newAccountCallback[msg.data.stamp] + } } - } - }) + }) this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']}) }) } diff --git a/apps/remix-ide/src/blockchain/providers/worker-vm.ts b/apps/remix-ide/src/blockchain/providers/worker-vm.ts index b261cda864..64a8d0255b 100644 --- a/apps/remix-ide/src/blockchain/providers/worker-vm.ts +++ b/apps/remix-ide/src/blockchain/providers/worker-vm.ts @@ -4,74 +4,74 @@ let provider: Provider = null self.onmessage = (e: MessageEvent) => { const data = e.data switch (data.cmd) { - case 'init': - { - provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber }) - provider.init().then(() => { - self.postMessage({ - cmd: 'initiateResult', - stamp: data.stamp - }) - }).catch((error) => { + case 'init': + { + provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber }) + provider.init().then(() => { + self.postMessage({ + cmd: 'initiateResult', + stamp: data.stamp + }) + }).catch((error) => { + self.postMessage({ + cmd: 'initiateResult', + error, + stamp: data.stamp + }) + }) + break + } + case 'sendAsync': + { + if (provider) { + provider.sendAsync(data.query, (error, result) => { self.postMessage({ - cmd: 'initiateResult', + cmd: 'sendAsyncResult', error, + result, stamp: data.stamp }) }) - break + } else { + self.postMessage({ + cmd: 'sendAsyncResult', + error: 'Provider not instantiated', + result: null, + stamp: data.stamp + }) } - case 'sendAsync': - { - if (provider) { - provider.sendAsync(data.query, (error, result) => { - self.postMessage({ - cmd: 'sendAsyncResult', - error, - result, - stamp: data.stamp - }) - }) - } else { - self.postMessage({ - cmd: 'sendAsyncResult', - error: 'Provider not instantiated', - result: null, - stamp: data.stamp - }) - } - break + break + } + case 'addAccount': + { + if (provider) { + provider.Accounts._addAccount(data.privateKey, data.balance) } - case 'addAccount': - { - if (provider) { - provider.Accounts._addAccount(data.privateKey, data.balance) - } - break + break + } + case 'newAccount': + { + if (provider) { + provider.Accounts.newAccount((error, address: string) => { + if (error) { + self.postMessage({ + cmd: 'newAccountResult', + error, + stamp: data.stamp + }) + } else { + self.postMessage({ + cmd: 'newAccountResult', + result: address, + stamp: data.stamp + }) + } + }) } - case 'newAccount': - { - if (provider) { - provider.Accounts.newAccount((error, address: string) => { - if (error) { - self.postMessage({ - cmd: 'newAccountResult', - error, - stamp: data.stamp - }) - } else { - self.postMessage({ - cmd: 'newAccountResult', - result: address, - stamp: data.stamp - }) - } - }) - } - break - } + break + } } }