Improvement + bug fix

- Some minor bug fix
- Major change: allow split view in CodePad, make CodePad API portable so that it is easy to use another editor
other than ACE in the futures (such as monaco editor)
This commit is contained in:
lxsang 2020-12-20 16:45:51 +01:00
parent 6ee3861be6
commit 05cea66870
18 changed files with 749 additions and 625 deletions

Binary file not shown.

View File

@ -166,10 +166,10 @@ namespace OS {
*
* @protected
* @param {Promise<any>} promise the promise on a task to be performed
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof BaseApplication
*/
protected load(promise: Promise<any>): Promise<any> {
protected load(promise: Promise<any>): Promise<void> {
const q = this._api.mid();
return new Promise(async (resolve, reject) => {
this._api.loading(q, this.name);

View File

@ -1001,7 +1001,7 @@ namespace OS {
fileview.fetch = (path: string) =>
new Promise(function (resolve, reject) {
if (!path) {
return resolve();
return resolve(undefined);
}
return path
.asFileHandle()

View File

@ -1379,9 +1379,9 @@ namespace OS {
* @export
* @param {string} l VFS path to the library
* @param {string} force force reload library
* @returns {Promise<any>} a promise on the result data
* @returns {Promise<void>} a promise on the result data
*/
export function requires(l: string, force: boolean = false): Promise<any> {
export function requires(l: string, force: boolean = false): Promise<void> {
return new Promise(function(resolve, reject) {
if (!API.shared[l] || force) {
const libfp = l.asFileHandle();
@ -1428,9 +1428,9 @@ namespace OS {
*
* @export
* @param {string[]} libs list of shared libraries
* @returns {Promise<any>}
* @returns {Promise<void>}
*/
export function require(libs: string[]): Promise<any> {
export function require(libs: string[]): Promise<void> {
return new Promise(function(resolve, reject) {
if (!(libs.length > 0)) {
return resolve();

View File

@ -228,7 +228,6 @@ namespace OS {
* dialog definition for the format of the input data
* @returns {Promise<any>} A promise on the callback data of the dialog, refer
* to each dialog definition for the format of the callback data
* @returns {Promise<any>}
*/
export function openDialog(
d: string | BaseDialog,
@ -237,7 +236,7 @@ namespace OS {
return new Promise(function (resolve, reject) {
if (dialog) {
dialog.show();
return resolve();
return resolve(undefined);
}
if (typeof d === "string") {
if (!dialogs[d]) {
@ -536,9 +535,9 @@ namespace OS {
*
* @export
* @param {string[]} srvs list of service class names
* @returns {Promise<any>}
* @returns {Promise<void>}
*/
export function pushServices(srvs: string[]): Promise<any> {
export function pushServices(srvs: string[]): Promise<void> {
return new Promise(function (resolve, reject) {
if (!(srvs.length > 0)) {
return resolve();

View File

@ -1,217 +0,0 @@
{
"About":"About",
"About: {0}":"About: {0}",
"Add category":"Add category",
"Add repository":"Add repository",
"Address":"Address",
"Alive (ms)":"Alive (ms)",
"Application installed":"Application installed",
"Application {0} is not executable":"Application {0} is not executable",
"Application":"Application",
"Applications":"Applications",
"April":"April",
"August":"August",
"Authentication":"Authentication",
"Cancel":"Cancel",
"Cannot Edit category":"Cannot Edit category",
"Cannot add new category":"Cannot add new category",
"Cannot create {0}":"Cannot create {0}",
"Cannot delete all content of: {0} [{1}]":"Cannot delete all content of: {0} [{1}]",
"Cannot delete the category: {0} [{1}]":"Cannot delete the category: {0} [{1}]",
"Cannot delete the section: {0}":"Cannot delete the section: {0}",
"Cannot delete: {0}":"Cannot delete: {0}",
"Cannot down load the app {0}":"Cannot down load the app {0}",
"Cannot export file for embedding to text":"Cannot export file for embedding to text",
"Cannot fetch CV categories":"Cannot fetch CV categories",
"Cannot fetch the entry content":"Cannot fetch the entry content",
"Cannot fetch user data":"Cannot fetch user data",
"Cannot install {0}":"Cannot install {0}",
"Cannot load 3rd library at: {0}":"Cannot load 3rd library at: {0}",
"Cannot move section":"Cannot move section",
"Cannot read service script: {0}":"Cannot read service script: {0}",
"Cannot render the PDF file":"Cannot render the PDF file",
"Cannot save blog: {0}":"Cannot save blog: {0}",
"Cannot save section: {0}":"Cannot save section: {0}",
"Cannot save system setting":"Cannot save system setting",
"Cannot save user data":"Cannot save user data",
"Cannot share file: {0}":"Cannot share file: {0}",
"Cannot uninstall package: {0}":"Cannot uninstall package: {0}",
"Categories":"Categories",
"Clear all":"Clear all",
"Close tab":"Close tab",
"Close without saving ?":"Close without saving ?",
"Close":"Close",
"Copy not yet implemented":"Copy not yet implemented",
"Copy":"Copy",
"Created: {0}":"Created: {0}",
"Cut":"Cut",
"December":"December",
"Delete a post":"Delete a post",
"Delete category":"Delete category",
"Delete section":"Delete section",
"Delete":"Delete",
"Desktop":"Desktop",
"Dialog {0} not found":"Dialog {0} not found",
"Do you really want to delete this post ?":"Do you really want to delete this post ?",
"Do you really want to delete: {0}?":"Do you really want to delete: {0}?",
"Download":"Download",
"Edit category":"Edit category",
"Edit repository":"Edit repository",
"Edit":"Edit",
"Email":"Email",
"Error find app by mimes {0}":"Error find app by mimes {0}",
"Error reading package meta data: {0}":"Error reading package meta data: {0}",
"Error saving file {0}":"Error saving file {0}",
"Exit":"Exit",
"Fail to create directory: {0}":"Fail to create directory: {0}",
"Fail to create {0}: {1}":"Fail to create {0}: {1}",
"Fail to delete {0}: {1}":"Fail to delete {0}: {1}",
"Fail to delete: {0}":"Fail to delete: {0}",
"Fail to fetch packages list from: {0}":"Fail to fetch packages list from: {0}",
"Fail to get file meta data: {0}":"Fail to get file meta data: {0}",
"Fail to make request: {0}":"Fail to make request: {0}",
"Fail to move file: {0} -> {1}":"Fail to move file: {0} -> {1}",
"Fail to paste: {0}":"Fail to paste: {0}",
"Fail to publish file: {0}":"Fail to publish file: {0}",
"Fail to query data from database: {0}":"Fail to query data from database: {0}",
"Fail to read file: {0}":"Fail to read file: {0}",
"Fail to rename to {0}: {1}":"Fail to rename to {0}: {1}",
"Fail to scan directory: {0}":"Fail to scan directory: {0}",
"Fail to upload file to: {0}":"Fail to upload file to: {0}",
"Fail to upload to {0}: {1}":"Fail to upload to {0}: {1}",
"Fail to write to file: {0}":"Fail to write to file: {0}",
"Fail to {0} package":"Fail to {0} package",
"February":"February",
"File name":"File name",
"File not found {0}":"File not found {0}",
"File {0} copied":"File {0} copied",
"File {0} cut":"File {0} cut",
"File":"File",
"Folder name":"Folder name",
"Format : [name] url":"Format : [name] url",
"Found {0} sections":"Found {0} sections",
"Fri":"Fri",
"From":"From",
"Full name must be entered":"Full name must be entered",
"Full name":"Full name",
"Google Drive":"Google Drive",
"Hidden files":"Hidden files",
"Home":"Home",
"Icon view":"Icon view",
"Ignore all {0} unsaved files ?":"Ignore all {0} unsaved files ?",
"Install":"Install",
"Invalid package: Meta data file not found":"Invalid package: Meta data file not found",
"January":"January",
"July":"July",
"June":"June",
"Kill process":"Kill process",
"Language file {0} not found":"Language file {0} not found",
"Launch":"Launch",
"List view":"List view",
"Location":"Location",
"Log out":"Log out",
"Logout":"Logout",
"March":"March",
"May":"May",
"Mime type {0} is not supported":"Mime type {0} is not supported",
"Modify section entry":"Modify section entry",
"Mon":"Mon",
"Move to":"Move to",
"Name":"Name",
"Navigation bar":"Navigation bar",
"New file":"New file",
"New folder":"New folder",
"New section entry for {0}":"New section entry for {0}",
"New":"New",
"No application available to open {0}":"No application available to open {0}",
"No post found: {0}":"No post found: {0}",
"No":"No",
"November":"November",
"OS":"OS",
"October":"October",
"Ok":"Ok",
"Only {0} could be selected":"Only {0} could be selected",
"Open file":"Open file",
"Open with":"Open with",
"Open":"Open",
"Options":"Options",
"Package uninstalled":"Package uninstalled",
"Parent can not be the category itself":"Parent can not be the category itself",
"Paste":"Paste",
"Phone":"Phone",
"Pid":"Pid",
"Please enter category name":"Please enter category name",
"Please enter tags":"Please enter tags",
"Please insert a title in the text: beginning with heading":"Please insert a title in the text: beginning with heading",
"Please select a category":"Please select a category",
"Please select a date":"Please select a date",
"Please select a file":"Please select a file",
"Please select a parent category":"Please select a parent category",
"Please select a section to edit":"Please select a section to edit",
"Please select a section to move":"Please select a section to move",
"Preview":"Preview",
"Properties":"Properties",
"Quit without saving ?":"Quit without saving ?",
"Quit":"Quit",
"Read more":"Read more",
"Refresh":"Refresh",
"Rename":"Rename",
"Repositories":"Repositories",
"Resource not found {0}":"Resource not found {0}",
"Resource not found: {0}":"Resource not found: {0}",
"Row {0}, col {1}, lines: {2}":"Row {0}, col {1}, lines: {2}",
"Sat":"Sat",
"Save as":"Save as",
"Save":"Save",
"Section list is empty, please add one":"Section list is empty, please add one",
"Select image file":"Select image file",
"Selected: {0} ({1} bytes)":"Selected: {0} ({1} bytes)",
"September":"September",
"Service":"Service",
"Share file":"Share file",
"Shared url: {0}":"Shared url: {0}",
"Shared":"Shared",
"Short biblio":"Short biblio",
"Sidebar":"Sidebar",
"Size":"Size",
"Subtitle":"Subtitle",
"Sun":"Sun",
"System fail: Cannot init desktop manager":"System fail: Cannot init desktop manager",
"System fail: Cannot init login screen":"System fail: Cannot init login screen",
"Tags":"Tags",
"This feature is not implemented yet":"This feature is not implemented yet",
"Thu":"Thu",
"Title or content must not be blank":"Title or content must not be blank",
"Title":"Title",
"Toggle Full screen":"Toggle Full screen",
"Tree view":"Tree view",
"Tue":"Tue",
"Type":"Type",
"Uninstall : {0}?":"Uninstall : {0}?",
"Uninstall":"Uninstall",
"Unknown API setting for {0}":"Unknown API setting for {0}",
"Updated: {0}":"Updated: {0}",
"Upload":"Upload",
"Url":"Url",
"User abort the authentication":"User abort the authentication",
"User data updated":"User data updated",
"VDB Unknown condition for delete command":"VDB Unknown condition for delete command",
"VFS Cannot encode file: {0}":"VFS Cannot encode file: {0}",
"VFS cannot create : {0}":"VFS cannot create : {0}",
"VFS cannot delete : {0}":"VFS cannot delete : {0}",
"VFS cannot download file : {0}":"VFS cannot download file : {0}",
"VFS cannot get meta data for {0}":"VFS cannot get meta data for {0}",
"VFS cannot init {0}: {1}":"VFS cannot init {0}: {1}",
"VFS cannot move : {0}":"VFS cannot move : {0}",
"VFS cannot read : {0}":"VFS cannot read : {0}",
"VFS cannot save : {0}":"VFS cannot save : {0}",
"VFS cannot write : {0}":"VFS cannot write : {0}",
"VFS unknown action: {0}":"VFS unknown action: {0}",
"VFS unknown handler: {0}":"VFS unknown handler: {0}",
"View":"View",
"Wed":"Wed",
"Would you like to login to {0}?":"Would you like to login to {0}?",
"Wrong format: it should be [name] url":"Wrong format: it should be [name] url",
"Yes":"Yes",
"{0} is not a directory":"{0} is not a directory"
}

View File

@ -20,7 +20,7 @@
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
grep --include=\*.coffee -roh "$1" -e '__("[^"]*"' | while read -r line ; do
grep --include=\*.ts -roh "$1" -e '__("[^"]*"' | while read -r line ; do
SUBSTRING=$(echo $line| cut -d'"' -f 2)
if test -f "$2" && [ ! -z "$(grep -F "\"$SUBSTRING\":" "$2")" ]
then
@ -29,7 +29,7 @@ grep --include=\*.coffee -roh "$1" -e '__("[^"]*"' | while read -r line ; do
echo -e "\t\"$SUBSTRING\":\"$SUBSTRING\"," >> "tmp.json"
fi
done
grep --include=\*.{coffee,html} -roh "$1" -e '\"__([^\"]*)\"' | while read -r line; do
grep --include=\*.{ts,html} -roh "$1" -e '\"__([^\"]*)\"' | while read -r line; do
len=$(( ${#line} - 6 ))
#echo $len
#echo $line

View File

@ -1244,10 +1244,10 @@ namespace OS {
* only work with file
*
* @protected
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof RemoteFileHandle
*/
protected _down(): Promise<any> {
protected _down(): Promise<void> {
return new Promise((resolve, reject) => {
if (this.info.type === "dir") {
return API.throwe(
@ -1527,10 +1527,10 @@ namespace OS {
* Download the buffer file
*
* @protected
* @returns {Promise<RequestResult>}
* @returns {Promise<void>}
* @memberof BufferFileHandle
*/
protected _down(): Promise<RequestResult> {
protected _down(): Promise<void> {
return new Promise((resolve, reject) => {
const blob = new Blob([this.cache], {
type: "octet/stream",
@ -1714,10 +1714,10 @@ namespace OS {
* Download shared file
*
* @protected
* @returns {Promise<RequestResult>}
* @returns {Promise<void>}
* @memberof SharedFileHandle
*/
protected _down(): Promise<RequestResult> {
protected _down(): Promise<void> {
return new Promise((resolve, reject) => {
if (this.info.type === "dir") {
return reject(

View File

@ -0,0 +1,139 @@
var ace: any;
namespace OS {
export namespace application {
export class CodePadACEModel extends CodePadBaseEditorModel {
private modes: GenericObject<any>;
constructor(app: CodePad, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) {
ace.config.set("basePath", "scripts/ace");
ace.require("ace/ext/language_tools");
super(app,tabbar,editorarea);
this.modes = ace.require("ace/ext/modelist");
}
getModes(): GenericObject<any>[] {
const list = [];
let v: GenericObject<any>;
for (v of Array.from(this.modes.modes)) {
list.push({ text: v.caption, mode: v.mode });
}
return list;
}
setTheme(theme: string): void {
this.editor.setTheme(theme);
}
protected setUndoManager(um: GenericObject<any>): void {
this.editor.getSession().setUndoManager(um);
}
protected setCursor(c: GenericObject<any>): void {
this.editor.renderer.scrollCursorIntoView(
{
row: c.row,
column: c.column,
},
0.5
);
this.editor.selection.moveTo(
c.row,
c.column
);
}
setMode(m: GenericObject<any>): void {
this.currfile.langmode = m;
this.editor.getSession().setMode(m.mode);
}
protected getCursor(): GenericObject<any> {
return this.editor.getCursorPosition();
}
protected newUndoManager(): GenericObject<any> {
return new ace.UndoManager();
}
/**
* Reference to the editor instance
*
* @protected
* @type {GenericObject<any>}
* @memberof CodePad
*/
protected editor: GenericObject<any>;
protected editorSetup(el: HTMLElement): void {
this.editor = ace.edit(el);
this.editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
highlightActiveLine: true,
highlightSelectedWord: true,
behavioursEnabled: true,
wrap: true,
fontSize: "10pt",
showInvisibles: true,
});
this.editor.setTheme("ace/theme/monokai");
this.editor.completers.push({
getCompletions(
editor: any,
session: any,
pos: any,
prefix: any,
callback: any
) { },
});
this.editor.getSession().setUseWrapMode(true);
}
on(evt_str: string, callback: () => void): void {
switch (evt_str) {
case "input":
case "focus":
this.editor.on(evt_str, callback);
break;
case "changeCursor":
this.editor
.getSession()
.selection.on(evt_str, callback);
break;
default:
break;
}
}
resize(): void {
this.editor.resize();
}
focus(): void {
this.editor.focus();
}
protected getModeForPath(path: string): GenericObject<any> {
const m = this.modes.getModeForPath(path);
return {
text: m.caption,
mode: m.mode
}
}
getEditorStatus(): GenericObject<any> {
const c = this.editor.session.selection.getCursor();
const l = this.editor.session.getLength();
return {
row: c.row,
column: c.column,
line: l,
langmode: this.currfile.langmode,
file: this.currfile.path
}
}
getValue(): string {
return this.editor.getValue();
}
setValue(value: string): void {
this.editor.setValue(value, -1);
}
}
}
}

View File

@ -153,7 +153,7 @@ namespace OS {
await this.mkfileAll(files, path, name);
this.app.currdir = rpath.asFileHandle();
this.app.toggleSideBar();
return this.app.openFile(
return this.app.eum.active.openFile(
`${rpath}/README.md`.asFileHandle() as application.CodePadFileHandle
);
} catch (e) {
@ -173,10 +173,10 @@ namespace OS {
*
* @private
* @param {string[]} list
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof AntOSDK
*/
private verify(list: string[]): Promise<any> {
private verify(list: string[]): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
@ -242,10 +242,10 @@ namespace OS {
* @private
* @param {GenericObject<any>} meta
* @param {boolean} debug
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof AntOSDK
*/
private build(meta: GenericObject<any>, debug: boolean): Promise<any> {
private build(meta: GenericObject<any>, debug: boolean): Promise<void> {
const dirs = [
`${meta.root}/build`,
`${meta.root}/build/debug`,
@ -268,7 +268,7 @@ namespace OS {
})(),
src
);
await new Promise(async function (r, e) {
await new Promise<void>(async function (r, e) {
let code = jsrc;
if (!debug) {
const options = {
@ -306,7 +306,7 @@ namespace OS {
return e(__e(ex));
}
});
await new Promise(async (r, e) => {
await new Promise<void>(async (r, e) => {
const txt = await this.cat(
(() => {
const result1 = [];

View File

@ -0,0 +1,339 @@
namespace OS {
export namespace application {
export abstract class CodePadBaseEditorModel {
/**
* Reference to the current editing file handle
*
* @protected
* @type {CodePadFileHandle}
* @memberof CodePad
*/
protected currfile: CodePadFileHandle;
private app: CodePad;
/**
* Reference to the editor tab bar UI
*
* @private
* @type {GUI.tag.TabBarTag}
* @memberof CodePad
*/
private tabbar: GUI.tag.TabBarTag;
private container: HTMLElement;
onstatuschange: (stat: GenericObject<any>) => void;
/**
* Editor mutex
*
* @private
* @type {boolean}
* @memberof CodePad
*/
private editormux: boolean;
constructor(app: CodePad, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) {
this.container = editorarea;
this.currfile = "Untitled".asFileHandle() as CodePadFileHandle;
this.tabbar = tabbar;
this.editorSetup(editorarea);
this.app = app;
this.editormux = false;
this.onstatuschange = undefined;
this.on("focus", () =>{
if(this.onstatuschange)
this.onstatuschange(this.getEditorStatus());
});
this.on("input", () => {
if (this.editormux) {
this.editormux = false;
return false;
}
if (!this.currfile.dirty) {
this.currfile.dirty = true;
this.currfile.text += "*";
return this.tabbar.update(undefined);
}
});
this.on("changeCursor", () => {
if (this.onstatuschange)
this.onstatuschange(this.getEditorStatus());
});
this.tabbar.ontabselect = (e) => {
return this.selecteTab($(e.data.item).index());
};
this.tabbar.ontabclose = (e) => {
const it = e.data.item;
if (!it) {
return false;
}
if (!it.data.dirty) {
return this.closeTab(it);
}
this.app.openDialog("YesNoDialog", {
title: __("Close tab"),
text: __("Close without saving ?"),
}).then((d) => {
if (d) {
return this.closeTab(it);
}
return this.focus();
});
return false;
};
}
/**
* Find a tab on the tabbar corresponding to a file handle
*
* @private
* @param {CodePadFileHandle} file then file handle to search
* @returns {number}
* @memberof CodePad
*/
private findTabByFile(file: CodePadFileHandle): number {
const lst = this.tabbar.items;
const its = (() => {
const result = [];
for (let i = 0; i < lst.length; i++) {
const d = lst[i];
if (d.hash() === file.hash()) {
result.push(i);
}
}
return result;
})();
if (its.length === 0) {
return -1;
}
return its[0];
}
/**
* Create new tab when opening a file
*
* @private
* @param {CodePadFileHandle} file
* @memberof CodePad
*/
private newTab(file: CodePadFileHandle): void {
file.text = file.basename ? file.basename : file.path;
if (!file.cache) {
file.cache = "";
}
file.um = this.newUndoManager();
this.currfile.selected = false;
file.selected = true;
//console.log cnt
this.tabbar.push(file);
}
/**
* Close a tab when a file is closed
*
* @private
* @param {GUI.tag.ListViewItemTag} it reference to the tab to close
* @returns {boolean}
* @memberof CodePad
*/
private closeTab(it: GUI.tag.ListViewItemTag): boolean {
this.tabbar.delete(it);
const cnt = this.tabbar.items.length;
if (cnt === 0) {
this.openFile(
"Untitled".asFileHandle() as CodePadFileHandle
);
return false;
}
this.tabbar.selected = cnt - 1;
return false;
}
/**
* Select a tab by its index
*
* @private
* @param {number} i tab index
* @returns {void}
* @memberof CodePad
*/
private selecteTab(i: number): void {
//return if i is @tabbar.get "selidx"
const file = this.tabbar.items[i] as CodePadFileHandle;
if (!file) {
return;
}
//return if file is @currfile
if (this.currfile !== file) {
this.currfile.cache = this.getValue();
this.currfile.cursor = this.getCursor();
this.currfile.selected = false;
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.setUndoManager(this.newUndoManager());
this.setValue(file.cache);
this.setMode(file.langmode);
if (file.cursor) {
this.setCursor(file.cursor);
}
this.setUndoManager(file.um);
if (this.onstatuschange)
this.onstatuschange(this.getEditorStatus());
this.focus();
}
selectFile(file: CodePadFileHandle | string): void {
const i = this.findTabByFile(
file.asFileHandle() as CodePadFileHandle
);
if (i !== -1) {
this.tabbar.selected = i;
}
}
/**
* Open a file in new tab. If the file is already opened,
* the just select the tab
*
*
* @param {CodePadFileHandle} file file to open
* @returns {void}
* @memberof CodePad
*/
openFile(file: CodePadFileHandle): void {
//find tab
const i = this.findTabByFile(file);
if (i !== -1) {
this.tabbar.selected = i;
return;
}
if (file.path.toString() === "Untitled") {
this.newTab(file);
return;
}
file.read()
.then((d) => {
file.cache = d || "";
return this.newTab(file);
})
.catch((e) => {
return this.app.error(
__("Unable to open: {0}", file.path),
e
);
});
}
/**
* write a file
*
* @private
* @param {CodePadFileHandle} file
* @memberof CodePad
*/
private write(file: CodePadFileHandle): void {
this.currfile.cache = this.getValue();
file.write("text/plain")
.then((d) => {
file.dirty = false;
file.text = file.basename;
this.tabbar.update(undefined);
})
.catch((e) =>
this.app.error(__("Unable to save file: {0}", file.path), e)
);
}
save(): void {
this.currfile.cache = this.getValue();
if (this.currfile.basename) {
return this.write(this.currfile);
}
return this.saveAs();
}
/**
* Save the current file as another file
*
* @public
* @memberof CodePad
*/
saveAs(): void {
this.app.openDialog("FileDialog", {
title: __("Save as"),
file: this.currfile,
}).then((f) => {
let d = f.file.path.asFileHandle();
if (f.file.type === "file") {
d = d.parent();
}
this.currfile.setPath(`${d.path}/${f.name}`);
this.write(this.currfile);
});
}
dirties(): CodePadFileHandle[] {
const result = [];
for (let v of Array.from(this.tabbar.items)) {
if (v.dirty) {
result.push(v);
}
}
return result;
}
set contextmenuHandle(cb:(e: any,m: any)=>void)
{
this.container.contextmenuHandle = cb;
}
closeAll(): void
{
this.tabbar.items = [];
this.setValue("");
this.setUndoManager(this.newUndoManager());
}
isDirty(): boolean
{
return this.dirties().length > 0;
}
protected abstract editorSetup(el: HTMLElement): void;
abstract on(evt_str: string, callback: () => void): void;
abstract resize(): void;
abstract focus(): void;
protected abstract getModeForPath(path: string): GenericObject<any>;
abstract getEditorStatus(): GenericObject<any>;
abstract getValue(): string;
abstract setValue(value: string): void;
protected abstract getCursor(): GenericObject<any>;
protected abstract newUndoManager(): GenericObject<any>;
protected abstract setUndoManager(um: GenericObject<any>): void;
abstract setMode(m: GenericObject<any>): void;
protected abstract setCursor(c: GenericObject<any>): void;
abstract setTheme(theme: string): void;
abstract getModes(): GenericObject<any>[];
}
}
}

View File

@ -137,8 +137,8 @@ namespace OS {
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected copy(files: string[], to: string): Promise<any> {
return new Promise((resolve, reject) => {
protected copy(files: string[], to: string): Promise<void> {
return new Promise((resolve, reject) =>{
if (files.length === 0) {
return resolve();
}
@ -287,7 +287,7 @@ namespace OS {
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected mkar(src: string, dest: string): Promise<any> {
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) => {
@ -352,7 +352,7 @@ namespace OS {
* @returns {Promise<any>}
* @memberof BaseExtension
*/
protected mkdirAll(list: string[]): Promise<any> {
protected mkdirAll(list: string[]): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
@ -388,7 +388,7 @@ namespace OS {
list: Array<string[]>,
path: string,
name: string
): Promise<any> {
): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();

View File

@ -135,7 +135,7 @@ namespace OS {
await this.mkfileAll(files, path, name);
this.app.currdir = rpath.asFileHandle();
this.app.toggleSideBar();
return this.app.openFile(
return this.app.eum.active.openFile(
`${rpath}/${name}.coffee`.asFileHandle() as application.CodePadFileHandle
);
} catch (e) {
@ -155,10 +155,10 @@ namespace OS {
*
* @private
* @param {string[]} list
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof ExtensionMaker
*/
private verify(list: string[]): Promise<any> {
private verify(list: string[]): Promise<void> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve();
@ -220,10 +220,10 @@ namespace OS {
*
* @private
* @param {GenericObject<any>} meta
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof ExtensionMaker
*/
private build(meta: GenericObject<any>): Promise<any> {
private build(meta: GenericObject<any>): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
const src = await this.compile(meta);
@ -239,7 +239,7 @@ namespace OS {
})(),
src
);
await new Promise((r, e) =>
await new Promise<void>((r, e) =>
`${meta.root}/build/debug/${meta.meta.name}.js`
.asFileHandle()
.setCache(jsrc)
@ -280,10 +280,10 @@ namespace OS {
*
* @private
* @param {GenericObject<any>} meta
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof ExtensionMaker
*/
private run(meta: GenericObject<any>): Promise<any> {
private run(meta: GenericObject<any>): Promise<void> {
return new Promise(async (resolve, reject) => {
const path = `${meta.root}/build/debug/${meta.meta.name}.js`;
if (API.shared[path]) {
@ -331,10 +331,10 @@ namespace OS {
* @private
* @param {string[]} files
* @param {*} zip
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof ExtensionMaker
*/
private installExtension(files: string[], zip: any): Promise<any> {
private installExtension(files: string[], zip: any): Promise<void> {
return new Promise((resolve, reject) => {
const idx = files.indexOf("extension.json");
if (idx < 0) {
@ -370,8 +370,8 @@ namespace OS {
private installFiles(
files: string[],
zip: any,
meta: GenericObject<any>
): Promise<any> {
meta: GenericObject<void>
): Promise<void> {
if (files.length === 0) {
return this.installMeta(meta);
}
@ -405,10 +405,10 @@ namespace OS {
*
* @private
* @param {GenericObject<any>} meta
* @returns {Promise<any>}
* @returns {Promise<void>}
* @memberof ExtensionMaker
*/
private installMeta(meta: GenericObject<any>): Promise<any> {
private installMeta(meta: GenericObject<any>): Promise<void> {
return new Promise(async (resolve, reject) => {
const file = `${this.app.meta().path
}/extensions.json`.asFileHandle();
@ -443,7 +443,7 @@ namespace OS {
* @returns {Promise<any>}
* @memberof ExtensionMaker
*/
private installZip(path: string): Promise<any> {
private installZip(path: string): Promise<void> {
return new Promise((resolve, reject) => {
this.import(["os://scripts/jszip.min.js"])
.then(() => {

View File

@ -1,4 +1,4 @@
module_files = main.js BaseExtension.js
module_files = main.js BaseExtension.js BaseEditorModel.js ACEModel.js
libfiles =

View File

@ -8,8 +8,17 @@
</afx-vbox>
<afx-resizer data-width = "3" ></afx-resizer>
<afx-vbox>
<afx-tab-bar closable="true" data-height="26" data-id = "tabbar"></afx-tab-bar>
<div data-id="datarea"></div>
<afx-hbox>
<afx-vbox data-id="left-panel">
<afx-tab-bar closable="true" data-height="26" data-id = "left-tabbar"></afx-tab-bar>
<div data-id="left-editorarea"></div>
</afx-vbox>
<afx-resizer data-width="3"></afx-resizer>
<afx-vbox data-id="right-panel">
<afx-tab-bar closable="true" data-height="26" data-id = "right-tabbar"></afx-tab-bar>
<div data-id="right-editorarea"></div>
</afx-vbox>
</afx-hbox>
<afx-resizer data-height = "3" dir = "ve" attachnext = "true" ></afx-resizer>
<afx-tab-container data-id = "bottombar" data-height="150" min-height="150" tabbarheight= "22">
<afx-hbox tabname="__(Output)" iconclass = "fa fa-file-text" class = "bottom-tab-content">
@ -21,9 +30,9 @@
</afx-vbox>
</afx-hbox>
<div data-height="20" data-id="statctn">
<afx-label text=" " ></afx-label>
<afx-label data-id="langstat" ></afx-label>
<afx-label data-id="editorstat" ></afx-label>
<afx-label text=" " data-id = "current-file-lbl" style="float:left;"></afx-label>
<afx-label data-id="langstat" style="float:right; padding-right: 10px;"></afx-label>
<afx-label data-id="editorstat" style="float:right;"></afx-label>
</div>
</afx-vbox>
</afx-app-window>

View File

@ -91,7 +91,6 @@ afx-app-window[data-id = "codepad"] .afx-window-wrapper div[data-id="statctn"]{
}
afx-app-window[data-id = "codepad"] .afx-window-wrapper div[data-id="statctn"] afx-label {
float: right;
padding-left: 10px;
}
@ -161,6 +160,10 @@ afx-app-window[data-id = "codepad"] div[data-id="output-tab"] pre.code-pad-log-w
color: orange;
}
afx-app-window[data-id = "codepad"] div[data-id="output-tab"] pre.code-pad-log-info {
color: white;
}
afx-app-window[data-id = "codepad"] afx-button[ data-id="logger-clear" ] button{
border: 0;
background: transparent;

View File

@ -1,4 +1,3 @@
var ace: any;
namespace OS {
export namespace application {
/**
@ -69,14 +68,16 @@ namespace OS {
* @extends {BaseApplication}
*/
export class CodePad extends BaseApplication {
/**
* Reference to the current editing file handle
* Reference to the editor manager instance
*
* @private
* @type {CodePadFileHandle}
* @type {EditorModelManager}
* @memberof CodePad
*/
private currfile: CodePadFileHandle;
eum: EditorModelManager;
/**
* Reference to the current working directory
@ -121,14 +122,7 @@ namespace OS {
* @memberof CodePad
*/
private bottombar: GUI.tag.TabContainerTag;
/**
* Reference to the editor tab bar UI
*
* @private
* @type {GUI.tag.TabBarTag}
* @memberof CodePad
*/
private tabbar: GUI.tag.TabBarTag;
/**
* Reference to the language status bar
@ -149,31 +143,13 @@ namespace OS {
private editorstat: GUI.tag.LabelTag;
/**
* Reference to the editor instance
* Reference to the file status bar
*
* @private
* @type {GenericObject<any>}
* @type {GUI.tag.LabelTag}
* @memberof CodePad
*/
private editor: GenericObject<any>;
/**
* Editor language modes
*
* @private
* @type {GenericObject<any>}
* @memberof CodePad
*/
private modes: GenericObject<any>;
/**
* Editor mutex
*
* @private
* @type {boolean}
* @memberof CodePad
*/
private editormux: boolean;
private filestat: GUI.tag.LabelTag;
/**
* Reference to the CommandPalette's spotlight
@ -184,6 +160,15 @@ namespace OS {
spotlight: CMDMenu;
/**
* Is the split mode enabled
*
* @private
* @type {boolean}
* @memberof CodePad
*/
private split_mode: boolean;
/**
* Reference to the editor logger
*
@ -243,16 +228,7 @@ namespace OS {
*/
constructor(args: AppArgumentsType[]) {
super("CodePad", args);
this.currfile = "Untitled".asFileHandle() as CodePadFileHandle;
this.currdir = undefined;
if (this.args && this.args.length > 0) {
if (this.args[0].type === "dir") {
this.currdir = this.args[0].path.asFileHandle() as CodePadFileHandle;
} else {
this.currfile = this.args[0].path.asFileHandle() as CodePadFileHandle;
this.currdir = this.currfile.parent();
}
}
}
/**
@ -263,13 +239,31 @@ namespace OS {
*/
main(): void {
this.extensions = {};
this.eum = new EditorModelManager();
this.fileview = this.find("fileview") as GUI.tag.FileViewTag;
this.sidebar = this.find("sidebar") as GUI.tag.VBoxTag;
this.bottombar = this.find("bottombar") as GUI.tag.TabContainerTag;
this.tabbar = this.find("tabbar") as GUI.tag.TabBarTag;
this.langstat = this.find("langstat") as GUI.tag.LabelTag;
this.editorstat = this.find("editorstat") as GUI.tag.LabelTag;
this.filestat = this.find("current-file-lbl") as GUI.tag.LabelTag;
this.logger = new Logger(this.find("output-tab"));
this.split_mode = true;
// add editor instance
this.eum
.add(new CodePadACEModel(
this,
this.find("left-tabbar") as GUI.tag.TabBarTag,
this.find("left-editorarea")) as CodePadBaseEditorModel)
.add(new CodePadACEModel(
this,
this.find("right-tabbar") as GUI.tag.TabBarTag,
this.find("right-editorarea")) as CodePadBaseEditorModel);
this.eum.onstatuschange = (st) =>
this.updateStatus(st)
this.fileview.fetch = (path) =>
new Promise(async function (resolve, reject) {
let dir: API.VFS.BaseFileHandle;
@ -289,7 +283,17 @@ namespace OS {
return reject(__e(e));
}
});
return this.setup();
let file = "Untitled".asFileHandle() as CodePadFileHandle;
if (this.args && this.args.length > 0) {
if (this.args[0].type === "dir") {
this.currdir = this.args[0].path.asFileHandle() as CodePadFileHandle;
} else {
file = this.args[0].path.asFileHandle() as CodePadFileHandle;
this.currdir = file.parent();
}
}
this.setup();
return this.eum.active.openFile(file);
}
/**
@ -300,73 +304,6 @@ namespace OS {
* @memberof CodePad
*/
private setup(): void {
ace.config.set("basePath", "scripts/ace");
ace.require("ace/ext/language_tools");
this.editor = ace.edit(this.find("datarea"));
this.editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
highlightActiveLine: true,
highlightSelectedWord: true,
behavioursEnabled: true,
wrap: true,
fontSize: "10pt",
showInvisibles: true,
});
//themes = ace.require "ace/ext/themelist"
this.editor.setTheme("ace/theme/monokai");
this.modes = ace.require("ace/ext/modelist");
this.editor.completers.push({
getCompletions(
editor: any,
session: any,
pos: any,
prefix: any,
callback: any
) { },
});
this.editor.getSession().setUseWrapMode(true);
this.editormux = false;
this.editor.on("input", () => {
if (this.editormux) {
this.editormux = false;
return false;
}
if (!this.currfile.dirty) {
this.currfile.dirty = true;
this.currfile.text += "*";
return this.tabbar.update(undefined);
}
});
this.editor
.getSession()
.selection.on("changeCursor", (e: any) => {
return this.updateStatus();
});
this.tabbar.ontabselect = (e) => {
return this.selecteTab($(e.data.item).index());
};
this.tabbar.ontabclose = (e) => {
const it = e.data.item;
if (!it) {
return false;
}
if (!it.data.dirty) {
return this.closeTab(it);
}
this.openDialog("YesNoDialog", {
title: __("Close tab"),
text: __("Close without saving ?"),
}).then((d) => {
if (d) {
return this.closeTab(it);
}
return this.editor.focus();
});
return false;
};
this.fileview.onfileopen = (e) => {
if (!e.data || !e.data.path) {
return;
@ -374,7 +311,7 @@ namespace OS {
if (e.data.type === "dir") {
return;
}
return this.openFile(
return this.eum.active.openFile(
e.data.path.asFileHandle() as CodePadFileHandle
);
};
@ -386,19 +323,14 @@ namespace OS {
if (e.data.type === "dir") {
return;
}
const i = this.findTabByFile(
e.data.path.asFileHandle() as CodePadFileHandle
);
if (i !== -1) {
return (this.tabbar.selected = i);
}
this.eum.active.selectFile(e.data.path);
};
this.on("resize", () => this.editor.resize());
this.on("focus", () => this.editor.focus());
this.on("resize", () => this.eum.resize());
this.on("focus", () => this.eum.active.focus());
this.spotlight = new CMDMenu(__("Command palette"));
this.bindKey("ALT-P", () => this.spotlight.run(this));
this.find("datarea").contextmenuHandle = (e, m) => {
this.eum.contextmenuHandle = (e, m) => {
m.items = [
{
text: __("Command palete"),
@ -477,166 +409,8 @@ namespace OS {
this.loadExtensionMetaData();
this.initCommandPalete();
this.toggleSideBar();
this.toggleSplitMode();
this.applyAllSetting();
return this.openFile(this.currfile);
}
/**
* Open a file in new tab. If the file is already opened,
* the just select the tab
*
*
* @param {CodePadFileHandle} file file to open
* @returns {void}
* @memberof CodePad
*/
openFile(file: CodePadFileHandle): void {
//find tab
const i = this.findTabByFile(file);
if (i !== -1) {
this.tabbar.selected = i;
return;
}
if (file.path.toString() === "Untitled") {
this.newTab(file);
return;
}
file.read()
.then((d) => {
file.cache = d || "";
return this.newTab(file);
})
.catch((e) => {
return this.error(
__("Unable to open: {0}", file.path),
e
);
});
}
/**
* Find a tab on the tabbar corresponding to a file handle
*
* @private
* @param {CodePadFileHandle} file then file handle to search
* @returns {number}
* @memberof CodePad
*/
private findTabByFile(file: CodePadFileHandle): number {
const lst = this.tabbar.items;
const its = (() => {
const result = [];
for (let i = 0; i < lst.length; i++) {
const d = lst[i];
if (d.hash() === file.hash()) {
result.push(i);
}
}
return result;
})();
if (its.length === 0) {
return -1;
}
return its[0];
}
/**
* Create new tab when opening a file
*
* @private
* @param {CodePadFileHandle} file
* @memberof CodePad
*/
private newTab(file: CodePadFileHandle): void {
file.text = file.basename ? file.basename : file.path;
if (!file.cache) {
file.cache = "";
}
file.um = new ace.UndoManager();
this.currfile.selected = false;
file.selected = true;
//console.log cnt
this.tabbar.push(file);
}
/**
* Close a tab when a file is closed
*
* @private
* @param {GUI.tag.ListViewItemTag} it reference to the tab to close
* @returns {boolean}
* @memberof CodePad
*/
private closeTab(it: GUI.tag.ListViewItemTag): boolean {
this.tabbar.delete(it);
const cnt = this.tabbar.items.length;
if (cnt === 0) {
this.openFile(
"Untitled".asFileHandle() as CodePadFileHandle
);
return false;
}
this.tabbar.selected = cnt - 1;
return false;
}
/**
* Select a tab by its index
*
* @private
* @param {number} i tab index
* @returns {void}
* @memberof CodePad
*/
private selecteTab(i: number): void {
//return if i is @tabbar.get "selidx"
const file = this.tabbar.items[i] as CodePadFileHandle;
if (!file) {
return;
}
(this
.scheme as GUI.tag.WindowTag).apptitle = file.text.toString();
//return if file is @currfile
if (this.currfile !== file) {
this.currfile.cache = this.editor.getValue();
this.currfile.cursor = this.editor.selection.getCursor();
this.currfile.selected = false;
this.currfile = file;
}
if (!file.langmode) {
if (file.path.toString() !== "Untitled") {
const m = this.modes.getModeForPath(file.path);
file.langmode = { caption: m.caption, mode: m.mode };
} else {
file.langmode = {
caption: "Text",
mode: "ace/mode/text",
};
}
}
this.editormux = true;
this.editor.getSession().setUndoManager(new ace.UndoManager());
this.editor.setValue(file.cache, -1);
this.editor.getSession().setMode(file.langmode.mode);
if (file.cursor) {
this.editor.renderer.scrollCursorIntoView(
{
row: file.cursor.row,
column: file.cursor.column,
},
0.5
);
this.editor.selection.moveTo(
file.cursor.row,
file.cursor.column
);
}
this.editor.getSession().setUndoManager(file.um);
this.updateStatus();
this.editor.focus();
}
/**
@ -645,16 +419,17 @@ namespace OS {
* @private
* @memberof CodePad
*/
private updateStatus(): void {
const c = this.editor.session.selection.getCursor();
const l = this.editor.session.getLength();
private updateStatus(stat: GenericObject<any> = undefined): void {
if (!stat)
stat = this.eum.active.getEditorStatus();
this.editorstat.text = __(
"Row {0}, col {1}, lines: {2}",
c.row + 1,
c.column + 1,
l
stat.row + 1,
stat.column + 1,
stat.line
);
this.langstat.text = this.currfile.langmode.caption;
this.langstat.text = stat.langmode.text;
this.filestat.text = stat.file
}
/**
@ -713,6 +488,33 @@ namespace OS {
this.showBottomBar(!this.setting.showBottomBar);
}
private toggleSplitMode():void {
const right_pannel = this.find("right-panel");
const right_editor = this.eum.editors[1];
const left_editor = this.eum.editors[0];
if(this.split_mode)
{
// before hide check if there is dirty files
if(right_editor.isDirty())
{
this.notify(__("Unable to disable split view: Please save changes of modified files on the right panel"));
return;
}
right_editor.closeAll();
$(right_pannel).hide();
this.split_mode = false;
left_editor.focus();
}
else
{
$(right_pannel).show();
this.split_mode = true;
right_editor.openFile("Untitled".asFileHandle() as CodePadFileHandle);
right_editor.focus();
}
this.trigger("resize");
}
/**
* Add an action to the [[CommandPalette]]'s spotlight
*
@ -758,26 +560,22 @@ namespace OS {
r: CodePad
) {
const data = d.data.item.data;
r.editor.setTheme(data.theme);
return r.editor.focus();
r.eum.active.setTheme(data.theme);
return r.eum.active.focus();
});
this.spotlight.addAction(cmdtheme);
const cmdmode = new CMDMenu(__("Change language mode"));
for (v of Array.from(this.modes.modes)) {
cmdmode.addAction({ text: v.caption, mode: v.mode });
for (v of Array.from(this.eum.active.getModes())) {
cmdmode.addAction({ text: v.text, mode: v.mode });
}
cmdmode.onchildselect(function (
d: GUI.TagEventType<GUI.tag.ListItemEventData>,
r: CodePad
) {
const data = d.data.item.data;
r.editor.session.setMode(data.mode);
r.currfile.langmode = {
caption: data.text,
mode: data.mode,
};
r.eum.active.setMode(data);
r.updateStatus();
r.editor.focus();
r.eum.active.focus();
});
this.spotlight.addAction(cmdmode);
this.addAction(CMDMenu.fromMenu(this.fileMenu()));
@ -1074,46 +872,9 @@ namespace OS {
}
}
/**
* Save a file
*
* @private
* @param {CodePadFileHandle} file
* @memberof CodePad
*/
private save(file: CodePadFileHandle): void {
file.write("text/plain")
.then((d) => {
file.dirty = false;
file.text = file.basename;
this.tabbar.update(undefined);
(this
.scheme as GUI.tag.WindowTag).apptitle = `${this.currfile.basename}`;
})
.catch((e) =>
this.error(__("Unable to save file: {0}", file.path), e)
);
}
/**
* Save the current file as another file
*
* @private
* @memberof CodePad
*/
private saveAs(): void {
this.openDialog("FileDialog", {
title: __("Save as"),
file: this.currfile,
}).then((f) => {
let d = f.file.path.asFileHandle();
if (f.file.type === "file") {
d = d.parent();
}
this.currfile.setPath(`${d.path}/${f.name}`);
this.save(this.currfile);
});
}
/**
* Menu action definition
@ -1131,7 +892,7 @@ namespace OS {
}
switch (dataid) {
case "new":
return me.openFile("Untitled".asFileHandle() as CodePadFileHandle);
return me.eum.active.openFile("Untitled".asFileHandle() as CodePadFileHandle);
case "open":
return me
.openDialog("FileDialog", {
@ -1141,7 +902,7 @@ namespace OS {
),
})
.then((f: API.FileInfoType) =>
me.openFile(f.file.path.asFileHandle())
me.eum.active.openFile(f.file.path.asFileHandle())
);
case "opendir":
return me
@ -1154,14 +915,10 @@ namespace OS {
return me.toggleSideBar();
});
case "save":
me.currfile.cache = me.editor.getValue();
if (me.currfile.basename) {
return me.save(me.currfile);
}
return me.saveAs();
return me.eum.active.save();
case "saveas":
me.currfile.cache = me.editor.getValue();
return me.saveAs();
return me.eum.active.saveAs();
default:
return console.log(dataid);
}
@ -1176,15 +933,7 @@ namespace OS {
*/
cleanup(evt: BaseEvent): void {
let v: GenericObject<any>;
const dirties = (() => {
const result = [];
for (v of Array.from(this.tabbar.items)) {
if (v.dirty) {
result.push(v);
}
}
return result;
})();
const dirties = this.eum.dirties();
if (dirties.length === 0) {
return;
}
@ -1231,6 +980,10 @@ namespace OS {
{
text: "__(Toggle bottom bar)",
dataid: "bottombar"
},
{
text: "__(Toggle split view)",
dataid: "splitview"
}
],
onchildselect: (
@ -1243,6 +996,10 @@ namespace OS {
case "bottombar":
return this.toggleBottomBar();
case "splitview":
return this.toggleSplitMode();
break;
default:
break;
@ -1355,7 +1112,102 @@ namespace OS {
return m;
};
/**
* Helper class to manager several instances
* of editor models
*
* @class EditorModelManager
*/
class EditorModelManager {
/**
* Referent to the active editor model
*
* @private
* @type {CodePadBaseEditorModel}
* @memberof EditorModelManager
*/
private active_editor: CodePadBaseEditorModel;
/**
* Store a list of editor models
*
* @private
* @type {CodePadBaseEditorModel[]}
* @memberof EditorModelManager
*/
private models: CodePadBaseEditorModel[];
/**
* Creates an instance of EditorModelManager.
* @memberof EditorModelManager
*/
constructor() {
this.active_editor = undefined;
this.models = [];
}
get editors(): CodePadBaseEditorModel[]{
return this.models;
}
set contextmenuHandle(cb: (e: any, m: any) => void) {
for (let ed of this.models) {
ed.contextmenuHandle = cb;
}
}
/**
* Get the active editor model
*
* @readonly
* @type {CodePadBaseEditorModel}
* @memberof EditorModelManager
*/
get active(): CodePadBaseEditorModel {
return this.active_editor;
}
/**
* Add a model to the manager
*
* @param {CodePadBaseEditorModel} model
* @memberof EditorModelManager
*/
add(model: CodePadBaseEditorModel): EditorModelManager {
this.models.push(model);
if (!this.active_editor)
this.active_editor = model;
model.on("focus", () => {
this.active_editor = model;
});
return this;
}
set onstatuschange(cb: (stat: GenericObject<any>) => void) {
for (let ed of this.models) {
ed.onstatuschange = cb;
}
}
dirties(): CodePadFileHandle[] {
let list = [];
for (let ed of this.models) {
list = list.concat(ed.dirties());
}
return list;
}
/**
* Resize all editor
*
* @memberof EditorModelManager
*/
resize(): void {
for (let ed of this.models) {
ed.resize();
}
}
}
/**
* This class handles log output to the Editor output container
*

View File

@ -7,7 +7,7 @@
"email": "xsang.le@gmail.com",
"licences": "GPLv3"
},
"version":"0.0.3-b",
"version":"0.1.1-b",
"category":"Developments",
"iconclass":"fa fa-pencil-square-o",
"mimes":[