open-save workspace

pull/866/head
yann300 4 years ago
parent f3380cc397
commit 22ea754f1f
  1. 1
      apps/remix-ide/.gitignore
  2. 1
      apps/remix-ide/contracts/README.txt
  3. 25
      apps/remix-ide/src/app.js
  4. 7
      apps/remix-ide/src/app/compiler/compiler-imports.js
  5. 1
      apps/remix-ide/src/app/editor/editor.js
  6. 13
      apps/remix-ide/src/app/files/compiler-metadata.js
  7. 4
      apps/remix-ide/src/app/files/file-explorer.js
  8. 48
      apps/remix-ide/src/app/files/fileManager.js
  9. 36
      apps/remix-ide/src/app/files/fileProvider.js
  10. 22
      apps/remix-ide/src/app/files/remixDProvider.js
  11. 41
      apps/remix-ide/src/app/files/workspaceFileProvider.js
  12. 185
      apps/remix-ide/src/app/panels/file-panel.js
  13. 3
      apps/remix-ide/src/app/panels/styles/file-panel-styles.css
  14. 1
      apps/remix-ide/src/app/tabs/compile-tab.js
  15. 15
      apps/remix-ide/src/app/tabs/test-tab.js
  16. 2
      apps/remix-ide/src/app/tabs/testTab/testTab.js
  17. 18
      apps/remix-ide/src/config.js
  18. 11
      apps/remix-ide/src/lib/gist-handler.js
  19. 5
      apps/remix-ide/src/lib/helper.js
  20. 30
      apps/remix-ide/src/migrateFileSystem.js
  21. 7
      apps/remix-ide/src/remixAppManager.js

@ -9,7 +9,6 @@ soljson.js.*
npm-debug.log*
remix
.DS_Store
contracts
TODO
.tern-port
temp_publish_docker

