diff --git a/src/app.js b/src/app.js index 1811ec333a1..b530d31c156 100644 --- a/src/app.js +++ b/src/app.js @@ -257,7 +257,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) // ----------------- fileManager servive ---------------------------- - const fileManager = new FileManager(editor) + const fileManager = new FileManager(editor, appManager) registry.put({api: fileManager, name: 'filemanager'}) const blockchain = new Blockchain(registry.get('config').api) @@ -396,7 +396,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) await appManager.activatePlugin(['mainPanel', 'menuicons']) - await appManager.activatePlugin(['home', 'sidePanel', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'scriptRunner', 'terminal', 'fetchAndCompile']) + await appManager.activatePlugin(['sidePanel']) // activating host plugin separately + await appManager.activatePlugin(['home', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'scriptRunner', 'terminal', 'fetchAndCompile']) const queryParams = new QueryParams() const params = queryParams.get() diff --git a/src/app/editor/example-contracts.js b/src/app/editor/example-contracts.js index 34c6ea9a647..a01dead068e 100644 --- a/src/app/editor/example-contracts.js +++ b/src/app/editor/example-contracts.js @@ -227,7 +227,7 @@ contract Ballot { var ballotTest = `pragma solidity >=0.4.22 <0.7.0; import "remix_tests.sol"; // this import is automatically injected by Remix. -import "./3_Ballot.sol"; +import "../3_Ballot.sol"; contract BallotTest { @@ -255,6 +255,6 @@ module.exports = { storage: { name: '1_Storage.sol', content: storage }, owner: { name: '2_Owner.sol', content: owner }, ballot: { name: '3_Ballot.sol', content: ballot }, - ballot_test: { name: '4_Ballot_test.sol', content: ballotTest }, + ballot_test: { name: 'tests/4_Ballot_test.sol', content: ballotTest }, basic: { name: 'basic.sol', content: basic } } diff --git a/src/app/files/file-explorer.js b/src/app/files/file-explorer.js index 19b678182f2..2d12bd8dce2 100644 --- a/src/app/files/file-explorer.js +++ b/src/app/files/file-explorer.js @@ -104,8 +104,8 @@ function fileExplorer (localRegistry, files, menuItems) { self.ensureRoot(() => { const folderpath = filepath.split('/').slice(0, -1).join('/') const currentTree = self.treeView.nodeAt(folderpath) - - if (currentTree && self.treeView.isExpanded(folderpath)) { + if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath) + if (currentTree) { self.files.resolveDirectory(folderpath, (error, fileTree) => { if (error) console.error(error) if (!fileTree) return @@ -136,6 +136,7 @@ function fileExplorer (localRegistry, files, menuItems) { if (!fileTree) return fileTree = normalize(folderpath, fileTree) self.treeView.updateNodeFromJSON(folderpath, fileTree, true) + if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath) }) }) } diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 6699179c067..5ab6ed33203 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -38,7 +38,7 @@ const createError = (err) => { } class FileManager extends Plugin { - constructor (editor) { + constructor (editor, appManager) { super(profile) this.openedFiles = {} // list all opened files this.events = new EventEmitter() @@ -46,6 +46,7 @@ class FileManager extends Plugin { this._components = {} this._components.compilerImport = new CompilerImport() this._components.registry = globalRegistry + this.appManager = appManager this.init() } @@ -493,6 +494,32 @@ class FileManager extends Plugin { return this._deps.filesProviders['browser'] } + // returns the list of directories inside path + dirList (path) { + const dirPaths = [] + const collectList = (path) => { + return new Promise((resolve, reject) => { + this.readdir(path).then((ls) => { + const promises = Object.keys(ls).map((item, index) => { + const root = (path.indexOf('/') === -1) ? path : path.substr(0, path.indexOf('/')) + const curPath = `${root}/${item}` // adding 'browser' or 'localhost' + if (ls[item].isDirectory && !dirPaths.includes(curPath)) { + dirPaths.push(curPath) + resolve(dirPaths) + } + return new Promise((resolve, reject) => { resolve() }) + }) + Promise.all(promises).then(() => { resolve(dirPaths) }) + }) + }) + } + return collectList(path) + } + + isRemixDActive () { + return this.appManager.isActive('remixd') + } + saveCurrentFile () { var currentFile = this._deps.config.get('currentFile') if (currentFile && this.editor.current()) { diff --git a/src/app/files/fileProvider.js b/src/app/files/fileProvider.js index 1b02dd398bc..ac6d1887b49 100644 --- a/src/app/files/fileProvider.js +++ b/src/app/files/fileProvider.js @@ -98,7 +98,8 @@ class FileProvider { return true } if (!exists && unprefixedpath.indexOf('/') !== -1) { - this.createDir(path) + // the last element is the filename and we should remove it + this.createDir(path.substr(0, path.lastIndexOf('/'))) } try { window.remixFileSystem.writeFileSync(unprefixedpath, content) @@ -116,9 +117,8 @@ class FileProvider { } createDir (path, cb) { - var unprefixedpath = this.removePrefix(path) + const unprefixedpath = this.removePrefix(path) const paths = unprefixedpath.split('/') - paths.pop() // last element should the filename if (paths.length && paths[0] === '') paths.shift() let currentCheck = '' paths.forEach((value) => { @@ -226,7 +226,7 @@ class FileProvider { if (files) { files.forEach(element => { const absPath = (path === '/' ? '' : path) + '/' + element - ret[absPath.indexOf('/') === 0 ? absPath.replace('/', '') : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() } + ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() } // ^ ret does not accept path starting with '/' }) } diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js index 57bb51f8062..c44a1101d85 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -69,7 +69,7 @@ module.exports = class SettingsTab extends ViewPlugin { // Gist settings const token = this.config.get('settings/gist-access-token') - const gistAccessToken = yo`` + const gistAccessToken = yo`` if (token) gistAccessToken.value = token const removeToken = () => { self.config.set('settings/gist-access-token', ''); gistAccessToken.value = ''; tooltip('Access token removed') } const saveToken = () => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') } diff --git a/src/app/tabs/test-tab.js b/src/app/tabs/test-tab.js index abc9986a1d4..96c24a2b970 100644 --- a/src/app/tabs/test-tab.js +++ b/src/app/tabs/test-tab.js @@ -33,11 +33,13 @@ module.exports = class TestTab extends ViewPlugin { this.runningTestsNumber = 0 this.readyTestsNumber = 0 this.areTestsRunning = false + this.defaultPath = 'browser/tests' + appManager.event.on('activate', (name) => { - if (name === 'solidity') this.updateRunAction(fileManager.currentFile()) + if (name === 'solidity') this.updateRunAction() }) appManager.event.on('deactivate', (name) => { - if (name === 'solidity') this.updateRunAction(fileManager.currentFile()) + if (name === 'solidity') this.updateRunAction() }) } @@ -56,9 +58,6 @@ module.exports = class TestTab extends ViewPlugin { }) this.fileManager.events.on('noFileSelected', () => { - this.updateGenerateFileAction() - this.updateRunAction() - this.updateTestFileList() }) this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file)) @@ -86,6 +85,7 @@ module.exports = class TestTab extends ViewPlugin { } listTests () { + if (!this.data.allTests) return [] return this.data.allTests.map( testFile => this.createSingleTest(testFile) ) @@ -351,6 +351,13 @@ module.exports = class TestTab extends ViewPlugin { }) } + updateCurrentPath (e) { + const newValue = e.target.value === '' ? this.defaultPath : e.target.value + this.testTabLogic.setCurrentPath(newValue) + this.updateRunAction() + this.updateForNewCurrent() + } + runTests () { this.areTestsRunning = true this.hasBeenStopped = false @@ -386,21 +393,16 @@ module.exports = class TestTab extends ViewPlugin { } updateGenerateFileAction (currentFile) { - let el = yo`` - if ( - !currentFile || - (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol') - ) { - el.setAttribute('disabled', 'disabled') - el.setAttribute('title', 'No solidity file selected') - } + let el = yo` + + ` if (!this.generateFileActionElement) { this.generateFileActionElement = el } else { @@ -411,13 +413,13 @@ module.exports = class TestTab extends ViewPlugin { updateRunAction (currentFile) { let el = yo` - ` - const isSolidityActive = this.appManager.actives.includes('solidity') - if (!currentFile || !isSolidityActive || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) { + const isSolidityActive = this.appManager.isActive('solidity') + if (!isSolidityActive || !this.listTests().length) { el.setAttribute('disabled', 'disabled') if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) { el.setAttribute('title', 'No solidity file selected') @@ -451,6 +453,7 @@ module.exports = class TestTab extends ViewPlugin { } else { yo.update(this.testFilesListElement, el) } + this.updateRunAction() return this.testFilesListElement } @@ -482,11 +485,40 @@ module.exports = class TestTab extends ViewPlugin { return yo`Progress: ${ready} finished (of ${this.runningTestsNumber})` } + updateDirList () { + for (var o of this.uiPathList.querySelectorAll('option')) o.remove() + this.uiPathList.appendChild(yo``) + if (this.testTabLogic.isRemixDActive()) this.uiPathList.appendChild(yo``) + if (!this._view.el) return + this.testTabLogic.dirList(this.inputPath.value).then((options) => { + options.forEach((path) => this.uiPathList.appendChild(yo``)) + }) + } + render () { this.onActivationInternal() this.testsOutput = yo`