diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index 932368c20b..cc73ba0942 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -563,8 +563,6 @@ class FileManager extends Plugin { if (file.startsWith('browser')) { return this._deps.filesProviders.browser } - const provider = this._deps.filesProviders.workspace - if (!provider.isReady()) throw createError({ code: 'ECONNRESET', message: 'No workspace has been opened.' }) return this._deps.filesProviders.workspace } diff --git a/apps/remix-ide/src/app/files/workspaceFileProvider.js b/apps/remix-ide/src/app/files/workspaceFileProvider.js index 70854929d9..742d85a775 100644 --- a/apps/remix-ide/src/app/files/workspaceFileProvider.js +++ b/apps/remix-ide/src/app/files/workspaceFileProvider.js @@ -1,5 +1,6 @@ 'use strict' +const EventManager = require('../../lib/events') const FileProvider = require('./fileProvider') const pathModule = require('path') @@ -8,6 +9,7 @@ class WorkspaceFileProvider extends FileProvider { super('') this.workspacesPath = '.workspaces' this.workspace = null + this.event = new EventManager() } setWorkspace (workspace) { @@ -28,7 +30,7 @@ class WorkspaceFileProvider extends FileProvider { } removePrefix (path) { - if (!this.workspace) throw new Error('No workspace has been opened.') + if (!this.workspace) this.createWorkspace() path = path.replace(/^\/|\/$/g, '') // remove first and last slash if (path.startsWith(this.workspacesPath + '/' + this.workspace)) return path if (path.startsWith(this.workspace)) return this.workspacesPath + '/' + this.workspace @@ -49,7 +51,7 @@ class WorkspaceFileProvider extends FileProvider { } resolveDirectory (path, callback) { - if (!this.workspace) throw new Error('No workspace has been opened.') + if (!this.workspace) this.createWorkspace() super.resolveDirectory(path, (error, files) => { if (error) return callback(error) const unscoped = {} @@ -74,9 +76,14 @@ class WorkspaceFileProvider extends FileProvider { } _normalizePath (path) { - if (!this.workspace) throw new Error('No workspace has been opened.') + if (!this.workspace) this.createWorkspace() return path.replace(this.workspacesPath + '/' + this.workspace + '/', '') } + + createWorkspace (name) { + if (!name) name = 'default_workspace' + this.event.trigger('createWorkspace', [name]) + } } module.exports = WorkspaceFileProvider diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 7e44fc2520..ecf4b0a55b 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -202,14 +202,17 @@ module.exports = class Filepanel extends ViewPlugin { if (!workspaceName) throw new Error('name cannot be empty') if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed') if (await this.workspaceExists(workspaceName)) throw new Error('workspace already exists') - const browserProvider = this._deps.fileProviders.browser - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - await this.processCreateWorkspace(workspaceName) - for (const file in examples) { - try { - await browserProvider.set('browser/' + workspacesPath + '/' + workspaceName + '/' + examples[file].name, examples[file].content) - } catch (error) { - console.error(error) + else { + this._deps.fileProviders.workspace.setWorkspace(workspaceName) + const browserProvider = this._deps.fileProviders.browser + const workspacesPath = this._deps.fileProviders.workspace.workspacesPath + await this.processCreateWorkspace(workspaceName) + for (const file in examples) { + try { + await browserProvider.set('browser/' + workspacesPath + '/' + workspaceName + '/' + examples[file].name, examples[file].content) + } catch (error) { + console.error(error) + } } } } diff --git a/apps/remix-ide/src/app/tabs/testTab/testTab.js b/apps/remix-ide/src/app/tabs/testTab/testTab.js index 56ecf1cca1..9b18732950 100644 --- a/apps/remix-ide/src/app/tabs/testTab/testTab.js +++ b/apps/remix-ide/src/app/tabs/testTab/testTab.js @@ -67,11 +67,17 @@ class TestTabLogic { generateTestContractSample (hasCurrent, fileToImport, contractName = 'testSuite') { let relative = remixPath.relative(this.currentPath, remixPath.dirname(fileToImport)) if (relative === '') relative = '.' - const comment = hasCurrent ? `import "${relative}/${remixPath.basename(fileToImport)}";` : '// Import here the file to test.' + const comment = hasCurrent ? `import "${relative}/${remixPath.basename(fileToImport)}";` : '// ' return `// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.22 <0.9.0; -import "remix_tests.sol"; // this import is automatically injected by Remix. + +// This import is automatically injected by Remix +import "remix_tests.sol"; + +// This import is required to use custom transaction context +// Although it may fail compilation in 'Solidity Compiler' plugin +// But it will work fine in 'Solidity Unit Testing' plugin import "remix_accounts.sol"; ${comment} @@ -81,15 +87,15 @@ contract ${contractName} { /// 'beforeAll' runs before all other tests /// More special functions are: 'beforeEach', 'beforeAll', 'afterEach' & 'afterAll' function beforeAll() public { - // Here should instantiate tested contract + // Assert.equal(uint(1), uint(1), "1 should be equal to 1"); } function checkSuccess() public { - // Use 'Assert' to test the contract, - // See documentation: https://remix-ide.readthedocs.io/en/latest/assert_library.html - Assert.equal(uint(2), uint(2), "2 should be equal to 2"); - Assert.notEqual(uint(2), uint(3), "2 should not be equal to 3"); + // Use 'Assert' methods: https://remix-ide.readthedocs.io/en/latest/assert_library.html + Assert.ok(2 == 2, 'should be true'); + Assert.greaterThan(uint(2), uint(1), "2 should be greater than to 1"); + Assert.lesserThan(uint(2), uint(3), "2 should be lesser than to 3"); } function checkSuccess2() public pure returns (bool) { @@ -98,11 +104,10 @@ contract ${contractName} { } function checkFailure() public { - Assert.equal(uint(1), uint(2), "1 is not equal to 2"); + Assert.notEqual(uint(1), uint(1), "1 should not be equal to 1"); } - /// Custom Transaction Context - /// See more: https://remix-ide.readthedocs.io/en/latest/unittesting.html#customization + /// Custom Transaction Context: https://remix-ide.readthedocs.io/en/latest/unittesting.html#customization /// #sender: account-1 /// #value: 100 function checkSenderAndValue() public payable { diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 6a41a942bc..3537de5331 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react' // eslint-disable-lin import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line import './remix-ui-workspace.css' import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line +import { Toaster } from '@remix-ui/toaster'// eslint-disable-line /* eslint-disable-next-line */ export interface WorkspaceProps { @@ -49,10 +50,13 @@ export const Workspace = (props: WorkspaceProps) => { } props.request.createNewFile = () => { + if (!state.workspaces.length) createNewWorkspace('default_workspace') props.plugin.resetNewFile() } props.request.uploadFile = (target) => { + if (!state.workspaces.length) createNewWorkspace('default_workspace') + setState(prevState => { return { ...prevState, uploadFileEvent: target } }) @@ -101,6 +105,10 @@ export const Workspace = (props: WorkspaceProps) => { remixdExplorer.loading() }) + props.workspace.event.register('createWorkspace', (name) => { + createNewWorkspace(name) + }) + if (props.initialWorkspace) { props.workspace.setWorkspace(props.initialWorkspace) setState(prevState => { @@ -109,6 +117,18 @@ export const Workspace = (props: WorkspaceProps) => { } }, []) + const createNewWorkspace = async (workspaceName) => { + try { + await props.fileManager.closeAllFiles() + await props.createWorkspace(workspaceName) + await setWorkspace(workspaceName) + toast('New default workspace has been created.') + } catch (e) { + modalMessage('Create Default Workspace', e.message) + console.error(e) + } + } + const [state, setState] = useState({ workspaces: [], reset: false, @@ -131,9 +151,16 @@ export const Workspace = (props: WorkspaceProps) => { }, handleHide: null }, - loadingLocalhost: false + loadingLocalhost: false, + toasterMsg: '' }) + const toast = (message: string) => { + setState(prevState => { + return { ...prevState, toasterMsg: message } + }) + } + /* workspace creation, renaming and deletion */ const renameCurrentWorkspace = () => { @@ -199,6 +226,7 @@ export const Workspace = (props: WorkspaceProps) => { const workspaceName = workspaceCreateInput.current.value try { + await props.fileManager.closeAllFiles() await props.createWorkspace(workspaceName) await setWorkspace(workspaceName) } catch (e) { @@ -312,6 +340,7 @@ export const Workspace = (props: WorkspaceProps) => { handleHide={ handleHideModal }> { (typeof state.modal.message !== 'string') && state.modal.message } +
resetFocus(true)}>
diff --git a/libs/remixd/package.json b/libs/remixd/package.json index e2ce01b2c1..607b0a7ff2 100644 --- a/libs/remixd/package.json +++ b/libs/remixd/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remixd", - "version": "0.3.4", + "version": "0.3.5", "description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)", "main": "index.js", "types": "./index.d.ts",