@ -18,7 +18,7 @@ import { LandingPage } from './app/ui/landing-page/landing-page'
import { MainPanel } from './app/components/main-panel'
import FetchAndCompile from './app/compiler/compiler-sourceVerifier-fetchAndCompile'
import migrateFileSystem from './migrateFileSystem'
import migrateFileSystem, { migrateToWorkspace } from './migrateFileSystem'
var isElectron = require('is-electron')
var csjs = require('csjs-inject')
@ -28,14 +28,13 @@ var registry = require('./global/registry')
var loadFileFromParent = require('./loadFilesFromParent')
var { OffsetToLineColumnConverter } = require('./lib/offsetToLineColumnConverter')
var QueryParams = require('./lib/query-params')
var GistHandler = require('./lib/gist-handler')
var Storage = remixLib.Storage
var RemixDProvider = require('./app/files/remixDProvider')
var Config = require('./config')
var examples = require('./app/editor/examples')
var modalDialogCustom = require('./app/ui/modal-dialog-custom')
var FileManager = require('./app/files/fileManager')
var FileProvider = require('./app/files/fileProvider')
var WorkspaceFileProvider = require('./app/files/workspaceFileProvider')
var toolTip = require('./app/ui/tooltip')
var CompilerMetadata = require('./app/files/compiler-metadata')
var CompilerImport = require('./app/compiler/compiler-imports')
@ -145,6 +144,9 @@ class App {
registry.put({ api: self._components.filesProviders.browser, name: 'fileproviders/browser' })
self._components.filesProviders.localhost = new RemixDProvider(self.appManager)
registry.put({ api: self._components.filesProviders.localhost, name: 'fileproviders/localhost' })
self._components.filesProviders.workspace = new WorkspaceFileProvider()
registry.put({ api: self._components.filesProviders.workspace, name: 'fileproviders/workspace' })
registry.put({ api: self._components.filesProviders, name: 'fileproviders' })
migrateFileSystem(self._components.filesProviders.browser)
@ -438,20 +440,9 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// get the file list from the parent iframe
loadFileFromParent(fileManager)
// get the file from gist
const gistHandler = new GistHandler()
const loadedFromGist = gistHandler.loadFromGist(params, fileManager)
if (!loadedFromGist) {
// insert example contracts if there are no files to show
self._components.filesProviders.browser.resolveDirectory('/', (error, filesList) => {
if (error) console.error(error)
if (Object.keys(filesList).length === 0) {
for (const file in examples) {
fileManager.writeFile(examples[file].name, examples[file].content)
}
}
})
}
migrateToWorkspace(fileManager)
filePanel.initWorkspace()
if (params.code) {
try {

@ -77,7 +77,7 @@ module.exports = class CompilerImports extends Plugin {
}
cb(null, content, cleanUrl, type, url)
} catch (e) {
return cb('Unable to import url : ' + e.message)
return cb(new Error('not found ' + url))
}
}
@ -88,9 +88,9 @@ module.exports = class CompilerImports extends Plugin {
(error, content, cleanUrl, type, url) => {
if (error) return cb(error)
if (this.fileManager) {
const browser = this.fileManager.fileProviderOf('browser/')
const workspace = this.fileManager.getProvider('workspace')
const path = targetPath || type + '/' + cleanUrl
if (browser) browser.addExternal(path, content, url)
if (workspace) workspace.addExternal(path, content, url)
}
cb(null, content)
})
@ -123,7 +123,6 @@ module.exports = class CompilerImports extends Plugin {
}
provider.exists(url, (error, exist) => {
if (error) return reject(error)
if (!exist && provider.type === 'localhost') return reject(new Error(`not found ${url}`))
/*
if the path is absolute and the file does not exist, we can stop here

@ -355,7 +355,6 @@ class Editor extends Plugin {
- URL prepended with "browser"
- URL not prepended with the file explorer. We assume (as it is in the whole app, that this is a "browser" URL
*/
if (!path.startsWith('localhost') && !path.startsWith('browser')) path = `browser/${path}`
if (!this.sessions[path]) {
const session = this._createSession(content, this._getMode(path))
this.sessions[path] = session

@ -1,6 +1,7 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import { joinPath } from '../../lib/helper'
var CompilerAbstract = require('../compiler/compiler-abstract')
const profile = {
@ -21,11 +22,11 @@ class CompilerMetadata extends Plugin {
}
_JSONFileName (path, contractName) {
return path + '/' + this.innerPath + '/' + contractName + '.json'
return joinPath(path, this.innerPath, contractName + '.json')
}
_MetadataFileName (path, contractName) {
return path + '/' + this.innerPath + '/' + contractName + '_metadata' + '.json'
return joinPath(path, this.innerPath, contractName + '_metadata.json')
}
onActivation () {
@ -33,9 +34,9 @@ class CompilerMetadata extends Plugin {
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
if (!self.config.get('settings/generate-contract-metadata')) return
const compiler = new CompilerAbstract(languageVersion, data, source)
var provider = self.fileManager.currentFileProvider()
var path = self.fileManager.currentPath()
if (provider && path) {
var provider = self.fileManager.fileProviderOf(source.target)
var path = self.fileManager.extractPathOf(source.target)
if (provider) {
compiler.visitContracts((contract) => {
if (contract.file !== source.target) return
@ -117,7 +118,7 @@ class CompilerMetadata extends Plugin {
path = this.fileManager.currentPath()
}
if (provider && path) {
if (provider) {
this.blockchain.detectNetwork((err, { id, name } = {}) => {
if (err) {
console.log(err)

@ -506,7 +506,7 @@ fileExplorer.prototype.toGist = function (id) {
}
// If 'id' is not defined, it is not a gist update but a creation so we have to take the files from the browser explorer.
const folder = id ? 'browser/gists/' + id : 'browser/'
const folder = id ? '/gists/' + id : '/'
this.packageFiles(this.files, folder, (error, packaged) => {
if (error) {
console.log(error)
@ -596,7 +596,7 @@ fileExplorer.prototype.packageFiles = function (filesProvider, directory, callba
})
}
fileExplorer.prototype.createNewFile = function (parentFolder = 'browser') {
fileExplorer.prototype.createNewFile = function (parentFolder = '/') {
const self = this
modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => {
if (!input) input = 'New file'

@ -39,6 +39,7 @@ const createError = (err) => {
class FileManager extends Plugin {
constructor (editor, appManager) {
super(profile)
this.mode = 'browser'
this.openedFiles = {} // list all opened files
this.events = new EventEmitter()
this.editor = editor
@ -48,6 +49,10 @@ class FileManager extends Plugin {
this.init()
}
setMode (mode) {
this.mode = mode
}
/**
* Emit error if path doesn't exist
* @param {string} path path of the file/directory
@ -265,6 +270,7 @@ class FileManager extends Plugin {
config: this._components.registry.get('config').api,
browserExplorer: this._components.registry.get('fileproviders/browser').api,
localhostExplorer: this._components.registry.get('fileproviders/localhost').api,
workspaceExplorer: this._components.registry.get('fileproviders/workspace').api,
filesProviders: this._components.registry.get('fileproviders').api
}
this._deps.browserExplorer.event.register('fileChanged', (path) => { this.fileChangedEvent(path) })
@ -275,6 +281,11 @@ class FileManager extends Plugin {
this._deps.localhostExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
this._deps.localhostExplorer.event.register('errored', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
this._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
this._deps.workspaceExplorer.event.register('fileChanged', (path) => { this.fileChangedEvent(path) })
this._deps.workspaceExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.workspaceExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
this._deps.workspaceExplorer.event.register('fileAdded', (path) => { this.fileAddedEvent(path) })
this.getCurrentFile = this.file
this.getFile = this.readFile
this.getFolder = this.readdir
@ -349,7 +360,7 @@ class FileManager extends Plugin {
extractPathOf (file) {
var reg = /(.*)(\/).*/
var path = reg.exec(file)
return path ? path[1] : null
return path ? path[1] : '/'
}
getFileContent (path) {
@ -468,18 +479,8 @@ class FileManager extends Plugin {
}
if (file) return _openFile(file)
else {
var browserProvider = this._deps.filesProviders.browser
browserProvider.resolveDirectory('browser', (error, filesProvider) => {
if (error) console.error(error)
var fileList = Object.keys(filesProvider)
if (fileList.length) {
_openFile(browserProvider.type + '/' + fileList[0])
} else {
// TODO: Only keep `this.emit` (issue#2210)
this.emit('noFileSelected')
this.events.emit('noFileSelected')
}
})
this.emit('noFileSelected')
this.events.emit('noFileSelected')
}
}
@ -488,10 +489,13 @@ class FileManager extends Plugin {
}
fileProviderOf (file) {
if (file.indexOf('localhost') === 0) {
if (file.startsWith('localhost') || this.mode === 'localhost') {
return this._deps.filesProviders.localhost
}
return this._deps.filesProviders.browser
if (file.startsWith('browser')) {
return this._deps.filesProviders.browser
}
return this._deps.filesProviders.workspace
}
// returns the list of directories inside path
@ -501,10 +505,8 @@ class FileManager extends Plugin {
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)
if (ls[item].isDirectory && !dirPaths.includes(item)) {
dirPaths.push(item)
resolve(dirPaths)
}
return new Promise((resolve, reject) => { resolve() })
@ -579,6 +581,14 @@ class FileManager extends Plugin {
if (callback) callback(error)
})
}
async createWorkspace (name) {
const workspaceProvider = this._deps.filesProviders.workspace
const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name
const workspaceRootPath = 'browser/' + workspaceProvider.workspacesPath
if (!this.exists(workspaceRootPath)) await this.mkdir(workspaceRootPath)
if (!this.exists(workspacePath)) await this.mkdir(workspacePath)
}
}
module.exports = FileManager

@ -193,6 +193,39 @@ class FileProvider {
})
}
/**
* copy the folder recursively
* @param {string} path is the folder to be copied over
* @param {string} destination is the destination folder
*/
copyFolderToJson (path) {
return new Promise((resolve, reject) => {
const json = {}
path = this.removePrefix(path)
if (window.remixFileSystem.existsSync(path)) {
try {
const items = window.remixFileSystem.readdirSync(path)
if (items.length !== 0) {
items.forEach(async (item, index) => {
const file = {}
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}`
if (window.remixFileSystem.statSync(curPath).isDirectory()) {
file.children = await this.copyFolderToJson(curPath)
} else {
file.content = window.remixFileSystem.readFileSync(curPath, 'utf8')
}
json[curPath] = file
})
}
} catch (e) {
console.log(e)
return reject(e)
}
}
return resolve(json)
})
}
removeFile (path) {
path = this.removePrefix(path)
if (window.remixFileSystem.existsSync(path) && !window.remixFileSystem.statSync(path).isDirectory()) {
@ -227,6 +260,8 @@ class FileProvider {
if (files) {
files.forEach(element => {
path = path.replace(/^\/|\/$/g, '') // remove first and last slash
element = element.replace(/^\/|\/$/g, '') // remove first and last slash
const absPath = (path === '/' ? '' : path) + '/' + element
ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() }
// ^ ret does not accept path starting with '/'
@ -243,7 +278,6 @@ class FileProvider {
}
_normalizePath (path) {
if (path.indexOf('/') !== 0) path = '/' + path
return this.type + path
}
}

@ -23,23 +23,23 @@ module.exports = class RemixDProvider {
})
this._appManager.on('remixd', 'folderAdded', (path) => {
this.event.trigger('folderAdded', [this.addPrefix(path)])
this.event.trigger('folderAdded', [path])
})
this._appManager.on('remixd', 'fileAdded', (path) => {
this.event.trigger('fileAdded', [this.addPrefix(path)])
this.event.trigger('fileAdded', [path])
})
this._appManager.on('remixd', 'fileChanged', (path) => {
this.event.trigger('fileChanged', [this.addPrefix(path)])
this.event.trigger('fileChanged', [path])
})
this._appManager.on('remixd', 'fileRemoved', (path) => {
this.event.trigger('fileRemoved', [this.addPrefix(path)])
this.event.trigger('fileRemoved', [path])
})
this._appManager.on('remixd', 'fileRenamed', (oldPath, newPath) => {
this.event.trigger('fileRemoved', [this.addPrefix(oldPath), this.addPrefix(newPath)])
this.event.trigger('fileRemoved', [oldPath, newPath])
})
this._appManager.on('remixd', 'rootFolderChanged', () => {
@ -136,7 +136,7 @@ module.exports = class RemixDProvider {
const unprefixedpath = this.removePrefix(path)
this._appManager.call('remixd', 'remove', { path: unprefixedpath })
.then(result => {
const path = this.type + '/' + unprefixedpath
const path = unprefixedpath
delete this.filesContent[path]
resolve(true)
@ -154,8 +154,8 @@ module.exports = class RemixDProvider {
if (!this._isReady) return new Promise((resolve, reject) => reject(new Error('provider not ready')))
return this._appManager.call('remixd', 'rename', { oldPath: unprefixedoldPath, newPath: unprefixednewPath })
.then(result => {
const newPath = this.type + '/' + unprefixednewPath
const oldPath = this.type + '/' + unprefixedoldPath
const newPath = unprefixednewPath
const oldPath = unprefixedoldPath
this.filesContent[newPath] = this.filesContent[oldPath]
delete this.filesContent[oldPath]
@ -181,12 +181,6 @@ module.exports = class RemixDProvider {
return path
}
addPrefix (path) {
if (path.indexOf(this.type + '/') === 0) return path
if (path[0] === '/') return 'localhost' + path
return 'localhost/' + path
}
resolveDirectory (path, callback) {
var self = this
if (path[0] === '/') path = path.substring(1)

@ -0,0 +1,41 @@
'use strict'
const FileProvider = require('./fileProvider')
class WorkspaceFileProvider extends FileProvider {
constructor () {
super('')
this.workspacesPath = '.workspaces'
}
setWorkspace (workspace) {
workspace = workspace.replace(/^\/|\/$/g, '') // remove first and last slash
this.workspace = workspace
}
removePrefix (path) {
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
path = super.removePrefix(path)
const ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path)
return ret.replace(/^\/|\/$/g, '')
}
resolveDirectory (path, callback) {
super.resolveDirectory(path, (error, files) => {
if (error) return callback(error)
const unscoped = {}
for (const file in files) {
unscoped[file.replace(this.workspacesPath + '/' + this.workspace + '/', '')] = files[file]
}
callback(null, unscoped)
})
}
_normalizePath (path) {
return path.replace(this.workspacesPath + '/' + this.workspace + '/', '')
}
}
module.exports = WorkspaceFileProvider

@ -7,10 +7,13 @@ import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line
import './styles/file-panel-styles.css'
var yo = require('yo-yo')
var EventManager = require('../../lib/events')
// var FileExplorer = require('../files/file-explorer')
var { RemixdHandle } = require('../files/remixd-handle.js')
var { GitHandle } = require('../files/git-handle.js')
var globalRegistry = require('../../global/registry')
var examples = require('../editor/examples')
var GistHandler = require('../../lib/gist-handler')
var QueryParams = require('../../lib/query-params')
const modalDialog = require('../ui/modal-dialog-custom')
var canUpload = window.File || window.FileReader || window.FileList || window.Blob
@ -54,13 +57,16 @@ module.exports = class Filepanel extends ViewPlugin {
fileManager: this._components.registry.get('filemanager').api,
config: this._components.registry.get('config').api
}
this.LOCALHOST = '<Connect Localhost>'
this.hideRemixdExplorer = true
this.remixdExplorer = {
hide: () => {
this._deps.fileManager.setMode('browser')
this.hideRemixdExplorer = true
this.renderComponent()
},
show: () => {
this._deps.fileManager.setMode('localhost')
this.hideRemixdExplorer = false
this.renderComponent()
}
@ -93,9 +99,54 @@ module.exports = class Filepanel extends ViewPlugin {
this.remixdExplorer.hide()
})
this.currentWorkspace = null
this.renderComponent()
}
initWorkspace () {
const queryParams = new QueryParams()
const params = queryParams.get()
// get the file from gist
const gistHandler = new GistHandler()
const loadedFromGist = gistHandler.loadFromGist(params, this._deps.fileManager)
if (!loadedFromGist) {
// insert example contracts if there are no files to show
this._deps.fileProviders.browser.resolveDirectory('/', async (error, filesList) => {
if (error) console.error(error)
if (Object.keys(filesList).length === 0) {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
for (const file in examples) {
await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + examples[file].name, examples[file].content)
}
this.setWorkspace('default_workspace')
}
})
} else {
this.setWorkspace('gists')
}
}
refreshWorkspacesList () {
if (!document.getElementById('workspacesSelect')) return
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, fileTree) => {
if (error) console.error(error)
const items = fileTree
items[this.LOCALHOST] = { isLocalHost: true }
ReactDOM.render(
(
Object.keys(items)
.filter((item) => fileTree[item].isDirectory || fileTree[item].isLocalHost)
.map((folder) => {
folder = folder.replace(workspacesPath + '/', '')
return <option selected={this.currentWorkspace === folder} value={folder}>{folder}</option>
})), document.getElementById('workspacesSelect')
)
if (!this.currentWorkspace) this.setWorkspace(Object.keys(fileTree)[0].replace(workspacesPath + '/', ''))
})
}
resetFocus (value) {
this.reset = value
this.renderComponent()
@ -125,6 +176,19 @@ module.exports = class Filepanel extends ViewPlugin {
return this.el
}
setWorkspace (name) {
this._deps.fileManager.removeTabsOf(this._deps.fileProviders.workspace)
this.currentWorkspace = name
if (name === this.LOCALHOST) {
this.call('manager', 'activatePlugin', 'remixd')
} else {
this._deps.fileProviders.workspace.setWorkspace(name)
this.call('manager', 'deactivatePlugin', 'remixd')
}
// TODO remove the opened tabs from the previous workspace
this.renderComponent()
}
/**
*
* @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] }
@ -139,24 +203,104 @@ module.exports = class Filepanel extends ViewPlugin {
this.renderComponent()
}
renameWorkspace () {
modalDialog.prompt('Rename Workspace', 'Please choose a name for the workspace', this.currentWorkspace, async (value) => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
await this._deps.fileManager.rename('browser/' + workspacesPath + '/' + this.currentWorkspace, 'browser/workspaces/' + value)
setTimeout(async () => {
this.setWorkspace(value)
}, 2000)
})
}
async createWorkspace () {
const workspace = `workspace_${Date.now()}`
modalDialog.prompt('New Workspace', 'Please choose a name for the workspace', workspace, (value) => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.createDir(workspacesPath + '/' + value, async () => {
this.setWorkspace(value)
setTimeout(async () => {
for (const file in examples) {
await this._deps.fileManager.writeFile(`${examples[file].name}`, examples[file].content)
}
}, 2000)
})
})
}
deleteCurrentWorkspace () {
if (!this.currentWorkspace) return
modalDialog.confirm('Delete Workspace', 'Please confirm workspace deletion', () => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.remove(workspacesPath + '/' + this.currentWorkspace)
this.currentWorkspace = null
this.renderComponent()
})
}
renderComponent () {
ReactDOM.render(
<div className='remixui_container'>
<div className='remixui_fileexplorer' onClick={() => this.resetFocus(true)}>
<div>
<header>
<div class="mb-2">
<label className="form-check-label" htmlFor="workspacesSelect">
Workspaces
</label>
<span className="remixui_menu">
<span
id='workspaceCreate'
data-id='workspaceCreate'
onClick={(e) => {
e.stopPropagation()
this.createWorkspace()
}}
className='far fa-plus-square remixui_menuicon'
title='Create a new Workspace'>
</span>
<span
hidden={this.currentWorkspace === this.LOCALHOST}
id='workspaceRename'
data-id='workspaceRename'
onClick={(e) => {
e.stopPropagation()
this.renameWorkspace()
}}
className='far fa-edit remixui_menuicon'
title='Rename current Workspace'>
</span>
<span
hidden={this.currentWorkspace === this.LOCALHOST}
id='workspaceDelete'
data-id='workspaceDelete'
onClick={(e) => {
e.stopPropagation()
this.deleteCurrentWorkspace()
}}
className='fas fa-trash'
title='Delete current Workspace'>
</span>
</span>
<select id="workspacesSelect" data-id="workspacesSelect" onChange={(e) => this.setWorkspace(e.target.value)} className="form-control custom-select">
</select>
</div>
</header>
</div>
<div className='remixui_fileExplorerTree'>
<div>
<div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'>
<FileExplorer
name='browser'
registry={this._components.registry}
filesProvider={this._deps.fileProviders.browser}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={this}
focusRoot={this.reset}
contextMenuItems={this.registeredMenuItems}
displayInput={this.displayNewFile}
externalUploads={this.uploadFileEvent}
/>
{ this.hideRemixdExplorer && this.currentWorkspace &&
<FileExplorer
name={this.currentWorkspace}
registry={this._components.registry}
filesProvider={this._deps.fileProviders.workspace}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={this}
focusRoot={this.reset}
contextMenuItems={this.registeredMenuItems}
/>
}
</div>
<div className='pl-2 filesystemexplorer remixui_treeview'>
{ !this.hideRemixdExplorer &&
@ -171,10 +315,27 @@ module.exports = class Filepanel extends ViewPlugin {
/>
}
</div>
<div className='pl-2 remixui_treeview'>
{ false && <FileExplorer
name='browser'
registry={this._components.registry}
filesProvider={this._deps.fileProviders.browser}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={this}
focusRoot={this.reset}
contextMenuItems={this.registeredMenuItems}
displayInput={this.displayNewFile}
externalUploads={this.uploadFileEvent}
/>
}
</div>
</div>
</div>
</div>
</div>
, this.el)
setTimeout(() => {
this.refreshWorkspacesList()
}, 500)
}
}

@ -54,3 +54,6 @@
margin-bottom: 2em;
word-break: break-word;
}
.remixui_menuicon {
padding-right : 10px;
}

@ -227,6 +227,7 @@ class CompileTab extends ViewPlugin {
* @param {object} settings {evmVersion, optimize, runs, version, language}
*/
async compileWithParameters (compilationTargets, settings) {
settings.version = settings.version || this.compilerContainer.data.selectedVersion
const res = await compile(compilationTargets, settings)
return res
}

@ -35,7 +35,7 @@ module.exports = class TestTab extends ViewPlugin {
this.runningTestsNumber = 0
this.readyTestsNumber = 0
this.areTestsRunning = false
this.defaultPath = 'browser/tests'
this.defaultPath = 'tests'
this.offsetToLineColumnConverter = offsetToLineColumnConverter
appManager.event.on('activate', (name) => {
@ -551,12 +551,17 @@ module.exports = class TestTab extends ViewPlugin {
updateDirList () {
for (var o of this.uiPathList.querySelectorAll('option')) o.remove()
this.uiPathList.appendChild(yo`<option>browser</option>`)
if (this.testTabLogic.isRemixDActive()) this.uiPathList.appendChild(yo`<option>localhost</option>`)
if (!this._view.el) return
this.testTabLogic.dirList(this.inputPath.value).then((options) => {
this.testTabLogic.dirList('/').then((options) => {
options.forEach((path) => this.uiPathList.appendChild(yo`<option>${path}</option>`))
})
/*
It is not possible anymore to see folder from outside of the current workspace
if (this.inputPath.value) {
this.testTabLogic.dirList(this.inputPath.value).then((options) => {
options.forEach((path) => this.uiPathList.appendChild(yo`<option>${path}</option>`))
})
}
*/
}
render () {

@ -5,7 +5,7 @@ const remixPath = require('path')
class TestTabLogic {
constructor (fileManager) {
this.fileManager = fileManager
this.currentPath = 'browser/tests'
this.currentPath = '/tests'
}
setCurrentPath (path) {

@ -22,7 +22,6 @@ function Config (storage) {
}
this.get = function (key) {
this.ensureStorageUpdated(key)
return this.items[key]
}
@ -35,23 +34,6 @@ function Config (storage) {
}
}
this.ensureStorageUpdated = function (key) {
if (key === 'currentFile') {
if (this.items[key] && this.items[key] !== '' &&
this.items[key].indexOf('config/') !== 0 &&
this.items[key].indexOf('browser/') !== 0 &&
this.items[key].indexOf('localhost/') !== 0 &&
this.items[key].indexOf('swarm/') !== 0 &&
this.items[key].indexOf('gist/') !== 0 &&
this.items[key].indexOf('github/') !== 0 &&
this.items[key].indexOf('ipfs/') !== 0 &&
this.items[key].indexOf('http/') !== 0 &&
this.items[key].indexOf('https/') !== 0) {
this.items[key] = 'browser/' + this.items[key]
}
}
}
this.getUnpersistedProperty = function (key) {
return this.unpersistedItems[key]
}

@ -42,25 +42,24 @@ function GistHandler (_window) {
}
this.loadFromGist = (params, fileManager) => {
const gistProvider = fileManager.fileProviderOf('browser')
const self = this
return self.handleLoad(params, function (gistId) {
request.get({
url: `https://api.github.com/gists/${gistId}`,
json: true
}, (error, response, data = {}) => {
}, async (error, response, data = {}) => {
if (error || !data.files) {
modalDialogCustom.alert(`Gist load error: ${error || data.message}`)
return
}
const obj = {}
Object.keys(data.files).forEach((element) => {
obj['/gists/' + gistId + '/' + element] = data.files[element]
obj['/' + gistId + '/' + element] = data.files[element]
})
fileManager.setBatchFiles(obj, 'browser', true, (errorLoadingFile) => {
fileManager.setBatchFiles(obj, 'workspace', true, (errorLoadingFile) => {
if (!errorLoadingFile) {
gistProvider.id = gistId
gistProvider.origGistFiles = data.files
const provider = fileManager.getProvider('workspace')
provider.lastLoadedGistId = gistId
}
})
})

@ -79,6 +79,11 @@ module.exports = {
? 'fak fa-vyper-mono' : path.endsWith('.lex')
? 'fak fa-lexon' : path.endsWith('.contract')
? 'fab fa-ethereum' : 'far fa-file'
},
joinPath (...paths) {
paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash)
if (paths.length === 1) return paths[0]
return paths.join('/')
}
}

@ -1,5 +1,5 @@
import { Storage } from '@remix-project/remix-lib'
import { joinPath } from './lib/helper'
/*
Migrating the files to the BrowserFS storage instead or raw localstorage
@ -20,3 +20,31 @@ export default (fileProvider) => {
})
fileStorageBrowserFS.set(flag, 'done')
}
export async function migrateToWorkspace (fileManager) {
const browserProvider = fileManager.getProvider('browser')
const workspaceProvider = fileManager.getProvider('workspace')
const flag = 'status'
const fileStorageBrowserWorkspace = new Storage('remix_browserWorkspace_migration:')
if (fileStorageBrowserWorkspace.get(flag) === 'done') return
const files = await browserProvider.copyFolderToJson('/')
console.log(files)
const workspaceName = 'default_workspace'
const workspacePath = joinPath('browser', workspaceProvider.workspacesPath, workspaceName)
await fileManager.createWorkspace(workspaceName)
await populateWorkspace(workspacePath, files, fileManager)
fileStorageBrowserWorkspace.set(flag, 'done')
}
const populateWorkspace = async (workspace, json, fileManager) => {
for (const item in json) {
const isFolder = json[item].content === undefined
if (isFolder) {
await fileManager.mkdir(joinPath(workspace, item))
await populateWorkspace(workspace, json[item].children, fileManager)
} else {
await fileManager.writeFile(joinPath(workspace, item), json[item].content)
}
}
}

@ -130,13 +130,12 @@ class PluginLoader {
constructor () {
const queryParams = new QueryParams()
this.donotAutoReload = ['remixd'] // that would be a bad practice to force loading some plugins at page load.
this.donotAutoReload = ['remixd', 'git'] // that would be a bad practice to force loading some plugins at page load.
this.loaders = {}
this.loaders.localStorage = {
set: (plugin, actives) => {
if (!this.donotAutoReload.includes(plugin.name)) {
localStorage.setItem('workspace', JSON.stringify(actives))
}
const saved = actives.filter((name) => !this.donotAutoReload.includes(name))
localStorage.setItem('workspace', JSON.stringify(saved))
},
get: () => { return JSON.parse(localStorage.getItem('workspace')) }
}

Loading…
Cancel
Save