Skip to content

Commit 3970caa

Browse files
committed
Adding option for file watching - based on #254
1 parent 336b8bf commit 3970caa

File tree

1 file changed

+140
-24
lines changed

1 file changed

+140
-24
lines changed

js/core/file.js

+140-24
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,40 @@
1212
"use strict";
1313
(function(){
1414

15-
var currentJSFileName = "code.js";
16-
var currentXMLFileName = "code_blocks.xml";
17-
var loadFileCallback;
15+
// how many millisecs between checking a file for modification?
16+
const WATCH_INTERVAL = 1000;
17+
// types of file we accept
18+
const MIMETYPE_JS = {"application/javascript": [".js", ".js"], "text/plain": [".txt"]};
19+
const MIMETYPE_XML = {"text/xml": [".xml"]};
1820

19-
const MIMETYPE_JS = ".js,.txt,application/javascript,text/plain";
20-
const MIMETYPE_XML = ".xml,text/xml";
21+
var currentJSFile = {
22+
name:"code.js",
23+
mimeTypes : MIMETYPE_JS,
24+
// also setValue, getValue, handle, lastModified
25+
};
26+
var currentXMLFile = {
27+
name:"code_blocks.xml",
28+
mimeTypes : MIMETYPE_XML,
29+
};
30+
var currentFiles = [currentJSFile, currentXMLFile];
31+
32+
var iconOpenFile;
33+
var iconSaveFile;
34+
// interval used when checking files for modification
35+
var watchInterval;
36+
// What we should do when clicking the 'openFile' button
37+
var openFileMode = "open"; // open, reload, watch, upload
2138

22-
function init() {
23-
// Configuration
2439

2540

26-
// Add stuff we need
27-
Espruino.Core.App.addIcon({
41+
function init() {
42+
currentJSFile.setValue = Espruino.Core.EditorJavaScript.setCode;
43+
currentJSFile.getValue = Espruino.Core.EditorJavaScript.getCode;
44+
currentXMLFile.setValue = Espruino.Core.EditorBlockly.setXML;
45+
currentXMLFile.getValue = Espruino.Core.EditorBlockly.getXML;
46+
47+
// Open file icon
48+
var icon = {
2849
id: "openFile",
2950
icon: "folder-open",
3051
title : "Open File",
@@ -35,13 +56,39 @@
3556
},
3657
click: function() {
3758
if (Espruino.Core.Code.isInBlockly())
38-
loadFile(Espruino.Core.EditorBlockly.setXML, currentXMLFileName, MIMETYPE_XML);
59+
loadFile(currentXMLFile);
3960
else
40-
loadFile(Espruino.Core.EditorJavaScript.setCode, currentJSFileName, MIMETYPE_JS);
61+
loadFile(currentJSFile);
4162
}
42-
});
43-
44-
Espruino.Core.App.addIcon({
63+
};
64+
// Only add extra options if showOpenFilePicker is available
65+
if (typeof window.showOpenFilePicker === 'function') {
66+
icon.more = function() {
67+
var popup = Espruino.Core.App.openPopup({
68+
id: "fileopenmodeselector",
69+
title: "Open File should...",
70+
contents: Espruino.Core.HTML.domList([{
71+
icon : "icon-folder-open", title : "Open File",
72+
description : "Load file from disk", callback : function() { setOpenFileMode("open");popup.close(); }
73+
},{
74+
icon : "icon-folder-open", title : "Reload File",
75+
description : "Reload the current file", callback : function() { setOpenFileMode("reload");popup.close(); }
76+
},{
77+
icon : "icon-refresh", title : "Watch File",
78+
description : "Watch for changes", callback : function() { setOpenFileMode("watch");popup.close(); }
79+
},{
80+
icon : "icon-refresh", title : "Watch and Upload",
81+
description : "Watch for changes and upload", callback : function() { setOpenFileMode("upload");popup.close(); }
82+
}
83+
]),
84+
position: "center",
85+
});
86+
};
87+
icon.info = "open";
88+
}
89+
iconOpenFile = Espruino.Core.App.addIcon(icon);
90+
// Save file icon
91+
iconSaveFile = Espruino.Core.App.addIcon({
4592
id: "saveFile",
4693
icon: "save",
4794
title : "Save File",
@@ -52,18 +99,31 @@
5299
},
53100
click: function() {
54101
if (Espruino.Core.Code.isInBlockly())
55-
Espruino.Core.Utils.fileSaveDialog(convertToOS(Espruino.Core.EditorBlockly.getXML()), currentXMLFileName, setCurrentFileName);
102+
saveFile(currentXMLFile);
56103
else
57-
Espruino.Core.Utils.fileSaveDialog(convertToOS(Espruino.Core.EditorJavaScript.getCode()), currentJSFileName, setCurrentFileName);
104+
saveFile(currentJSFile);
58105
}
59106
});
60107
}
61108

109+
function setOpenFileMode(mode) {
110+
iconOpenFile.setInfo(mode);
111+
openFileMode = mode;
112+
if (watchInterval) clearInterval(watchInterval);
113+
watchInterval = undefined;
114+
// if we're in
115+
if (openFileMode == "watch" || openFileMode == "upload") {
116+
watchInterval = setInterval(function() {
117+
currentFiles.forEach(readFileContents);
118+
}, WATCH_INTERVAL);
119+
}
120+
}
121+
62122
function setCurrentFileName(filename) {
63123
if (Espruino.Core.Code.isInBlockly()) {
64-
currentXMLFileName = filename;
124+
currentXMLFile.name = filename;
65125
} else {
66-
currentJSFileName = filename;
126+
currentJSFile.name = filename;
67127
}
68128
}
69129

@@ -79,16 +139,35 @@
79139
return chars.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n");
80140
};
81141

82-
function loadFile(callback, filename, mimeType) {
83-
if (chrome.fileSystem) {
142+
function loadFile(currentFile) {
143+
currentFile.lastModified = undefined;
144+
/* if clicking the button should just reload the existing file,
145+
do that */
146+
if (openFileMode == "reload" && currentFile.handle) {
147+
readFileContents(currentFile);
148+
return;
149+
}
150+
currentFile.handle = undefined;
151+
152+
if (typeof window.showOpenFilePicker === 'function') {
153+
window.showOpenFilePicker({types: [{accept: currentFile.mimeTypes}]}).
154+
then(function(fileHandles) {
155+
var fileHandle = fileHandles[0];
156+
if (fileHandle.name) {
157+
setCurrentFileName(fileHandle.name);
158+
}
159+
currentFile.handle = fileHandle;
160+
readFileContents(currentFile);
161+
});
162+
} else if (chrome.fileSystem) {
84163
// Chrome Web App / NW.js
85-
chrome.fileSystem.chooseEntry({type: 'openFile', suggestedName:filename}, function(fileEntry) {
164+
chrome.fileSystem.chooseEntry({type: 'openFile', suggestedName:currentFile.name}, function(fileEntry) {
86165
if (!fileEntry) return;
87166
if (fileEntry.name) setCurrentFileName(fileEntry.name);
88167
fileEntry.file(function(file) {
89168
var reader = new FileReader();
90169
reader.onload = function(e) {
91-
callback(convertFromOS(e.target.result));
170+
currentFile.setValue(convertFromOS(e.target.result));
92171
};
93172
reader.onerror = function() {
94173
Espruino.Core.Notifications.error("Error Loading", true);
@@ -97,14 +176,51 @@
97176
});
98177
});
99178
} else {
100-
Espruino.Core.Utils.fileOpenDialog({id:"code",type:"text",mimeType:mimeType}, function(data, mimeType, fileName) {
179+
var mimeTypeList = Object.values(currentFile.mimeTypes) + "," + Object.keys(currentFile.mimeTypes);
180+
Espruino.Core.Utils.fileOpenDialog({id:"code",type:"text",mimeType:mimeTypeList}, function(data, mimeType, fileName) {
101181
if (fileName) setCurrentFileName(fileName);
102-
callback(convertFromOS(data));
182+
currentFile.setValue(convertFromOS(data));
103183
});
104184
}
105185
}
106186

107187

188+
// read a file from window.showOpenFilePicker
189+
function readFileContents(currentFile) {
190+
if (!currentFile.handle) {
191+
return;
192+
}
193+
194+
var file;
195+
currentFile.handle.getFile().then(function(f) {
196+
file = f;
197+
// if file is newer, proceed to load it
198+
if (!currentFile.lastModified ||
199+
file.lastModified > currentFile.lastModified)
200+
return file.text();
201+
else
202+
return undefined;
203+
}).then(function(contents) {
204+
if (!contents) return;
205+
// if loaded, update editor
206+
currentFile.lastModified = file.lastModified;
207+
currentFile.setValue(convertFromOS(contents));
208+
if (openFileMode == "upload") {
209+
Espruino.Core.Notifications.info(new Date().toLocaleTimeString() + ": " + currentFile.name+" changed, uploading...");
210+
Espruino.Plugins.KeyShortcuts.action("icon-deploy");
211+
}
212+
});
213+
}
214+
215+
function saveFile(currentFile) {
216+
/* TODO: if currentFile.handle, we could write direct to this
217+
file without the dialog. But then what do we do for 'save as'? The down-arrow
218+
next to the icon? */
219+
Espruino.Core.Utils.fileSaveDialog(convertToOS(currentFile.getValue()), currentFile.name, function(name) {
220+
currentFile.name = name;
221+
});
222+
}
223+
108224
Espruino.Core.File = {
109225
init : init
110226
};

0 commit comments

Comments
 (0)