Unverified Commit 134245e0 authored by yann300's avatar yann300 Committed by GitHub

Merge pull request #1016 from ethereum/file-exlorer

Fix file explorer update events
parents f9e518ac 0b394f74
...@@ -2,7 +2,7 @@ language: node_js ...@@ -2,7 +2,7 @@ language: node_js
node_js: node_js:
- "7" - "7"
script: script:
- npm run lint && npm run test && npm run downloadsolc && npm run make-mock-compiler && npm run build - npm run lint && npm run test && npm run downloadsolc && npm run make-mock-compiler && npm run setupremix && npm run build
- ./ci/browser_tests.sh - ./ci/browser_tests.sh
deploy: deploy:
- provider: script - provider: script
......
...@@ -4,14 +4,7 @@ set -e ...@@ -4,14 +4,7 @@ set -e
setupRemixd () { setupRemixd () {
mkdir remixdSharedfolder mkdir remixdSharedfolder
cd remixdSharedfolder cd contracts
echo "contract test1 { function get () returns (uint) { return 8; }}" > contract1.sol
echo "contract test2 { function get () returns (uint) { return 9; }}" > contract2.sol
mkdir folder1
cd folder1
echo "contract test1 { function get () returns (uint) { return 10; }}" > contract1.sol
echo "contract test2 { function get () returns (uint) { return 11; }}" > contract2.sol
cd ..
echo 'sharing folder: ' echo 'sharing folder: '
echo $PWD echo $PWD
node ../node_modules/remixd/src/main.js -s $PWD & node ../node_modules/remixd/src/main.js -s $PWD &
......
contract test1 { function get () returns (uint) { return 8; }}
\ No newline at end of file
contract test2 { function get () returns (uint) { return 9; }}
\ No newline at end of file
contract test1 { function get () returns (uint) { return 10; }}
\ No newline at end of file
contract test2 { function get () returns (uint) { return 11; }}
\ No newline at end of file
contract test2 { function get () returns (uint) { return 11; }}
\ No newline at end of file
contract test2 { function get () returns (uint) { return 11; }}
\ No newline at end of file
...@@ -139,7 +139,8 @@ ...@@ -139,7 +139,8 @@
] ]
}, },
"scripts": { "scripts": {
"pullremix": "mkdir remix; git clone https://github.com/ethereum/remix; cd ..", "setupremix": "npm run pullremix && npm run linkremixcore && npm run linkremixlib && npm run linkremixsolidity && npm run linkremixdebugger;",
"pullremix": "git clone https://github.com/ethereum/remix",
"linkremixcore": "cd node_modules && rm -rf remix-core && ln -s ../remix/remix-core remix-core && cd ..", "linkremixcore": "cd node_modules && rm -rf remix-core && ln -s ../remix/remix-core remix-core && cd ..",
"linkremixlib": "cd node_modules && rm -rf remix-lib && ln -s ../remix/remix-lib remix-lib && cd ..", "linkremixlib": "cd node_modules && rm -rf remix-lib && ln -s ../remix/remix-lib remix-lib && cd ..",
"linkremixsolidity": "cd node_modules && rm -rf remix-solidity && ln -s ../remix/remix-solidity remix-solidity && cd ..", "linkremixsolidity": "cd node_modules && rm -rf remix-solidity && ln -s ../remix/remix-solidity remix-solidity && cd ..",
......
...@@ -497,10 +497,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org ...@@ -497,10 +497,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
switchFile: function (path) { switchFile: function (path) {
fileManager.switchFile(path) fileManager.switchFile(path)
}, },
event: this.event, event: fileManager.event,
currentFile: function () { config: config,
return config.get('currentFile')
},
currentContent: function () { currentContent: function () {
return editor.get(config.get('currentFile')) return editor.get(config.get('currentFile'))
}, },
......
...@@ -5,7 +5,9 @@ class BasicReadOnlyExplorer { ...@@ -5,7 +5,9 @@ class BasicReadOnlyExplorer {
constructor (type) { constructor (type) {
this.event = new EventManager() this.event = new EventManager()
this.files = {} this.files = {}
this.paths = {}
this.normalizedNames = {} // contains the raw url associated with the displayed path this.normalizedNames = {} // contains the raw url associated with the displayed path
this.paths[type] = {}
this.type = type this.type = type
this.readonly = true this.readonly = true
} }
...@@ -36,19 +38,32 @@ class BasicReadOnlyExplorer { ...@@ -36,19 +38,32 @@ class BasicReadOnlyExplorer {
} }
set (path, content, cb) { set (path, content, cb) {
this.addReadOnly(path, content) var unprefixedPath = this.removePrefix(path)
this.addReadOnly(unprefixedPath, content)
if (cb) cb() if (cb) cb()
return true return true
} }
addReadOnly (path, content, rawPath) { addReadOnly (path, content, rawPath) {
var unprefixedPath = this.removePrefix(path)
try { // lazy try to format JSON try { // lazy try to format JSON
content = JSON.stringify(JSON.parse(content), null, '\t') content = JSON.stringify(JSON.parse(content), null, '\t')
} catch (e) {} } catch (e) {}
this.files[this.type + '/' + unprefixedPath] = content // splitting off the path in a tree structure, the json tree is used in `resolveDirectory`
var split = path
var folder = false
while (split.lastIndexOf('/') !== -1) {
var subitem = split.substring(split.lastIndexOf('/'))
split = split.substring(0, split.lastIndexOf('/'))
if (!this.paths[this.type + '/' + split]) {
this.paths[this.type + '/' + split] = {}
}
this.paths[this.type + '/' + split][split + subitem] = { isDirectory: folder }
folder = true
}
this.paths[this.type][split] = { isDirectory: folder }
this.files[path] = content
this.normalizedNames[rawPath] = path this.normalizedNames[rawPath] = path
this.event.trigger('fileAdded', [this.type + '/' + unprefixedPath, true]) this.event.trigger('fileAdded', [path, true])
return true return true
} }
...@@ -68,46 +83,12 @@ class BasicReadOnlyExplorer { ...@@ -68,46 +83,12 @@ class BasicReadOnlyExplorer {
return this.files return this.files
} }
// resolveDirectory (path, callback) {
// Tree model for files
// {
// 'a': { }, // empty directory 'a'
// 'b': {
// 'c': {}, // empty directory 'b/c'
// 'd': { '/readonly': true, '/content': 'Hello World' } // files 'b/c/d'
// 'e': { '/readonly': false, '/path': 'b/c/d' } // symlink to 'b/c/d'
// 'f': { '/readonly': false, '/content': '<executable>', '/mode': 0755 }
// }
// }
//
resolveDirectory (path, callback /* (error, filesList) => { } */) {
var self = this var self = this
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } }) if (!path) return callback(null, { [self.type]: { } })
var tree = {} // we just return the json tree populated by `addReadOnly`
// This does not include '.remix.config', because it is filtered callback(null, this.paths[path])
// inside list().
Object.keys(this.list()).forEach(function (path) {
hashmapize(tree, path, {
'/readonly': self.isReadOnly(path),
'/content': self.get(path)
})
})
return callback(null, tree[path] || {})
function hashmapize (obj, path, val) {
var nodes = path.split('/')
var i = 0
for (; i < nodes.length - 1; i++) {
var node = nodes[i]
if (obj[node] === undefined) {
obj[node] = {}
}
obj = obj[node]
}
obj[nodes[i]] = val
}
} }
removePrefix (path) { removePrefix (path) {
......
...@@ -103,55 +103,29 @@ function Files (storage) { ...@@ -103,55 +103,29 @@ function Files (storage) {
return false return false
} }
//
// Tree model for files
// {
// 'a': { }, // empty directory 'a'
// 'b': {
// 'c': {}, // empty directory 'b/c'
// 'd': { '/readonly': true, '/content': 'Hello World' } // files 'b/c/d'
// 'e': { '/readonly': false, '/path': 'b/c/d' } // symlink to 'b/c/d'
// 'f': { '/readonly': false, '/content': '<executable>', '/mode': 0755 }
// }
// }
//
this.resolveDirectory = function (path, callback) { this.resolveDirectory = function (path, callback) {
var self = this var self = this
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } }) if (!path) return callback(null, { [self.type]: { } })
path = self.removePrefix(path)
var filesList = {} var filesList = {}
var tree = {} var tree = {}
// add r/w filesList to the list // add r/w filesList to the list
storage.keys().forEach((path) => { storage.keys().forEach((path) => {
// NOTE: as a temporary measure do not show the config file // NOTE: as a temporary measure do not show the config file
if (path !== '.remix.config') { if (path !== '.remix.config') {
filesList[self.type + '/' + path] = false filesList[path] = false
} }
}) })
// add r/o files to the list // add r/o files to the list
Object.keys(readonly).forEach((path) => { Object.keys(readonly).forEach((path) => {
filesList[self.type + '/' + path] = true filesList[path] = true
}) })
Object.keys(filesList).forEach(function (path) { Object.keys(filesList).forEach(function (path) {
hashmapize(tree, path, { tree[path] = { isDirectory: false }
'/readonly': self.isReadOnly(path),
'/content': self.get(path)
}) })
}) return callback(null, tree)
return callback(null, tree[path] || {})
function hashmapize (obj, path, val) {
var nodes = path.split('/')
var i = 0
for (; i < nodes.length - 1; i++) {
var node = nodes[i]
if (obj[node] === undefined) {
obj[node] = {}
}
obj = obj[node]
}
obj[nodes[i]] = val
}
} }
this.removePrefix = function (path) { this.removePrefix = function (path) {
......
This diff is collapsed.
...@@ -47,6 +47,7 @@ class FileManager { ...@@ -47,6 +47,7 @@ class FileManager {
self.switchFile(Object.keys(self.tabbedFiles)[0]) self.switchFile(Object.keys(self.tabbedFiles)[0])
} else { } else {
opt.editor.displayEmptyReadOnlySession() opt.editor.displayEmptyReadOnlySession()
self.opt.config.set('currentFile', '')
} }
return false return false
}) })
...@@ -98,7 +99,6 @@ class FileManager { ...@@ -98,7 +99,6 @@ class FileManager {
// Display files that have already been selected // Display files that have already been selected
refreshTabs (newfile) { refreshTabs (newfile) {
var self = this
if (newfile) { if (newfile) {
this.tabbedFiles[newfile] = newfile this.tabbedFiles[newfile] = newfile
} }
...@@ -109,29 +109,32 @@ class FileManager { ...@@ -109,29 +109,32 @@ class FileManager {
for (var file in this.tabbedFiles) { for (var file in this.tabbedFiles) {
$filesEl.append(yo`<li class="file"><span class="name">${file}</span><span class="remove"><i class="fa fa-close"></i></span></li>`) $filesEl.append(yo`<li class="file"><span class="name">${file}</span><span class="remove"><i class="fa fa-close"></i></span></li>`)
} }
var currentFileOpen = !!this.opt.config.get('currentFile')
if (currentFileOpen) { var active = $('#files .file').filter(function () {
var active = $('#files .file').filter(function () { return $(this).find('.name').text() === self.opt.config.get('currentFile') }) return $(this).find('.name').text() === newfile
active.addClass('active') })
} if (active.length) active.addClass('active')
$('#input').toggle(currentFileOpen) else this.switchFile()
$('#output').toggle(currentFileOpen) // $('#input').toggle(active)
$('#output').toggle(active)
} }
switchFile (file) { switchFile (file) {
var self = this var self = this
if (!file) { if (file) return _switchFile(file)
self.opt.filesProviders['browser'].resolveDirectory('/', (error, filesTree) => { else {
var browserProvider = self.opt.filesProviders['browser']
browserProvider.resolveDirectory('browser', (error, filesTree) => {
if (error) console.error(error) if (error) console.error(error)
var fileList = Object.keys(flatten(filesTree)) var fileList = Object.keys(filesTree)
if (fileList.length) { if (fileList.length) {
file = fileList[0] _switchFile(browserProvider.type + '/' + fileList[0])
if (file) _switchFile(file) } else {
self.event.trigger('currentFileChanged', [])
} }
}) })
} else _switchFile(file) }
function _switchFile () { function _switchFile (file) {
self.saveCurrentFile() self.saveCurrentFile()
self.opt.config.set('currentFile', file) self.opt.config.set('currentFile', file)
self.refreshTabs(file) self.refreshTabs(file)
...@@ -179,23 +182,3 @@ class FileManager { ...@@ -179,23 +182,3 @@ class FileManager {
} }
module.exports = FileManager module.exports = FileManager
function flatten (tree) {
var flat = {}
var names = Object.keys(tree || {})
if (!names.length) return
else {
names.forEach(name => {
if ('/content' in tree[name]) flat[name] = false
else {
var subflat = flatten(tree[name])
if (!subflat) {
// empty folder
} else {
Object.keys(subflat).forEach(path => { flat[name + '/' + path] = false })
}
}
})
return flat
}
}
...@@ -137,19 +137,6 @@ module.exports = class SharedFolder { ...@@ -137,19 +137,6 @@ module.exports = class SharedFolder {
return true return true
} }
//
// Tree model for files
// {
// 'a': { }, // empty directory 'a'
// 'b': {
// 'c': {}, // empty directory 'b/c'
// 'd': { '/readonly': true, '/content': 'Hello World' } // files 'b/c/d'
// 'e': { '/readonly': false, '/path': 'b/c/d' } // symlink to 'b/c/d'
// 'f': { '/readonly': false, '/content': '<executable>', '/mode': 0755 }
// }
// }
//
removePrefix (path) { removePrefix (path) {
path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path
if (path[0] === '/') return path.substring(1) if (path[0] === '/') return path.substring(1)
...@@ -160,7 +147,7 @@ module.exports = class SharedFolder { ...@@ -160,7 +147,7 @@ module.exports = class SharedFolder {
var self = this var self = this
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } }) if (!path) return callback(null, { [self.type]: { } })
path = self.removePrefix('' + (path || '')) path = self.removePrefix(path)
self.remixd.dir(path, callback) self.remixd.dir(path, callback)
} }
} }
......
/* global FileReader */
var async = require('async') var async = require('async')
var $ = require('jquery') var $ = require('jquery')
var yo = require('yo-yo') var yo = require('yo-yo')
...@@ -73,7 +74,7 @@ function filepanel (appAPI, filesProvider) { ...@@ -73,7 +74,7 @@ function filepanel (appAPI, filesProvider) {
</div> </div>
<div class=${css.treeviews}> <div class=${css.treeviews}>
<div class=${css.treeview}>${fileExplorer.init()}</div> <div class=${css.treeview}>${fileExplorer.init()}</div>
<div class="filesystemexplorer ${css.treeview}"></div> <div class="filesystemexplorer ${css.treeview}">${fileSystemExplorer.init()}</div>
<div class="swarmexplorer ${css.treeview}">${swarmExplorer.init()}</div> <div class="swarmexplorer ${css.treeview}">${swarmExplorer.init()}</div>
<div class="githubexplorer ${css.treeview}">${githubExplorer.init()}</div> <div class="githubexplorer ${css.treeview}">${githubExplorer.init()}</div>
<div class="gistexplorer ${css.treeview}">${gistExplorer.init()}</div> <div class="gistexplorer ${css.treeview}">${gistExplorer.init()}</div>
...@@ -87,7 +88,7 @@ function filepanel (appAPI, filesProvider) { ...@@ -87,7 +88,7 @@ function filepanel (appAPI, filesProvider) {
var event = new EventManager() var event = new EventManager()
self.event = event self.event = event
var element = template() var element = template()
fileExplorer.ensureRoot()
var containerFileSystem = element.querySelector('.filesystemexplorer') var containerFileSystem = element.querySelector('.filesystemexplorer')
var websocketconn = element.querySelector('.websocketconn') var websocketconn = element.querySelector('.websocketconn')
filesProvider['localhost'].remixd.event.register('connecting', (event) => { filesProvider['localhost'].remixd.event.register('connecting', (event) => {
...@@ -143,7 +144,30 @@ function filepanel (appAPI, filesProvider) { ...@@ -143,7 +144,30 @@ function filepanel (appAPI, filesProvider) {
// the files module. Please ask the user here if they want to overwrite // the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will // a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module. // pick that up via the 'fileAdded' event from the files module.
;[...this.files].forEach(fileExplorer.api.addFile)
;[...this.files].forEach((file) => {
var files = fileExplorer.files
function loadFile () {
var fileReader = new FileReader()
fileReader.onload = function (event) {
if (helper.checkSpecialChars(file.name)) {
modalDialogCustom.alert('Special characters are not allowed')
return
}
var success = files.set(name, event.target.result)
if (!success) modalDialogCustom.alert('Failed to create file ' + name)
else self.events.trigger('focus', [name])
}
fileReader.readAsText(file)
}
var name = files.type + '/' + file.name
if (!files.exists(name)) {
loadFile()
} else {
modalDialogCustom.confirm(null, `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
}
})
} }
// ----------------- resizeable ui --------------- // ----------------- resizeable ui ---------------
...@@ -200,7 +224,6 @@ function filepanel (appAPI, filesProvider) { ...@@ -200,7 +224,6 @@ function filepanel (appAPI, filesProvider) {
* @param {String} txHash - hash of the transaction * @param {String} txHash - hash of the transaction
*/ */
function connectToLocalhost () { function connectToLocalhost () {
var container = document.querySelector('.filesystemexplorer')
if (filesProvider['localhost'].isConnected()) { if (filesProvider['localhost'].isConnected()) {
filesProvider['localhost'].close((error) => { filesProvider['localhost'].close((error) => {
if (error) console.log(error) if (error) console.log(error)
...@@ -213,10 +236,7 @@ function filepanel (appAPI, filesProvider) { ...@@ -213,10 +236,7 @@ function filepanel (appAPI, filesProvider) {
if (error) { if (error) {
console.log(error) console.log(error)
} else { } else {
if (fileSystemExplorer.element && container.children.length > 0) { fileSystemExplorer.ensureRoot()
container.removeChild(fileSystemExplorer.element)
}
container.appendChild(fileSystemExplorer.init())
} }
}) })
}}) }})
......
...@@ -77,7 +77,7 @@ var css = csjs` ...@@ -77,7 +77,7 @@ var css = csjs`
} }
.dragbar { .dragbar {
position : absolute; position : absolute;
top : 37px; top : 29px;
width : 0.5em; width : 0.5em;
right : 0; right : 0;
bottom : 0; bottom : 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment