Rewrite file handling logic using the new files API

pull/1/head
Alex Beregszaszi 8 years ago
parent d55a6dc43b
commit ddf06bd244
  1. 133
      src/app.js
  2. 159
      src/app/editor.js

@ -1,4 +1,4 @@
/* global alert, confirm, prompt, Option, Worker, chrome */ /* global alert, confirm, prompt, FileReader, Option, Worker, chrome */
'use strict' 'use strict'
var $ = require('jquery') var $ = require('jquery')
@ -10,6 +10,7 @@ var GistHandler = require('./app/gist-handler')
var gistHandler = new GistHandler() var gistHandler = new GistHandler()
var Storage = require('./app/storage') var Storage = require('./app/storage')
var Files = require('./app/files')
var Config = require('./app/config') var Config = require('./app/config')
var Editor = require('./app/editor') var Editor = require('./app/editor')
var Renderer = require('./app/renderer') var Renderer = require('./app/renderer')
@ -22,6 +23,8 @@ var EventManager = require('./lib/eventManager')
var StaticAnalysis = require('./app/staticanalysis/staticAnalysisView') var StaticAnalysis = require('./app/staticanalysis/staticAnalysisView')
var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter')
var examples = require('./app/example-contracts')
// The event listener needs to be registered as early as possible, because the // The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event. // parent will send the message upon the "load" event.
var filesToLoad = null var filesToLoad = null
@ -40,16 +43,28 @@ var run = function () {
var self = this var self = this
this.event = new EventManager() this.event = new EventManager()
var storage = new Storage() var storage = new Storage()
var files = new Files(storage)
var config = new Config(storage) var config = new Config(storage)
var currentFile
function packageFiles () {
return files.list().map(function (path) { return { content: files.get(path) } })
}
function createNonClashingName (path) {
var counter = ''
while (files.exists(path + counter)) {
counter = (counter | 0) + 1
}
return path + counter
}
// Add files received from remote instance (i.e. another browser-solidity) // Add files received from remote instance (i.e. another browser-solidity)
function loadFiles (files) { function loadFiles (files) {
for (var f in files) { for (var f in files) {
storage.loadFile(f, files[f].content) files.set(createNonClashingName(f), files[f].content)
} }
// Set the first file as current tab switchToNextFile()
editor.setCacheFile(Object.keys(files)[0])
updateFiles()
} }
// Replace early callback with instant response // Replace early callback with instant response
@ -87,6 +102,11 @@ var run = function () {
}) })
}) })
// insert ballot contract if there are no files available
if (!loadingFromGist && (files.list().length === 0)) {
files.set(examples.ballot.name, examples.ballot.content)
}
// ----------------- Chrome cloud storage sync -------------------- // ----------------- Chrome cloud storage sync --------------------
function chromeCloudSync () { function chromeCloudSync () {
@ -131,7 +151,7 @@ var run = function () {
// ----------------- editor ---------------------- // ----------------- editor ----------------------
var editor = new Editor(loadingFromGist, storage) var editor = new Editor()
// ----------------- tabbed menu ------------------- // ----------------- tabbed menu -------------------
$('#options li').click(function (ev) { $('#options li').click(function (ev) {
@ -155,7 +175,7 @@ var run = function () {
$('#gist').click(function () { $('#gist').click(function () {
if (confirm('Are you sure you want to publish all your files anonymously as a public gist on github.com?')) { if (confirm('Are you sure you want to publish all your files anonymously as a public gist on github.com?')) {
var files = editor.packageFiles() var files = packageFiles()
var description = 'Created using browser-solidity: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://ethereum.github.io/browser-solidity/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist=' var description = 'Created using browser-solidity: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://ethereum.github.io/browser-solidity/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist='
$.ajax({ $.ajax({
@ -182,7 +202,7 @@ var run = function () {
if (target === null) { if (target === null) {
return return
} }
var files = editor.packageFiles() var files = packageFiles()
$('<iframe/>', { $('<iframe/>', {
src: target, src: target,
style: 'display:none;', style: 'display:none;',
@ -196,12 +216,9 @@ var run = function () {
var FILE_SCROLL_DELTA = 300 var FILE_SCROLL_DELTA = 300
$('.newFile').on('click', function () { $('.newFile').on('click', function () {
editor.newFile() var newName = createNonClashingName('Untitled')
updateFiles() files.set(newName, '')
switchToFile(newName)
$filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () {
reAdjust()
})
}) })
// ----------------- file upload ------------- // ----------------- file upload -------------
@ -210,14 +227,15 @@ var run = function () {
var fileList = $('input.inputFile')[0].files var fileList = $('input.inputFile')[0].files
for (var i = 0; i < fileList.length; i++) { for (var i = 0; i < fileList.length; i++) {
var name = fileList[i].name var name = fileList[i].name
if (!editor.hasFile(name) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) { if (!files.exists(name) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) {
editor.uploadFile(fileList[i], updateFiles) var fileReader = new FileReader()
fileReader.onload = function (ev) {
files.set(name, ev.target.result)
switchToFile(name)
}
fileReader.readAsText(fileList[i])
} }
} }
$filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () {
reAdjust()
})
}) })
// Switch tab // Switch tab
@ -248,15 +266,14 @@ var run = function () {
$fileNameInputEl.off('keyup') $fileNameInputEl.off('keyup')
if (newName !== originalName && confirm( if (newName !== originalName && confirm(
editor.hasFile(newName) files.exists(newName)
? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?' ? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?'
: 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) { : 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) {
storage.rename(originalName, newName) files.rename(originalName, newName)
editor.renameSession(originalName, newName) switchToFile(newName)
editor.setCacheFile(newName) editor.discard(originalName)
} }
updateFiles()
return false return false
} }
@ -269,10 +286,9 @@ var run = function () {
var name = $(this).parent().find('.name').text() var name = $(this).parent().find('.name').text()
if (confirm('Are you sure you want to remove: ' + name + ' from local storage?')) { if (confirm('Are you sure you want to remove: ' + name + ' from local storage?')) {
storage.remove(name) files.remove(name)
editor.removeSession(name) switchToNextFile()
editor.setNextFile(name) editor.discard(name)
updateFiles()
} }
return false return false
}) })
@ -280,32 +296,46 @@ var run = function () {
editor.event.register('sessionSwitched', updateFiles) editor.event.register('sessionSwitched', updateFiles)
function switchToFile (file) { function switchToFile (file) {
editor.setCacheFile(file) currentFile = file
updateFiles()
if (files.isReadOnly(file)) {
editor.openReadOnly(file, files.get(file))
} else {
editor.open(file, files.get(file))
}
}
function switchToNextFile () {
switchToFile(Object.keys(files.list())[0])
} }
switchToNextFile()
// Synchronise tab list with file names known to the editor // Synchronise tab list with file names known to the editor
function updateFiles () { function updateFiles () {
var $filesEl = $('#files') var $filesEl = $('#files')
var files = editor.getFiles() var fileNames = Object.keys(files.list())
$filesEl.find('.file').remove() $filesEl.find('.file').remove()
$('#output').empty() $('#output').empty()
for (var f in files) { for (var f in fileNames) {
var name = files[f] var name = fileNames[f]
$filesEl.append($('<li class="file"><span class="name">' + name + '</span><span class="remove"><i class="fa fa-close"></i></span></li>')) $filesEl.append($('<li class="file"><span class="name">' + name + '</span><span class="remove"><i class="fa fa-close"></i></span></li>'))
} }
if (editor.cacheFileIsPresent()) { var currentFileOpen = !!currentFile
var currentFileName = editor.getCacheFile()
var active = $('#files .file').filter(function () { return $(this).find('.name').text() === currentFileName }) if (currentFileOpen) {
var active = $('#files .file').filter(function () { return $(this).find('.name').text() === currentFile })
active.addClass('active') active.addClass('active')
editor.resetSession()
} }
$('#input').toggle(editor.cacheFileIsPresent()) $('#input').toggle(currentFileOpen)
$('#output').toggle(editor.cacheFileIsPresent()) $('#output').toggle(currentFileOpen)
$filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () {
reAdjust() reAdjust()
})
} }
var $filesWrapper = $('.files-wrapper') var $filesWrapper = $('.files-wrapper')
@ -368,8 +398,6 @@ var run = function () {
}) })
}) })
updateFiles()
// ----------------- resizeable ui --------------- // ----------------- resizeable ui ---------------
var EDITOR_WINDOW_SIZE = 'editorWindowSize' var EDITOR_WINDOW_SIZE = 'editorWindowSize'
@ -470,8 +498,8 @@ var run = function () {
function handleImportCall (url, cb) { function handleImportCall (url, cb) {
var githubMatch var githubMatch
if (editor.hasFile(url)) { if (files.exists(url)) {
cb(null, editor.getFile(url)) cb(null, files.get(url))
} else if (url in cachedRemoteFiles) { } else if (url in cachedRemoteFiles) {
cb(null, cachedRemoteFiles[url]) cb(null, cachedRemoteFiles[url])
} else if ((githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^/]*\/[^/]*)\/(.*)/.exec(url))) { } else if ((githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^/]*\/[^/]*)\/(.*)/.exec(url))) {
@ -506,9 +534,8 @@ var run = function () {
this.statementMarker = null this.statementMarker = null
this.fullLineMarker = null this.fullLineMarker = null
if (lineColumnPos) { if (lineColumnPos) {
var name = editor.getCacheFile() // current opened tab
var source = compiler.lastCompilationResult.data.sourceList[location.file] // auto switch to that tab var source = compiler.lastCompilationResult.data.sourceList[location.file] // auto switch to that tab
if (name !== source) { if (currentFile !== source) {
switchToFile(source) switchToFile(source)
} }
this.statementMarker = editor.addMarker(lineColumnPos, 'highlightcode') this.statementMarker = editor.addMarker(lineColumnPos, 'highlightcode')
@ -569,12 +596,12 @@ var run = function () {
var rendererAPI = { var rendererAPI = {
error: (file, error) => { error: (file, error) => {
if (file === editor.getCacheFile()) { if (file === currentFile) {
editor.addAnnotation(error) editor.addAnnotation(error)
} }
}, },
errorClick: (errFile, errLine, errCol) => { errorClick: (errFile, errLine, errCol) => {
if (errFile !== editor.getCacheFile() && editor.hasFile(errFile)) { if (errFile !== currentFile && files.exists(errFile)) {
switchToFile(errFile) switchToFile(errFile)
} }
editor.gotoLine(errLine, errCol) editor.gotoLine(errLine, errCol)
@ -623,9 +650,9 @@ var run = function () {
function runCompiler () { function runCompiler () {
var files = {} var files = {}
var target = editor.getCacheFile() var target = currentFile
files[target] = editor.getValue() files[target] = editor.get(currentFile)
compiler.compile(files, target) compiler.compile(files, target)
} }
@ -635,7 +662,7 @@ var run = function () {
var saveTimeout = null var saveTimeout = null
function editorOnChange () { function editorOnChange () {
var input = editor.getValue() var input = editor.get(currentFile)
// if there's no change, don't do anything // if there's no change, don't do anything
if (input === previousInput) { if (input === previousInput) {
@ -649,8 +676,8 @@ var run = function () {
window.clearTimeout(saveTimeout) window.clearTimeout(saveTimeout)
} }
saveTimeout = window.setTimeout(function () { saveTimeout = window.setTimeout(function () {
var input = editor.getValue() var input = editor.get(currentFile)
editor.setCacheFileContent(input) files.set(currentFile, input)
}, 5000) }, 5000)
// special case: there's nothing else to do // special case: there's nothing else to do

@ -1,122 +1,73 @@
/* global FileReader */
'use strict' 'use strict'
var EventManager = require('../lib/eventManager') var EventManager = require('../lib/eventManager')
var examples = require('./example-contracts')
var ace = require('brace') var ace = require('brace')
var Range = ace.acequire('ace/range').Range var Range = ace.acequire('ace/range').Range
require('../mode-solidity.js') require('../mode-solidity.js')
function Editor (doNotLoadStorage, storage) { function Editor () {
var SOL_CACHE_FILE = null
var editor = ace.edit('input') var editor = ace.edit('input')
document.getElementById('input').editor = editor // required to access the editor during tests document.getElementById('input').editor = editor // required to access the editor during tests
var event = new EventManager() var event = new EventManager()
this.event = event this.event = event
var sessions = {} var sessions = {}
var sourceAnnotations = [] var sourceAnnotations = []
var readOnlySessions = {}
var currentSession
this.addMarker = function (lineColumnPos, cssClass) { var emptySession = createSession('')
var currentRange = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column)
return editor.session.addMarker(currentRange, cssClass)
}
this.removeMarker = function (markerId) { function createSession (content) {
editor.session.removeMarker(markerId) var s = new ace.EditSession(content, 'ace/mode/javascript')
} s.setUndoManager(new ace.UndoManager())
s.setTabSize(4)
this.newFile = function () { s.setUseSoftTabs(true)
var untitledCount = '' return s
while (storage.exists('Untitled' + untitledCount)) {
untitledCount = (untitledCount - 0) + 1
}
this.setCacheFile('Untitled' + untitledCount)
this.setCacheFileContent('')
}
this.uploadFile = function (file, callback) {
var fileReader = new FileReader()
var name = file.name
var self = this
fileReader.onload = function (e) {
self.setCacheFile(name)
self.setCacheFileContent(e.target.result)
callback()
}
fileReader.readAsText(file)
}
this.setCacheFileContent = function (content) {
storage.set(SOL_CACHE_FILE, content)
}
this.setCacheFile = function (cacheFile) {
SOL_CACHE_FILE = cacheFile
}
this.getCacheFile = function () {
return SOL_CACHE_FILE
}
this.cacheFileIsPresent = function () {
return !!SOL_CACHE_FILE
}
this.setNextFile = function (fileKey) {
var index = this.getFiles().indexOf(fileKey)
this.setCacheFile(this.getFiles()[ Math.max(0, index - 1) ])
} }
this.resetSession = function () { function switchSession (path) {
editor.setSession(sessions[this.getCacheFile()]) currentSession = path
editor.setSession(sessions[currentSession])
editor.setReadOnly(readOnlySessions[currentSession])
editor.focus() editor.focus()
} }
this.removeSession = function (fileKey) { this.open = function (path, content) {
delete sessions[fileKey] if (!sessions[path]) {
var session = createSession(content)
sessions[path] = session
readOnlySessions[path] = false
}
switchSession(path)
} }
this.renameSession = function (oldFileKey, newFileKey) { this.openReadOnly = function (path, content) {
if (oldFileKey !== newFileKey) { if (!sessions[path]) {
sessions[newFileKey] = sessions[oldFileKey] var session = createSession(content)
this.removeSession(oldFileKey) sessions[path] = session
readOnlySessions[path] = true
} }
switchSession(path)
} }
this.hasFile = function (name) { this.get = function (path) {
return this.getFiles().indexOf(name) !== -1 if (currentSession === path) {
return editor.getValue()
} }
this.getFile = function (name) {
return storage.get(name)
} }
function getFiles () { this.current = function (path) {
var files = [] if (editor.getSession() === emptySession) {
storage.keys().forEach(function (f) { return
// NOTE: as a temporary measure do not show the config file in the editor
if (f !== '.browser-solidity.json') {
files.push(f)
if (!sessions[f]) sessions[f] = newEditorSession(f)
} }
}) return currentSession
return files
} }
this.getFiles = getFiles
this.packageFiles = function () { this.discard = function (path) {
var files = {} if (currentSession !== path) {
var filesArr = this.getFiles() delete sessions[path]
for (var f in filesArr) {
files[filesArr[f]] = {
content: storage.get(filesArr[f])
}
} }
return files
} }
this.resize = function () { this.resize = function () {
@ -133,8 +84,13 @@ function Editor (doNotLoadStorage, storage) {
} }
} }
this.getValue = function () { this.addMarker = function (lineColumnPos, cssClass) {
return editor.getValue() var currentRange = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column)
return editor.session.addMarker(currentRange, cssClass)
}
this.removeMarker = function (markerId) {
editor.session.removeMarker(markerId)
} }
this.clearAnnotations = function () { this.clearAnnotations = function () {
@ -156,15 +112,6 @@ function Editor (doNotLoadStorage, storage) {
editor.gotoLine(line + 1, col - 1, true) editor.gotoLine(line + 1, col - 1, true)
} }
function newEditorSession (filekey) {
var s = new ace.EditSession(storage.get(filekey), 'ace/mode/javascript')
s.setUndoManager(new ace.UndoManager())
s.setTabSize(4)
s.setUseSoftTabs(true)
sessions[filekey] = s
return s
}
// Do setup on initialisation here // Do setup on initialisation here
editor.on('changeSession', function () { editor.on('changeSession', function () {
event.trigger('sessionSwitched', []) event.trigger('sessionSwitched', [])
@ -178,24 +125,6 @@ function Editor (doNotLoadStorage, storage) {
editor.commands.bindKeys({ 'ctrl-t': null }) editor.commands.bindKeys({ 'ctrl-t': null })
editor.commands.bindKeys({ 'ctrl-f': null }) editor.commands.bindKeys({ 'ctrl-f': null })
if (doNotLoadStorage) {
return
}
var files = getFiles()
if (files.length === 0) {
files.push(examples.ballot.name)
storage.set(examples.ballot.name, examples.ballot.content)
}
this.setCacheFile(files[0])
for (var x in files) {
sessions[files[x]] = newEditorSession(files[x])
}
editor.setSession(sessions[this.getCacheFile()])
editor.resize(true) editor.resize(true)
} }

Loading…
Cancel
Save