move some API from CodePad to system API

This commit is contained in:
lxsang 2021-04-14 15:17:13 +02:00
parent b2e2cbeda5
commit e657c98688
9 changed files with 685 additions and 665 deletions

Binary file not shown.

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
//along with this program. If not, see https://www.gnu.org/licenses/. //along with this program. If not, see https://www.gnu.org/licenses/.
type VFSFileHandleClass = { new (...args: any[]): OS.API.VFS.BaseFileHandle }; type VFSFileHandleClass = { new(...args: any[]): OS.API.VFS.BaseFileHandle };
interface String { interface String {
/** /**
* Convert a string to VFS file handle. * Convert a string to VFS file handle.
@ -164,6 +164,8 @@ namespace OS {
* AntOS Virtual File System (VFS) * AntOS Virtual File System (VFS)
*/ */
export namespace VFS { export namespace VFS {
declare var JSZip: any;
String.prototype.asFileHandle = function (): BaseFileHandle { String.prototype.asFileHandle = function (): BaseFileHandle {
const list = this.split("://"); const list = this.split("://");
const handles = API.VFS.findHandles(list[0]); const handles = API.VFS.findHandles(list[0]);
@ -1298,7 +1300,7 @@ namespace OS {
register("^(home|desktop|os|Untitled)$", RemoteFileHandle); register("^(home|desktop|os|Untitled)$", RemoteFileHandle);
/** /**
* Package file is remote file ([[RemoteFileHandle]]) located either in * Package file is remote file ([[RemoteFileHandle]]) located either in
* the local user packages location or system packages * the local user packages location or system packages
@ -1316,8 +1318,7 @@ namespace OS {
* @class PackageFileHandle * @class PackageFileHandle
* @extends {RemoteFileHandle} * @extends {RemoteFileHandle}
*/ */
export class PackageFileHandle extends RemoteFileHandle export class PackageFileHandle extends RemoteFileHandle {
{
/** /**
*Creates an instance of PackageFileHandle. *Creates an instance of PackageFileHandle.
@ -1325,12 +1326,11 @@ namespace OS {
* @memberof PackageFileHandle * @memberof PackageFileHandle
*/ */
constructor(pkg_path: string) { constructor(pkg_path: string) {
var error:FormattedString|string; var error: FormattedString | string;
var pkg_name: string; var pkg_name: string;
super(pkg_path); super(pkg_path);
// now find the correct path // now find the correct path
if(!this.genealogy || this.genealogy.length == 0) if (!this.genealogy || this.genealogy.length == 0) {
{
error = __("Invalid package path"); error = __("Invalid package path");
announcer.oserror(error, API.throwe(error)); announcer.oserror(error, API.throwe(error));
throw new Error(error.__()); throw new Error(error.__());
@ -1338,14 +1338,12 @@ namespace OS {
else { else {
// get the correct path of the package // get the correct path of the package
pkg_name = this.genealogy.shift(); pkg_name = this.genealogy.shift();
if(OS.setting.system.packages[pkg_name]) if (OS.setting.system.packages[pkg_name]) {
{
this.setPath(OS.setting.system.packages[pkg_name].path + "/" + this.genealogy.join("/")); this.setPath(OS.setting.system.packages[pkg_name].path + "/" + this.genealogy.join("/"));
} }
else else {
{
error = __("Package not found {0}", pkg_name); error = __("Package not found {0}", pkg_name);
announcer.oserror(error,API.throwe(error)); announcer.oserror(error, API.throwe(error));
throw new Error(error.__()); throw new Error(error.__());
} }
} }
@ -1427,8 +1425,7 @@ namespace OS {
const result = []; const result = [];
for (let k in OS.setting.system.packages) { for (let k in OS.setting.system.packages) {
const v = OS.setting.system.packages[k]; const v = OS.setting.system.packages[k];
if(v.app) if (v.app) {
{
result.push(v); result.push(v);
} }
} }
@ -1599,7 +1596,7 @@ namespace OS {
//read the file //read the file
if (t === "binary") { if (t === "binary") {
//return API.handle.fileblob(this.path); //return API.handle.fileblob(this.path);
return API.blob(this.path+ "?_=" + new Date().getTime()); return API.blob(this.path + "?_=" + new Date().getTime());
} }
return API.get(this.path, t ? t : "text"); return API.get(this.path, t ? t : "text");
} }
@ -1760,6 +1757,399 @@ namespace OS {
} }
API.VFS.register("^shared$", SharedFileHandle); API.VFS.register("^shared$", SharedFileHandle);
/**Utilities global functions */
/**
* Read a file content from a zip archive
*
* The content type should be:
* - base64 : the result will be a string, the binary in a base64 form.
* - text (or string): the result will be an unicode string.
* - binarystring: the result will be a string in binary form, using 1 byte per char (2 bytes).
* - array: the result will be an Array of bytes (numbers between 0 and 255).
* - uint8array : the result will be a Uint8Array. This requires a compatible browser.
* - arraybuffer : the result will be a ArrayBuffer. This requires a compatible browser.
* - blob : the result will be a Blob. This requires a compatible browser.
* - nodebuffer : the result will be a nodejs Buffer. This requires nodejs.
*
* If file_name is not specified, the first file_name in the zip archive will be read
* @export
* @param {string} file zip file
* @param {string} type content type to read
* @param {string} [file_name] the file should be read from the zip archive
* @return {*} {Promise<any>}
*/
export function readFileFromZip(file: string, type: string, file_name?: string): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
await API.requires("os://scripts/jszip.min.js");
try {
const data = await file.asFileHandle().read("binary");
try {
const zip = await JSZip.loadAsync(data);
if (!file_name) {
for (let name in zip.files) {
file_name = name;
break;
}
}
try {
const udata = await zip.file(file_name).async(type);
resolve(udata);
} catch (e_2) {
return reject(__e(e_2));
}
} catch (e_1) {
return reject(__e(e_1));
}
} catch (e) {
return reject(__e(e));
}
} catch (e_3) {
return reject(__e(e_3));
}
});
}
/**
* Cat al file to a single out-put
*
* @export
* @param {string[]} list list of VFS files
* @param {string} data input data string that will be cat to the files content
* @return {*} {Promise<string>}
*/
export function cat(list: string[], data: string): Promise<string> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(data);
}
const file = list.splice(0, 1)[0].asFileHandle();
return file
.read()
.then((text: string) => {
data = data + "\n" + text;
return cat(list, data)
.then((d) => resolve(d))
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
* Read all files content to on the list
*
* @export
* @param {string[]} list list of VFS files
* @param {GenericObject<string>[]} contents content array
* @return {*} {Promise<GenericObject<string>[]>}
*/
export function read_files(list: string[], contents: GenericObject<string>[]): Promise<GenericObject<string>[]> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(contents);
}
const file = list.splice(0, 1)[0].asFileHandle();
return file
.read()
.then((text: string) => {
contents.push({
path: file.path,
content: text
});
return read_files(list, contents)
.then((d) => resolve(d))
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
* Copy files to a folder
*
* @export
* @param {string[]} files list of files
* @param {string} to destination folder
* @return {*} {Promise<void>}
*/
export function copy(files: string[], to: string): Promise<void> {
return new Promise((resolve, reject) => {
if (files.length === 0) {
return resolve();
}
const file = files.splice(0, 1)[0].asFileHandle();
const tof = `${to}/${file.basename}`.asFileHandle();
return file
.onready()
.then((meta: { type: string }) => {
if (meta.type === "dir") {
// copy directory
const desdir = to.asFileHandle();
return desdir
.mk(file.basename)
.then(() => {
// read the dir content
return file
.read()
.then((data: API.RequestResult) => {
const list = (data.result as API.FileInfoType[]).map(
(v) => v.path
);
return copy(
list,
`${desdir.path}/${file.basename}`
)
.then(() => {
return copy(files, to)
.then(() => resolve())
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
} else {
// copy file
return file
.read("binary")
.then(async (data: ArrayBuffer) => {
const d = await tof
.setCache(
new Blob([data], {
type: file.info.mime,
})
)
.write(file.info.mime);
try {
await copy(files, to);
return resolve();
} catch (e) {
return reject(__e(e));
}
})
.catch((e: Error) => reject(__e(e)));
}
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
* Add a list of files to the zip archive
*
* @param {string[]} list list of VFS files
* @param {*} zip JSZip handle
* @param {string} base root path of all added files in the zip
* @return {*} {Promise<any>}
*/
function aradd(list: string[], zip: any, base: string): Promise<any> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(zip);
}
const path = list.splice(0, 1)[0];
const file = path.asFileHandle();
return file
.onready()
.then((meta: { type: string }) => {
if (meta.type === "dir") {
return file
.read()
.then(
(d: {
result:
| Iterable<unknown>
| ArrayLike<unknown>;
}) => {
const l = (d.result as API.FileInfoType[]).map(
(v) => v.path
);
return aradd(
l,
zip,
`${base}${file.basename}/`
)
.then(() => {
return aradd(
list,
zip,
base
)
.then(() => resolve(zip))
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
}
)
.catch((e: Error) => reject(__e(e)));
} else {
return file
.read("binary")
.then(async (d: any) => {
const zpath = `${base}${file.basename}`.replace(
/^\/+|\/+$/g,
""
);
zip.file(zpath, d, { binary: true });
try {
await aradd(list, zip, base);
return resolve(zip);
}
catch (e) {
return reject(__e(e));
}
})
.catch((e: Error) => reject(__e(e)));
}
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
* Create a zip archive from a folder
*
* @export
* @param {string} src source folder
* @param {string} dest destination archive
* @return {*} {Promise<void>}
*/
export function mkar(src: string, dest: string): Promise<void> {
return new Promise((resolve, reject) => {
return new Promise(async (r, e) => {
try {
await API.requires("os://scripts/jszip.min.js");
try {
const d = await src.asFileHandle().read();
return r(d.result);
} catch (ex) {
return e(__e(ex));
}
} catch (ex_1) {
return e(__e(ex_1));
}
})
.then((files: API.FileInfoType[]) => {
return new Promise(async (r, e) => {
const zip = new JSZip();
try {
const z = await aradd(
files.map((v: { path: any }) => v.path),
zip,
"/"
);
return r(z);
} catch (ex) {
return e(__e(ex));
}
});
})
.then((zip: any) => {
return zip
.generateAsync({ type: "base64" })
.then((data: string) => {
return dest
.asFileHandle()
.setCache(
"data:application/zip;base64," + data
)
.write("base64")
.then((r: any) => {
resolve();
})
.catch((e: Error) => reject(__e(e)));
});
})
.catch((e) => reject(__e(e)));
});
}
/**
* Create a list of directories
*
* @export
* @param {string[]} list of directories to be created
* @return {*} {Promise<void>}
*/
export function mkdirAll(list: string[]): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
}
const path = list.splice(0, 1)[0].asFileHandle();
return path
.parent()
.mk(path.basename)
.then((d: any) => {
return mkdirAll(list)
.then(() => resolve())
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
*
*
* @export
* @param {Array<string[]>} list of templates mapping files
* @param {string} path path stored create files
* @param {string} name
* @return {*} {Promise<void>}
*/
/**
* Make files from a set of template files
*
* @export
* @param {Array<string[]>} list mapping paths between templates files and created files
* @param {string} path files destination
* @param {(data: string) => string} callback: pre-processing files content before writing to destination files
* @return {*} {Promise<void>}
*/
export function mktpl(
list: Array<string[]>,
path: string,
callback: (data: string) => string
): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
}
const item = list.splice(0, 1)[0];
return `${path}/${item[0]}`
.asFileHandle()
.read()
.then((data) => {
const file = item[1].asFileHandle();
return file
.setCache(callback(data))
.write("text/plain")
.then(() => {
return mktpl(list, path, callback)
.then(() => resolve())
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
});
}
} }
} }
} }

View File

@ -7,41 +7,121 @@ namespace OS {
* Wrapper model for the ACE text editor * Wrapper model for the ACE text editor
* *
* @export * @export
* @class CodePadACEModel * @class ACEModel
* @extends {CodePadBaseEditorModel} * @extends {BaseEditorModel}
*/ */
export class CodePadACEModel extends CodePadBaseEditorModel { export class ACEModel extends BaseEditorModel {
/**
* Current editor mode
*
* @private
* @type {GenericObject<any>}
* @memberof ACEModel
*/
private mode: GenericObject<any>;
/** /**
* Reference to ACE language modes * Reference to ACE language modes
* *
* @private * @private
* @type {GenericObject<any>} * @type {GenericObject<any>}
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
private modes: GenericObject<any>; private modes: GenericObject<any>;
/** /**
* Creates an instance of CodePadACEModel. * Creates an instance of ACEModel.
* @param {CodePad} app CodePad instance * @param {ACEModel} app instance
* @param {GUI.tag.TabBarTag} tabbar tabbar element * @param {GUI.tag.TabBarTag} tabbar tabbar element
* @param {HTMLElement} editorarea main editor container element * @param {HTMLElement} editorarea main editor container element
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
constructor(app: CodePad, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) { constructor(app: BaseApplication, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) {
ace.config.set("basePath", "scripts/ace"); ace.config.set("basePath", "scripts/ace");
ace.require("ace/ext/language_tools"); ace.require("ace/ext/language_tools");
super(app, tabbar, editorarea); super(app, tabbar, editorarea);
this.modes = ace.require("ace/ext/modelist"); this.modes = ace.require("ace/ext/modelist");
} }
/**
* Reset the editor
*
* @protected
* @memberof ACEModel
*/
protected resetEditor(): void {
this.setValue("");
this.editor.getSession().setUndoManager(new ace.UndoManager());
}
/**
* Get a text model from the current editor session
*
* @protected
* @return {*}
* @memberof ACEModel
*/
protected getTexModel() {
const textModel = {} as any;
textModel.cursor = this.editor.getCursorPosition();
textModel.cache = this.getValue();
textModel.um = this.editor.session.getUndoManager();
textModel.langmode = this.mode;
return textModel;
}
/**
* Set text model to current editor session
*
* @protected
* @param {*} model
* @memberof ACEModel
*/
protected setTextModel(model: any): void {
this.editor.getSession().setUndoManager(new ace.UndoManager());
this.setValue(model.cache);
this.setMode(model.langmode);
if (model.cursor) {
this.setCursor(model.cursor);
}
this.editor.getSession().setUndoManager(model.um);
}
/**
* Create new editor model from file
*
* @protected
* @param {EditorFileHandle} file
* @return {*} {*}
* @memberof ACEModel
*/
protected newTextModelFrom(file: EditorFileHandle): any {
const textModel = {} as any;
textModel.um = new ace.UndoManager();
textModel.cache = file.cache;
textModel.cursor = undefined;
if (file.path.toString() !== "Untitled") {
textModel.langmode = this.getModeForPath(file.path);
} else {
textModel.langmode = {
text: "Text",
mode: "ace/mode/text",
};
}
return textModel;
}
/** /**
* Get language modes * Get language modes
* *
* @return {*} {GenericObject<any>[]} * @return {*} {GenericObject<any>[]}
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
getModes(): GenericObject<any>[] { getModes(): GenericObject<any>[] {
const list = []; const list = [];
@ -57,33 +137,20 @@ namespace OS {
* Set the editor theme * Set the editor theme
* *
* @param {string} theme theme name * @param {string} theme theme name
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
setTheme(theme: string): void { setTheme(theme: string): void {
this.editor.setTheme(theme); this.editor.setTheme(theme);
} }
/**
* Set the editor undo manager
*
* @protected
* @param {GenericObject<any>} um
* @memberof CodePadACEModel
*/
protected setUndoManager(um: GenericObject<any>): void {
this.editor.getSession().setUndoManager(um);
}
/** /**
* Set the editor cursor * Set the editor cursor
* *
* @protected * @private
* @param {GenericObject<any>} c cursor option * @param {GenericObject<any>} c cursor option
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
protected setCursor(c: GenericObject<any>): void { private setCursor(c: GenericObject<any>): void {
this.editor.renderer.scrollCursorIntoView( this.editor.renderer.scrollCursorIntoView(
{ {
row: c.row, row: c.row,
@ -110,43 +177,21 @@ namespace OS {
* ``` * ```
* *
* @param {GenericObject<any>} m language mode object * @param {GenericObject<any>} m language mode object
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
setMode(m: GenericObject<any>): void { setMode(m: GenericObject<any>): void {
this.currfile.langmode = m; this.mode = m;
this.editor.getSession().setMode(m.mode); this.editor.getSession().setMode(m.mode);
} }
/**
* Get current editor cursor position
*
* @protected
* @return {*} {GenericObject<any>}
* @memberof CodePadACEModel
*/
protected getCursor(): GenericObject<any> {
return this.editor.getCursorPosition();
}
/**
* create a new UndoManage instance
*
* @protected
* @return {*} {GenericObject<any>}
* @memberof CodePadACEModel
*/
protected newUndoManager(): GenericObject<any> {
return new ace.UndoManager();
}
/** /**
* Reference to the editor instance * Reference to the editor instance
* *
* @protected * @protected
* @type {GenericObject<any>} * @type {GenericObject<any>}
* @memberof CodePad * @memberof ACEModel
*/ */
protected editor: GenericObject<any>; protected editor: GenericObject<any>;
@ -156,7 +201,7 @@ namespace OS {
* *
* @protected * @protected
* @param {HTMLElement} el editor container DOM * @param {HTMLElement} el editor container DOM
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
protected editorSetup(el: HTMLElement): void { protected editorSetup(el: HTMLElement): void {
this.editor = ace.edit(el); this.editor = ace.edit(el);
@ -191,7 +236,7 @@ namespace OS {
* *
* @param {string} evt_str event name * @param {string} evt_str event name
* @param {() => void} callback callback function * @param {() => void} callback callback function
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
on(evt_str: string, callback: () => void): void { on(evt_str: string, callback: () => void): void {
switch (evt_str) { switch (evt_str) {
@ -213,7 +258,7 @@ namespace OS {
/** /**
* Resize the editor * Resize the editor
* *
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
resize(): void { resize(): void {
this.editor.resize(); this.editor.resize();
@ -223,7 +268,7 @@ namespace OS {
/** /**
* Focus on the editor * Focus on the editor
* *
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
focus(): void { focus(): void {
this.editor.focus(); this.editor.focus();
@ -236,9 +281,9 @@ namespace OS {
* @protected * @protected
* @param {string} path * @param {string} path
* @return {*} {GenericObject<any>} * @return {*} {GenericObject<any>}
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
protected getModeForPath(path: string): GenericObject<any> { protected getModeForPath(path: string): GenericObject<any> {
const m = this.modes.getModeForPath(path); const m = this.modes.getModeForPath(path);
return { return {
text: m.caption, text: m.caption,
@ -250,7 +295,7 @@ namespace OS {
* Get the editor status * Get the editor status
* *
* @return {*} {GenericObject<any>} * @return {*} {GenericObject<any>}
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
getEditorStatus(): GenericObject<any> { getEditorStatus(): GenericObject<any> {
const c = this.editor.session.selection.getCursor(); const c = this.editor.session.selection.getCursor();
@ -259,7 +304,7 @@ namespace OS {
row: c.row, row: c.row,
column: c.column, column: c.column,
line: l, line: l,
langmode: this.currfile.langmode, langmode: this.mode,
file: this.currfile.path file: this.currfile.path
} }
} }
@ -269,7 +314,7 @@ namespace OS {
* Get editor value * Get editor value
* *
* @return {*} {string} * @return {*} {string}
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
getValue(): string { getValue(): string {
return this.editor.getValue(); return this.editor.getValue();
@ -280,7 +325,7 @@ namespace OS {
* Set editor value * Set editor value
* *
* @param {string} value * @param {string} value
* @memberof CodePadACEModel * @memberof ACEModel
*/ */
setValue(value: string): void { setValue(value: string): void {
this.editor.setValue(value, -1); this.editor.setValue(value, -1);

View File

@ -113,7 +113,7 @@ namespace OS {
try { try {
await this.build(meta, false); await this.build(meta, false);
try { try {
return this.mkar( return API.VFS.mkar(
`${meta.root}/build/debug`, `${meta.root}/build/debug`,
`${meta.root}/build/release/${meta.name}.zip` `${meta.root}/build/release/${meta.name}.zip`
); );
@ -160,14 +160,16 @@ namespace OS {
["templates/sdk-README.tpl", `${rpath}/README.md`], ["templates/sdk-README.tpl", `${rpath}/README.md`],
["templates/sdk-scheme.tpl", `${rpath}/assets/scheme.html`], ["templates/sdk-scheme.tpl", `${rpath}/assets/scheme.html`],
]; ];
this.mkdirAll(dirs) API.VFS.mkdirAll(dirs)
.then(async () => { .then(async () => {
try { try {
await this.mkfileAll(files, path, name); await API.VFS.mktpl(files, this.basedir(), (data) => {
return data.format(name, `${path}/${name}`);
});
this.app.currdir = rpath.asFileHandle(); this.app.currdir = rpath.asFileHandle();
this.app.toggleSideBar(); this.app.toggleSideBar();
return this.app.eum.active.openFile( return this.app.eum.active.openFile(
`${rpath}/README.md`.asFileHandle() as application.CodePadFileHandle `${rpath}/README.md`.asFileHandle() as application.EditorFileHandle
); );
} catch (e) { } catch (e) {
return this.logger().error( return this.logger().error(
@ -226,7 +228,7 @@ namespace OS {
return resolve(AntOSDK.corelib["ts"]); return resolve(AntOSDK.corelib["ts"]);
} }
try { try {
const code = await this.loadzip(`${path}.zip`, "text"); const code = await API.VFS.readFileFromZip(`${path}.zip`, "text");
AntOSDK.corelib["ts"] = ts.createSourceFile(path, code, ts.ScriptTarget.Latest); AntOSDK.corelib["ts"] = ts.createSourceFile(path, code, ts.ScriptTarget.Latest);
return resolve(AntOSDK.corelib["ts"]); return resolve(AntOSDK.corelib["ts"]);
} catch (e) { } catch (e) {
@ -252,7 +254,7 @@ namespace OS {
try { try {
await this.load_corelib(core_lib); await this.load_corelib(core_lib);
const arr = []; const arr = [];
this.read_files(files, arr).then((_result) => { API.VFS.read_files(files, arr).then((_result) => {
const libs: string[] = arr.map((e) => e.path) const libs: string[] = arr.map((e) => e.path)
libs.unshift(core_lib); libs.unshift(core_lib);
const src_files: GenericObject<any> = {}; const src_files: GenericObject<any> = {};
@ -310,10 +312,14 @@ namespace OS {
const libs = [ const libs = [
`${this.basedir()}/libs/terser.min.js`, `${this.basedir()}/libs/terser.min.js`,
]; ];
if (!meta.coffees)
meta.coffees = [];
if (meta.coffees.length > 0) { if (meta.coffees.length > 0) {
libs.push(`${this.basedir()}/libs/coffeescript.js`); libs.push(`${this.basedir()}/libs/coffeescript.js`);
} }
if (meta.ts.length > 0) { if (!meta.ts)
meta.ts = [];
if (meta.ts && meta.ts.length > 0) {
libs.push("os://scripts/jszip.min.js"); libs.push("os://scripts/jszip.min.js");
libs.push(`${this.basedir()}/libs/typescript.min.js`) libs.push(`${this.basedir()}/libs/typescript.min.js`)
} }
@ -347,14 +353,13 @@ namespace OS {
*/ */
private compile_coffee(list: string[]): Promise<string> { private compile_coffee(list: string[]): Promise<string> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
if(list.length == 0) if (list.length == 0) {
{
return resolve(""); return resolve("");
} }
try { try {
await this.verify_coffee(list.map((x: string) => x)); await this.verify_coffee(list.map((x: string) => x));
try { try {
const code = await this.cat(list, ""); const code = await API.VFS.cat(list, "");
const jsrc = CoffeeScript.compile(code); const jsrc = CoffeeScript.compile(code);
this.logger().info(__("Compiled successful")); this.logger().info(__("Compiled successful"));
return resolve(jsrc); return resolve(jsrc);
@ -384,12 +389,12 @@ namespace OS {
]; ];
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
await this.mkdirAll(dirs); await API.VFS.mkdirAll(dirs);
try { try {
const src = await this.compile(meta); const src = await this.compile(meta);
let v: string; let v: string;
try { try {
let jsrc = await this.cat( let jsrc = await API.VFS.cat(
(() => { (() => {
const result = []; const result = [];
for (v of meta.javascripts) { for (v of meta.javascripts) {
@ -438,7 +443,7 @@ namespace OS {
} }
}); });
await new Promise<void>(async (r, e) => { await new Promise<void>(async (r, e) => {
const txt = await this.cat( const txt = await API.VFS.cat(
(() => { (() => {
const result1 = []; const result1 = [];
for (v of meta.css) { for (v of meta.css) {
@ -461,7 +466,7 @@ namespace OS {
return e(__e(ex_1)); return e(__e(ex_1));
} }
}); });
await this.copy( await API.VFS.copy(
(() => { (() => {
const result1_1 = []; const result1_1 = [];
for (v of meta.copies) { for (v of meta.copies) {

View File

@ -1,32 +1,61 @@
namespace OS { namespace OS {
export namespace application { export namespace application {
export abstract class CodePadBaseEditorModel { /**
* Extends the [[RemoteFileHandle]] interface with some useful
* properties used by the Editor API
*/
export type EditorFileHandle = API.VFS.RemoteFileHandle & {
/**
* The text will be displayed on the tab bar when opened
*
* @type {string}
*/
text: string;
/**
* Editor text model attached to the file
* modification history of the file
*
* @type {any}
*/
textModel: any;
/**
* Indicate whether the file is selected
*
* @type {boolean}
*/
selected: boolean;
};
export abstract class BaseEditorModel {
/** /**
* Reference to the current editing file handle * Reference to the current editing file handle
* *
* @protected * @protected
* @type {CodePadFileHandle} * @type {EditorFileHandle}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
protected currfile: CodePadFileHandle; protected currfile: EditorFileHandle;
/** /**
* Referent to the parent app * Referent to the parent app
* *
* @private * @private
* @type {CodePad} * @type {BaseApplication}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
private app: CodePad; private app: BaseApplication;
/** /**
* Reference to the editor tab bar UI * Reference to the editor tab bar UI
* *
* @private * @private
* @type {GUI.tag.TabBarTag} * @type {GUI.tag.TabBarTag}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private tabbar: GUI.tag.TabBarTag; private tabbar: GUI.tag.TabBarTag;
@ -36,7 +65,7 @@ namespace OS {
* *
* @private * @private
* @type {HTMLElement} * @type {HTMLElement}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
private container: HTMLElement; private container: HTMLElement;
@ -47,22 +76,22 @@ namespace OS {
* *
* @private * @private
* @type {boolean} * @type {boolean}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private editormux: boolean; private editormux: boolean;
/** /**
* Creates an instance of CodePadBaseEditorModel. * Creates an instance of BaseEditorModel.
* *
* @param {CodePad} app parent app * @param {Antedit} app parent app
* @param {GUI.tag.TabBarTag} tabbar tabbar DOM element * @param {GUI.tag.TabBarTag} tabbar tabbar DOM element
* @param {HTMLElement} editorarea editor container DOM element * @param {HTMLElement} editorarea editor container DOM element
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
constructor(app: CodePad, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) { constructor(app: BaseApplication, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) {
this.container = editorarea; this.container = editorarea;
this.currfile = "Untitled".asFileHandle() as CodePadFileHandle; this.currfile = "Untitled".asFileHandle() as EditorFileHandle;
this.tabbar = tabbar; this.tabbar = tabbar;
this.editorSetup(editorarea); this.editorSetup(editorarea);
this.app = app; this.app = app;
@ -118,11 +147,11 @@ namespace OS {
* Find a tab on the tabbar corresponding to a file handle * Find a tab on the tabbar corresponding to a file handle
* *
* @private * @private
* @param {CodePadFileHandle} file then file handle to search * @param {EditorFileHandle} file then file handle to search
* @returns {number} * @returns {number}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private findTabByFile(file: CodePadFileHandle): number { private findTabByFile(file: EditorFileHandle): number {
const lst = this.tabbar.items; const lst = this.tabbar.items;
const its = (() => { const its = (() => {
const result = []; const result = [];
@ -144,15 +173,15 @@ namespace OS {
* Create new tab when opening a file * Create new tab when opening a file
* *
* @private * @private
* @param {CodePadFileHandle} file * @param {EditorFileHandle} file
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private newTab(file: CodePadFileHandle): void { private newTab(file: EditorFileHandle): void {
file.text = file.basename ? file.basename : file.path; file.text = file.basename ? file.basename : file.path;
if (!file.cache) { if (!file.cache) {
file.cache = ""; file.cache = "";
} }
file.um = this.newUndoManager(); file.textModel = this.newTextModelFrom(file);
this.currfile.selected = false; this.currfile.selected = false;
file.selected = true; file.selected = true;
//console.log cnt //console.log cnt
@ -165,7 +194,7 @@ namespace OS {
* @private * @private
* @param {GUI.tag.ListViewItemTag} it reference to the tab to close * @param {GUI.tag.ListViewItemTag} it reference to the tab to close
* @returns {boolean} * @returns {boolean}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private closeTab(it: GUI.tag.ListViewItemTag): boolean { private closeTab(it: GUI.tag.ListViewItemTag): boolean {
this.tabbar.delete(it); this.tabbar.delete(it);
@ -173,7 +202,7 @@ namespace OS {
if (cnt === 0) { if (cnt === 0) {
this.openFile( this.openFile(
"Untitled".asFileHandle() as CodePadFileHandle "Untitled".asFileHandle() as EditorFileHandle
); );
return false; return false;
} }
@ -187,41 +216,23 @@ namespace OS {
* @private * @private
* @param {number} i tab index * @param {number} i tab index
* @returns {void} * @returns {void}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private selecteTab(i: number): void { private selecteTab(i: number): void {
//return if i is @tabbar.get "selidx" //return if i is @tabbar.get "selidx"
const file = this.tabbar.items[i] as CodePadFileHandle; const file = this.tabbar.items[i] as EditorFileHandle;
if (!file) { if (!file) {
return; return;
} }
//return if file is @currfile //return if file is @currfile
if (this.currfile !== file) { if (this.currfile !== file) {
this.currfile.cache = this.getValue(); this.currfile.textModel = this.getTexModel();
this.currfile.cursor = this.getCursor();
this.currfile.selected = false; this.currfile.selected = false;
this.currfile = file; this.currfile = file;
} }
if (!file.langmode) {
if (file.path.toString() !== "Untitled") {
file.langmode = this.getModeForPath(file.path);
} else {
file.langmode = {
text: "Text",
mode: "ace/mode/text",
};
}
}
this.editormux = true; this.editormux = true;
this.setUndoManager(this.newUndoManager()); this.setTextModel(file.textModel);
this.setValue(file.cache);
this.setMode(file.langmode);
if (file.cursor) {
this.setCursor(file.cursor);
}
this.setUndoManager(file.um);
if (this.onstatuschange) if (this.onstatuschange)
this.onstatuschange(this.getEditorStatus()); this.onstatuschange(this.getEditorStatus());
this.focus(); this.focus();
@ -231,12 +242,12 @@ namespace OS {
/** /**
* Select an opened file, this will select the corresponding tab * Select an opened file, this will select the corresponding tab
* *
* @param {(CodePadFileHandle | string)} file * @param {(EditorFileHandle | string)} file
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
selectFile(file: CodePadFileHandle | string): void { selectFile(file: EditorFileHandle | string): void {
const i = this.findTabByFile( const i = this.findTabByFile(
file.asFileHandle() as CodePadFileHandle file.asFileHandle() as EditorFileHandle
); );
if (i !== -1) { if (i !== -1) {
this.tabbar.selected = i; this.tabbar.selected = i;
@ -247,11 +258,11 @@ namespace OS {
* the just select the tab * the just select the tab
* *
* *
* @param {CodePadFileHandle} file file to open * @param {EditorFileHandle} file file to open
* @returns {void} * @returns {void}
* @memberof CodePad * @memberof BaseEditorModel
*/ */
openFile(file: CodePadFileHandle): void { openFile(file: EditorFileHandle): void {
//find tab //find tab
const i = this.findTabByFile(file); const i = this.findTabByFile(file);
if (i !== -1) { if (i !== -1) {
@ -280,10 +291,10 @@ namespace OS {
* write a file * write a file
* *
* @private * @private
* @param {CodePadFileHandle} file * @param {EditorFileHandle} file
* @memberof CodePad * @memberof BaseEditorModel
*/ */
private write(file: CodePadFileHandle): void { private write(file: EditorFileHandle): void {
this.currfile.cache = this.getValue(); this.currfile.cache = this.getValue();
file.write("text/plain") file.write("text/plain")
.then((d) => { .then((d) => {
@ -301,7 +312,7 @@ namespace OS {
* Save the current opened file * Save the current opened file
* *
* @return {*} {void} * @return {*} {void}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
save(): void { save(): void {
this.currfile.cache = this.getValue(); this.currfile.cache = this.getValue();
@ -315,7 +326,7 @@ namespace OS {
* Save the current file as another file * Save the current file as another file
* *
* @public * @public
* @memberof CodePad * @memberof BaseEditorModel
*/ */
saveAs(): void { saveAs(): void {
this.app.openDialog("FileDialog", { this.app.openDialog("FileDialog", {
@ -334,10 +345,10 @@ namespace OS {
/** /**
* Get all dirty file handles in the editor * Get all dirty file handles in the editor
* *
* @return {*} {CodePadFileHandle[]} * @return {*} {EditorFileHandle[]}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
dirties(): CodePadFileHandle[] { dirties(): EditorFileHandle[] {
const result = []; const result = [];
for (let v of Array.from(this.tabbar.items)) { for (let v of Array.from(this.tabbar.items)) {
if (v.dirty) { if (v.dirty) {
@ -350,7 +361,7 @@ namespace OS {
/** /**
* Context menu handle for the editor * Context menu handle for the editor
* *
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
set contextmenuHandle(cb: (e: any, m: any) => void) { set contextmenuHandle(cb: (e: any, m: any) => void) {
this.container.contextmenuHandle = cb; this.container.contextmenuHandle = cb;
@ -360,19 +371,18 @@ namespace OS {
/** /**
* Close all opened files * Close all opened files
* *
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
closeAll(): void { closeAll(): void {
this.tabbar.items = []; this.tabbar.items = [];
this.setValue(""); this.resetEditor();
this.setUndoManager(this.newUndoManager());
} }
/** /**
* Check whether the editor is dirty * Check whether the editor is dirty
* *
* @return {*} {boolean} * @return {*} {boolean}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
isDirty(): boolean { isDirty(): boolean {
return this.dirties().length > 0; return this.dirties().length > 0;
@ -386,7 +396,7 @@ namespace OS {
* @protected * @protected
* @abstract * @abstract
* @param {HTMLElement} el * @param {HTMLElement} el
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
protected abstract editorSetup(el: HTMLElement): void; protected abstract editorSetup(el: HTMLElement): void;
@ -399,7 +409,7 @@ namespace OS {
* @abstract * @abstract
* @param {string} evt_str * @param {string} evt_str
* @param {() => void} callback * @param {() => void} callback
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract on(evt_str: string, callback: () => void): void; abstract on(evt_str: string, callback: () => void): void;
@ -410,7 +420,7 @@ namespace OS {
* Should be implemented by subclasses * Should be implemented by subclasses
* *
* @abstract * @abstract
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract resize(): void; abstract resize(): void;
@ -421,7 +431,7 @@ namespace OS {
* Should be implemented by subclasses * Should be implemented by subclasses
* *
* @abstract * @abstract
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract focus(): void; abstract focus(): void;
@ -435,7 +445,7 @@ namespace OS {
* @abstract * @abstract
* @param {string} path * @param {string} path
* @return {*} {GenericObject<any>} * @return {*} {GenericObject<any>}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
protected abstract getModeForPath(path: string): GenericObject<any>; protected abstract getModeForPath(path: string): GenericObject<any>;
@ -447,7 +457,7 @@ namespace OS {
* *
* @abstract * @abstract
* @return {*} {GenericObject<any>} * @return {*} {GenericObject<any>}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract getEditorStatus(): GenericObject<any>; abstract getEditorStatus(): GenericObject<any>;
@ -459,7 +469,7 @@ namespace OS {
* *
* @abstract * @abstract
* @return {*} {string} * @return {*} {string}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract getValue(): string; abstract getValue(): string;
@ -471,51 +481,10 @@ namespace OS {
* *
* @abstract * @abstract
* @param {string} value * @param {string} value
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract setValue(value: string): void; abstract setValue(value: string): void;
/**
* Get the current editor position
*
* Should be implemented by subclasses
*
* @protected
* @abstract
* @return {*} {GenericObject<any>}
* @memberof CodePadBaseEditorModel
*/
protected abstract getCursor(): GenericObject<any>;
/**
* Create new instance of UndoManager
*
* This is specific to each editor, so
* it should be implemented by subclasses
*
* @protected
* @abstract
* @return {*} {GenericObject<any>}
* @memberof CodePadBaseEditorModel
*/
protected abstract newUndoManager(): GenericObject<any>;
/**
* Set the editor UndoManager
*
* Should be implemented by subclasses
*
* @protected
* @abstract
* @param {GenericObject<any>} um
* @memberof CodePadBaseEditorModel
*/
protected abstract setUndoManager(um: GenericObject<any>): void;
/** /**
* Set the editor language mode * Set the editor language mode
* *
@ -523,24 +492,54 @@ namespace OS {
* *
* @abstract * @abstract
* @param {GenericObject<any>} m * @param {GenericObject<any>} m
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract setMode(m: GenericObject<any>): void; abstract setMode(m: GenericObject<any>): void;
/** /**
* Set current editor cursor position * Get textModel from the current editor session
*
* Should be implemented by subclasses
* *
* @protected * @protected
* @abstract * @abstract
* @param {GenericObject<any>} c * @return {*} {*}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
protected abstract setCursor(c: GenericObject<any>): void; protected abstract getTexModel(): any;
/**
* Set text model to the current editor session
*
* @protected
* @abstract
* @param {*} model
* @memberof BaseEditorModel
*/
protected abstract setTextModel(model: any): void;
/**
* Create new text model from the VFS file
*
* @protected
* @abstract
* @param {EditorFileHandle} file
* @return {*} {*}
* @memberof BaseEditorModel
*/
protected abstract newTextModelFrom(file: EditorFileHandle): any;
/**
* Reset the editor
*
* @protected
* @abstract
* @memberof BaseEditorModel
*/
protected abstract resetEditor(): void;
/** /**
* Set the current editor theme * Set the current editor theme
* *
@ -548,7 +547,7 @@ namespace OS {
* *
* @abstract * @abstract
* @param {string} theme * @param {string} theme
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract setTheme(theme: string): void; abstract setTheme(theme: string): void;
@ -558,7 +557,7 @@ namespace OS {
* *
* @abstract * @abstract
* @return {*} {GenericObject<any>[]} * @return {*} {GenericObject<any>[]}
* @memberof CodePadBaseEditorModel * @memberof BaseEditorModel
*/ */
abstract getModes(): GenericObject<any>[]; abstract getModes(): GenericObject<any>[];
} }

View File

@ -37,40 +37,7 @@ namespace OS {
import(libs: string[]): Promise<any> { import(libs: string[]): Promise<any> {
return API.require(libs); return API.require(libs);
} }
/**
* Get content of a file compressed in a zip archive
*
* @param {*} file
* @memberof BaseExtension
*/
loadzip(file: string, type:string): Promise<any>
{
return new Promise(async (resolve, reject) =>{
try {
const data = await file.asFileHandle().read("binary");
try {
const zip = await JSZip.loadAsync(data);
let file_name = "";
for (let name in zip.files) {
file_name = name;
break;
}
try {
const udata = await zip.file(file_name).async(type);
resolve(udata);
} catch (e_2) {
return reject(__e(e_2));
}
} catch (e_1) {
return reject(__e(e_1));
}
} catch (e) {
return reject(__e(e));
}
});
}
/** /**
* *
* *
@ -136,352 +103,6 @@ namespace OS {
return []; return [];
} }
/**
* Cat all files to a single output string
*
* @protected
* @param {string[]} list
* @param {string} data
* @returns {Promise<string>}
* @memberof BaseExtension
*/
protected cat(list: string[], data: string): Promise<string> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(data);
}
const file = list.splice(0, 1)[0].asFileHandle();
return file
.read()
.then((text: string) => {
data = data + "\n" + text;
return this.cat(list, data)
.then((d) => resolve(d))
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
* Read all file content in the list
*
* @protected
* @param {string[]} list
* @return {*} {Promise<GenericObject<string>>}
* @memberof BaseExtension
*/
protected read_files(list:string[], contents: GenericObject<string>[]): Promise<GenericObject<string>[]>
{
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(contents);
}
const file = list.splice(0, 1)[0].asFileHandle();
return file
.read()
.then((text: string) => {
contents.push({
path: file.path,
content: text
});
return this.read_files(list, contents)
.then((d) => resolve(d))
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
*
*
* @protected
* @param {string[]} files
* @param {string} to
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected copy(files: string[], to: string): Promise<void> {
return new Promise((resolve, reject) => {
if (files.length === 0) {
return resolve();
}
const file = files.splice(0, 1)[0].asFileHandle();
const tof = `${to}/${file.basename}`.asFileHandle();
return file
.onready()
.then((meta: { type: string }) => {
if (meta.type === "dir") {
// copy directory
const desdir = to.asFileHandle();
return desdir
.mk(file.basename)
.then(() => {
// read the dir content
return file
.read()
.then((data: API.RequestResult) => {
const list = (data.result as API.FileInfoType[]).map(
(v) => v.path
);
return this.copy(
list,
`${desdir.path}/${file.basename}`
)
.then(() => {
return this.copy(files, to)
.then(() => resolve())
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
} else {
// copy file
return file
.read("binary")
.then(async (data: ArrayBuffer) => {
const d = await tof
.setCache(
new Blob([data], {
type: file.info.mime,
})
)
.write(file.info.mime);
try {
await this.copy(files, to);
return resolve();
} catch (e) {
return reject(__e(e));
}
})
.catch((e: Error) => reject(__e(e)));
}
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
*
*
* @private
* @param {string[]} list
* @param {*} zip
* @param {string} base
* @returns {Promise<any>}
* @memberof BaseExtension
*/
private aradd(list: string[], zip: any, base: string): Promise<any> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(zip);
}
const path = list.splice(0, 1)[0];
const file = path.asFileHandle();
return file
.onready()
.then((meta: { type: string }) => {
if (meta.type === "dir") {
return file
.read()
.then(
(d: {
result:
| Iterable<unknown>
| ArrayLike<unknown>;
}) => {
const l = (d.result as API.FileInfoType[]).map(
(v) => v.path
);
return this.aradd(
l,
zip,
`${base}${file.basename}/`
)
.then(() => {
return this.aradd(
list,
zip,
base
)
.then(() => resolve(zip))
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
}
)
.catch((e: Error) => reject(__e(e)));
} else {
return file
.read("binary")
.then(async (d: any) => {
const zpath = `${base}${file.basename}`.replace(
/^\/+|\/+$/g,
""
);
zip.file(zpath, d, { binary: true });
try {
await this.aradd(list, zip, base);
return resolve(zip);
}
catch (e) {
return reject(__e(e));
}
})
.catch((e: Error) => reject(__e(e)));
}
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
*
*
* @protected
* @param {string} src
* @param {string} dest
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected mkar(src: string, dest: string): Promise<void> {
this.logger().info(__("Preparing for release"));
return new Promise((resolve, reject) => {
return new Promise(async (r, e) => {
try {
await this.import(["os://scripts/jszip.min.js"]);
try {
const d = await src.asFileHandle().read();
return r(d.result);
} catch (ex) {
return e(__e(ex));
}
} catch (ex_1) {
return e(__e(ex_1));
}
})
.then((files: API.FileInfoType[]) => {
return new Promise(async (r, e) => {
const zip = new JSZip();
try {
const z = await this.aradd(
files.map((v: { path: any }) => v.path),
zip,
"/"
);
return r(z);
} catch (ex) {
return e(__e(ex));
}
});
})
.then((zip: any) => {
return zip
.generateAsync({ type: "base64" })
.then((data: string) => {
return dest
.asFileHandle()
.setCache(
"data:application/zip;base64," + data
)
.write("base64")
.then((r: any) => {
resolve();
return this.logger().info(
__(
"Archive is generated at: {0}",
dest
)
);
})
.catch((e: Error) => reject(__e(e)));
});
})
.catch((e) => reject(__e(e)));
});
}
/**
*
*
* @protected
* @param {string[]} list
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected mkdirAll(list: string[]): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
}
const path = list.splice(0, 1)[0].asFileHandle();
return path
.parent()
.mk(path.basename)
.then((d: any) => {
this.app.observable.trigger("filechange", {
file: path.parent(),
type: "dir",
});
return this.mkdirAll(list)
.then(() => resolve())
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
*
*
* @protected
* @param {string[]} list
* @param {string} path
* @param {string} name
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected mkfileAll(
list: Array<string[]>,
path: string,
name: string
): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
}
const item = list.splice(0, 1)[0];
return `${this.basedir()}/${item[0]}`
.asFileHandle()
.read()
.then((data) => {
const file = item[1].asFileHandle();
return file
.setCache(data.format(name, `${path}/${name}`))
.write("text/plain")
.then(() => {
this.app.trigger("filechange", {
file,
type: "file",
});
return this.mkfileAll(list, path, name)
.then(() => resolve())
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
});
}
/** /**
* *

View File

@ -68,7 +68,7 @@ namespace OS {
try { try {
await this.build(meta); await this.build(meta);
try { try {
return this.mkar( return API.VFS.mkar(
`${meta.root}/build/debug`, `${meta.root}/build/debug`,
`${meta.root}/build/release/${meta.meta.name}.zip` `${meta.root}/build/release/${meta.meta.name}.zip`
); );
@ -157,14 +157,16 @@ namespace OS {
["templates/ext-main.tpl", `${rpath}/${name}.coffee`], ["templates/ext-main.tpl", `${rpath}/${name}.coffee`],
["templates/ext-extension.tpl", `${rpath}/extension.json`], ["templates/ext-extension.tpl", `${rpath}/extension.json`],
]; ];
this.mkdirAll(dirs) API.VFS.mkdirAll(dirs)
.then(async () => { .then(async () => {
try { try {
await this.mkfileAll(files, path, name); await API.VFS.mktpl(files, this.basedir(), (data)=>{
return data.format(name, `${path}/${name}`);
});
this.app.currdir = rpath.asFileHandle(); this.app.currdir = rpath.asFileHandle();
this.app.toggleSideBar(); this.app.toggleSideBar();
return this.app.eum.active.openFile( return this.app.eum.active.openFile(
`${rpath}/${name}.coffee`.asFileHandle() as application.CodePadFileHandle `${rpath}/${name}.coffee`.asFileHandle() as application.EditorFileHandle
); );
} catch (e) { } catch (e) {
return this.logger().error( return this.logger().error(
@ -227,7 +229,7 @@ namespace OS {
try { try {
await this.verify(list.map((x: string) => x)); await this.verify(list.map((x: string) => x));
try { try {
const code = await this.cat(list, ""); const code = await API.VFS.cat(list, "");
const jsrc = CoffeeScript.compile(code); const jsrc = CoffeeScript.compile(code);
this.logger().info(__("Compiled successful")); this.logger().info(__("Compiled successful"));
return resolve(jsrc); return resolve(jsrc);
@ -257,7 +259,7 @@ namespace OS {
const src = await this.compile(meta); const src = await this.compile(meta);
let v: string; let v: string;
try { try {
const jsrc = await this.cat( const jsrc = await API.VFS.cat(
(() => { (() => {
const result = []; const result = [];
for (v of meta.javascripts) { for (v of meta.javascripts) {
@ -283,7 +285,7 @@ namespace OS {
.then((data) => r(data)) .then((data) => r(data))
.catch((ex_1) => e(__e(ex_1))) .catch((ex_1) => e(__e(ex_1)))
); );
await this.copy( await API.VFS.copy(
(() => { (() => {
const result1 = []; const result1 = [];
for (v of meta.copies) { for (v of meta.copies) {
@ -503,7 +505,7 @@ namespace OS {
} }
} }
if (dir.length > 0) { if (dir.length > 0) {
this.mkdirAll(dir) API.VFS.mkdirAll(dir)
.then(() => { .then(() => {
this.installExtension( this.installExtension(
files, files,

View File

@ -1,47 +1,5 @@
namespace OS { namespace OS {
export namespace application { export namespace application {
/**
* Extends the [[RemoteFileHandle]] interface with some useful
* properties used by [[CodePad]]
*/
export type CodePadFileHandle = API.VFS.RemoteFileHandle & {
/**
* The text will be displayed on the tab bar when opened
*
* @type {string}
*/
text: string;
/**
* ACE Undo manager of the current file, stores the
* modification history of the file
*
* @type {GenericObject<any>}
*/
um: GenericObject<any>;
/**
* Indicate whether the file is selected
*
* @type {boolean}
*/
selected: boolean;
/**
* Store the latest cursor position on the editor
* when editing the file
*
* @type {GenericObject<any>}
*/
cursor: GenericObject<any>;
/**
* Language mode setting of the file
*
* @type {GenericObject<string>}
*/
langmode: GenericObject<string>;
};
/** /**
* [[CodePad]]'s [[CommandPalette]] action type definition * [[CodePad]]'s [[CommandPalette]] action type definition
*/ */
@ -252,14 +210,14 @@ namespace OS {
// add editor instance // add editor instance
this.eum this.eum
.add(new CodePadACEModel( .add(new ACEModel(
this, this,
this.find("left-tabbar") as GUI.tag.TabBarTag, this.find("left-tabbar") as GUI.tag.TabBarTag,
this.find("left-editorarea")) as CodePadBaseEditorModel) this.find("left-editorarea")) as BaseEditorModel)
.add(new CodePadACEModel( .add(new ACEModel(
this, this,
this.find("right-tabbar") as GUI.tag.TabBarTag, this.find("right-tabbar") as GUI.tag.TabBarTag,
this.find("right-editorarea")) as CodePadBaseEditorModel); this.find("right-editorarea")) as BaseEditorModel);
this.eum.onstatuschange = (st) => this.eum.onstatuschange = (st) =>
this.updateStatus(st) this.updateStatus(st)
@ -283,13 +241,13 @@ namespace OS {
return reject(__e(e)); return reject(__e(e));
} }
}); });
let file = "Untitled".asFileHandle() as CodePadFileHandle; let file = "Untitled".asFileHandle() as EditorFileHandle;
if (this.args && this.args.length > 0) { if (this.args && this.args.length > 0) {
this.addRecent(this.args[0].path); this.addRecent(this.args[0].path);
if (this.args[0].type === "dir") { if (this.args[0].type === "dir") {
this.currdir = this.args[0].path.asFileHandle() as CodePadFileHandle; this.currdir = this.args[0].path.asFileHandle() as EditorFileHandle;
} else { } else {
file = this.args[0].path.asFileHandle() as CodePadFileHandle; file = this.args[0].path.asFileHandle() as EditorFileHandle;
this.currdir = file.parent(); this.currdir = file.parent();
} }
} }
@ -316,7 +274,7 @@ namespace OS {
} }
this.addRecent(e.data.path); this.addRecent(e.data.path);
return this.eum.active.openFile( return this.eum.active.openFile(
e.data.path.asFileHandle() as CodePadFileHandle e.data.path.asFileHandle() as EditorFileHandle
); );
}; };
@ -519,7 +477,7 @@ namespace OS {
else { else {
$(right_pannel).show(); $(right_pannel).show();
this.split_mode = true; this.split_mode = true;
right_editor.openFile("Untitled".asFileHandle() as CodePadFileHandle); right_editor.openFile("Untitled".asFileHandle() as EditorFileHandle);
right_editor.focus(); right_editor.focus();
} }
this.trigger("resize"); this.trigger("resize");
@ -977,7 +935,7 @@ namespace OS {
} }
switch (dataid) { switch (dataid) {
case "new": case "new":
return me.eum.active.openFile("Untitled".asFileHandle() as CodePadFileHandle); return me.eum.active.openFile("Untitled".asFileHandle() as EditorFileHandle);
case "open": case "open":
return me return me
.openDialog("FileDialog", { .openDialog("FileDialog", {
@ -1081,7 +1039,7 @@ namespace OS {
], ],
onchildselect: ( onchildselect: (
e: GUI.TagEventType<GUI.tag.MenuEventData>, e: GUI.TagEventType<GUI.tag.MenuEventData>,
r: CodePadFileHandle r: EditorFileHandle
) => { ) => {
switch (e.data.item.data.dataid) { switch (e.data.item.data.dataid) {
case "cmdpalette": case "cmdpalette":
@ -1225,19 +1183,19 @@ namespace OS {
* Referent to the active editor model * Referent to the active editor model
* *
* @private * @private
* @type {CodePadBaseEditorModel} * @type {BaseEditorModel}
* @memberof EditorModelManager * @memberof EditorModelManager
*/ */
private active_editor: CodePadBaseEditorModel; private active_editor: BaseEditorModel;
/** /**
* Store a list of editor models * Store a list of editor models
* *
* @private * @private
* @type {CodePadBaseEditorModel[]} * @type {BaseEditorModel[]}
* @memberof EditorModelManager * @memberof EditorModelManager
*/ */
private models: CodePadBaseEditorModel[]; private models: BaseEditorModel[];
/** /**
* Creates an instance of EditorModelManager. * Creates an instance of EditorModelManager.
@ -1248,7 +1206,7 @@ namespace OS {
this.models = []; this.models = [];
} }
get editors(): CodePadBaseEditorModel[] { get editors(): BaseEditorModel[] {
return this.models; return this.models;
} }
set contextmenuHandle(cb: (e: any, m: any) => void) { set contextmenuHandle(cb: (e: any, m: any) => void) {
@ -1261,20 +1219,20 @@ namespace OS {
* Get the active editor model * Get the active editor model
* *
* @readonly * @readonly
* @type {CodePadBaseEditorModel} * @type {BaseEditorModel}
* @memberof EditorModelManager * @memberof EditorModelManager
*/ */
get active(): CodePadBaseEditorModel { get active(): BaseEditorModel {
return this.active_editor; return this.active_editor;
} }
/** /**
* Add a model to the manager * Add a model to the manager
* *
* @param {CodePadBaseEditorModel} model * @param {BaseEditorModel} model
* @memberof EditorModelManager * @memberof EditorModelManager
*/ */
add(model: CodePadBaseEditorModel): EditorModelManager { add(model: BaseEditorModel): EditorModelManager {
this.models.push(model); this.models.push(model);
if (!this.active_editor) if (!this.active_editor)
this.active_editor = model; this.active_editor = model;
@ -1290,7 +1248,7 @@ namespace OS {
} }
} }
dirties(): CodePadFileHandle[] { dirties(): EditorFileHandle[] {
let list = []; let list = [];
for (let ed of this.models) { for (let ed of this.models) {
list = list.concat(ed.dirties()); list = list.concat(ed.dirties());