first release of libantosdk

This commit is contained in:
mrsang
2021-04-18 18:49:53 +02:00
parent 211753bfdc
commit 90864aabb7
61 changed files with 154246 additions and 27 deletions

View File

@ -1,3 +1,38 @@
<afx-app-window apptitle="Antedit" width="500" height="400" data-id="Antedit">
<afx-hbox ></afx-hbox>
<afx-app-window apptitle="Antos Editor" width="600" height="400" data-id="antedit">
<afx-vbox>
<afx-hbox data-id="wrapper">
<afx-vbox data-width = "155" min-width="155" data-id = "sidebar">
<div data-height="10"></div>
<afx-file-view chdir="false" data-id = "fileview" view="tree" status = "false">
</afx-file-view>
</afx-vbox>
<afx-resizer data-width = "3" ></afx-resizer>
<afx-vbox>
<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">
<afx-button text = "" data-id="logger-clear" iconclass="fa fa-trash" data-width="21"></afx-button>
<div data-id="output-tab" iconclass = "fa fa-file-text" >
</div>
</afx-hbox>
</afx-tab-container>
</afx-vbox>
</afx-hbox>
<div data-height="20" data-id="statctn">
<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

@ -0,0 +1,15 @@
# Antedit
This is an example project, generated by AntOS Development Kit
## Howto
Use the CodePad command palette to access to the SDK functionalities:
1. Create new project
2. Init the project from the current folder located in side bar
3. Build and run the project
4. Release the project in zip package
## Set up build target
Open the `project.json` file from the current project tree and add/remove
build target entries. Save the file

View File

@ -0,0 +1,19 @@
(function() {
// import the CodePad application module
const App = this.OS.application.Antedit;
// define the extension
App.extensions.{0} = class {0} extends App.EditorBaseExtension {
constructor(app) {
super("{0}",app);
}
test() {
return this.notify("Test action is invoked");
}
cleanup() {}
};
}).call(this);

View File

@ -0,0 +1,15 @@
{
"javascripts": ["{0}.js"],
"copies": [],
"meta": {
"name": "{0}",
"text": "{0}",
"version": "0.0.1-a",
"actions" : [
{
"text": "__(Example action)",
"name": "test"
}
]
}
}

View File

@ -0,0 +1,33 @@
[
{
"name": "EditorExtensionMaker",
"text": "Antedit Extension",
"version": "0.0.1-a",
"actions" : [
{
"text": "__(New Extension)",
"name": "create"
},
{
"text": "__(Build)",
"name": "build"
},
{
"text": "__(Run)",
"name": "run"
},
{
"text": "__(Build release)",
"name": "release"
},
{
"text": "__(Install extension from file)",
"name": "install"
},
{
"text": "__(Install extension from URL)",
"name": "installFromURL"
}
]
}
]

View File

@ -0,0 +1,168 @@
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container
{
/*border-top: 1px solid #272822;*/
overflow: hidden;
overflow-x: auto;
font-size: 12px;
scrollbar-width: none;
/*scrollbar-color: #656565 transparent;*/
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container::-webkit-scrollbar {
height: 0;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container::-webkit-scrollbar-track {
background: transparent;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container::-webkit-scrollbar-thumb {
background-color: #656565;
border: 0;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul
{
width: intrinsic;
width: -moz-max-content;
width: -webkit-max-content;
width: max-content;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li.selected,
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul > afx-list-item > li.selected{
background-color:#272822;
color:white;
border: 0;
border-radius: 0;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view afx-list-view i.closable:before {
color:afafaf;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li,
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul li{
background-color:#333333;
color:#afafaf;
border-radius: 0;
border: 0;
padding-top: 5px;
padding-bottom: 5px;
padding-right: 20px;
border-right: 1px solid #272822;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-vbox[data-id = "sidebar"]{
background-color:#272822;
}
afx-app-window[data-id = "antedit"] div.afx-window-content {
background-color:#333333;
}
afx-app-window[data-id = "antedit"] afx-resizer {
background-color:#272822;
border-right: 1px solid #656565;
border-bottom: 1px solid #656565;
}
afx-app-window[data-id = "antedit"] .bottom-tab-content {
background-color:#272822;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-tree-view{
color: white;
padding: 0;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-tree-view afx-tree-view-item ul li{
padding-left: 10px;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper .afx_tree_item_selected ul{
background-color: #116cd6;
}
afx-app-window[data-id = "antedit"] afx-file-view afx-tree-view .afx-tree-view-item:before{
color: white;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper div[data-id="statctn"]{
color: white;
background-color: #007acc;
padding-right: 10px;
padding-top: 5px;
font-size: 11px;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper div[data-id="statctn"] afx-label {
padding-left: 10px;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper{
border-radius: 0px;
border: 0;
/*border: 1px solid #37373d;*/
background-color: transparent;
box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.65);
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li
{
background-color: transparent;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view afx-list-item li{
background-color: transparent;
color:#afafaf;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper div.list-container > ul li:hover{
background-color: #37373d;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li.selected,
afx-app-window[data-id = "cmd-win"] .afx-window-wrappe dafx-list-viewafx-list-view ul li.selected
{
background-color: #116cd6;
color:white;
}
afx-app-window[data-id = "cmd-win"] .afx-window-top{
height: 0;
border:0;
}
afx-app-window[data-id = "cmd-win"] input{
border: 1px solid #007acc;
border-radius: 0;
font-size: 12px;
color:#afafaf;
background-color:#272822;
padding-left: 5px;
margin: 3px;
}
afx-app-window[data-id = "cmd-win"] .afx-window-content{
background-color:#272822;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] {
overflow-y: auto;
overflow-x: hidden;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre {
margin: 3px;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
font-family: monospace;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-error {
color: red;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-warn {
color: orange;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-info {
color: white;
}
afx-app-window[data-id = "antedit"] afx-button[ data-id="logger-clear" ] button{
border: 0;
background: transparent;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,24 @@
{
"pkgname": "Antedit",
"app":"Antedit",
"name":"Antos Editor",
"description":"Antos text/code editor",
"info":{
"author": "Xuan Sang LE",
"email": "mrsang@iohub.dev"
},
"version":"0.1.0-a",
"category":"Development",
"iconclass":"bi bi-journal-code",
"mimes":[
"text/.*",
"[^/]*/json.*",
"[^/]*/.*ml",
"[^/]*/javascript",
"dir"
],
"dependencies":[
"MonacoCore@0.23.0-r"
],
"locale": {}
}

View File

@ -0,0 +1,38 @@
<afx-app-window apptitle="Antos Editor" width="600" height="400" data-id="antedit">
<afx-vbox>
<afx-hbox data-id="wrapper">
<afx-vbox data-width = "155" min-width="155" data-id = "sidebar">
<div data-height="10"></div>
<afx-file-view chdir="false" data-id = "fileview" view="tree" status = "false">
</afx-file-view>
</afx-vbox>
<afx-resizer data-width = "3" ></afx-resizer>
<afx-vbox>
<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">
<afx-button text = "" data-id="logger-clear" iconclass="fa fa-trash" data-width="21"></afx-button>
<div data-id="output-tab" iconclass = "fa fa-file-text" >
</div>
</afx-hbox>
</afx-tab-container>
</afx-vbox>
</afx-hbox>
<div data-height="20" data-id="statctn">
<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>

Binary file not shown.

View File

@ -1,7 +0,0 @@
class Antedit extends this.OS.application.BaseApplication
constructor: ( args ) ->
super "Antedit", args
main: () ->
this.OS.register "Antedit", Antedit

167
Antedit/css/main.css Normal file
View File

@ -0,0 +1,167 @@
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container
{
/*border-top: 1px solid #272822;*/
overflow: hidden;
overflow-x: auto;
font-size: 12px;
scrollbar-width: none;
/*scrollbar-color: #656565 transparent;*/
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container::-webkit-scrollbar {
height: 0;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container::-webkit-scrollbar-track {
background: transparent;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container::-webkit-scrollbar-thumb {
background-color: #656565;
border: 0;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul
{
width: intrinsic;
width: -moz-max-content;
width: -webkit-max-content;
width: max-content;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li.selected,
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul > afx-list-item > li.selected{
background-color:#272822;
color:white;
border: 0;
border-radius: 0;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view afx-list-view i.closable:before {
color:afafaf;
}
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li,
afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul li{
background-color:#333333;
color:#afafaf;
border-radius: 0;
border: 0;
padding-top: 5px;
padding-bottom: 5px;
padding-right: 20px;
border-right: 1px solid #272822;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-vbox[data-id = "sidebar"]{
background-color:#272822;
}
afx-app-window[data-id = "antedit"] div.afx-window-content {
background-color:#333333;
}
afx-app-window[data-id = "antedit"] afx-resizer {
background-color:#272822;
border-right: 1px solid #656565;
border-bottom: 1px solid #656565;
}
afx-app-window[data-id = "antedit"] .bottom-tab-content {
background-color:#272822;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-tree-view{
color: white;
padding: 0;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-tree-view afx-tree-view-item ul li{
padding-left: 10px;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper .afx_tree_item_selected ul{
background-color: #116cd6;
}
afx-app-window[data-id = "antedit"] afx-file-view afx-tree-view .afx-tree-view-item:before{
color: white;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper div[data-id="statctn"]{
color: white;
background-color: #007acc;
padding-right: 10px;
padding-top: 5px;
font-size: 11px;
}
afx-app-window[data-id = "antedit"] .afx-window-wrapper div[data-id="statctn"] afx-label {
padding-left: 10px;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper{
border-radius: 0px;
border: 0;
/*border: 1px solid #37373d;*/
background-color: transparent;
box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.65);
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li
{
background-color: transparent;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view afx-list-item li{
background-color: transparent;
color:#afafaf;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper div.list-container > ul li:hover{
background-color: #37373d;
}
afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li.selected,
afx-app-window[data-id = "cmd-win"] .afx-window-wrappe dafx-list-viewafx-list-view ul li.selected
{
background-color: #116cd6;
color:white;
}
afx-app-window[data-id = "cmd-win"] .afx-window-top{
height: 0;
border:0;
}
afx-app-window[data-id = "cmd-win"] input{
border: 1px solid #007acc;
border-radius: 0;
font-size: 12px;
color:#afafaf;
background-color:#272822;
padding-left: 5px;
margin: 3px;
}
afx-app-window[data-id = "cmd-win"] .afx-window-content{
background-color:#272822;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] {
overflow-y: auto;
overflow-x: hidden;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre {
margin: 3px;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
font-family: monospace;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-error {
color: red;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-warn {
color: orange;
}
afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-info {
color: white;
}
afx-app-window[data-id = "antedit"] afx-button[ data-id="logger-clear" ] button{
border: 0;
background: transparent;
}

View File

@ -0,0 +1,19 @@
(function() {
// import the CodePad application module
const App = this.OS.application.Antedit;
// define the extension
App.extensions.{0} = class {0} extends App.EditorBaseExtension {
constructor(app) {
super("{0}",app);
}
test() {
return this.notify("Test action is invoked");
}
cleanup() {}
};
}).call(this);

View File

@ -0,0 +1,15 @@
{
"javascripts": ["{0}.js"],
"copies": [],
"meta": {
"name": "{0}",
"text": "{0}",
"version": "0.0.1-a",
"actions" : [
{
"text": "__(Example action)",
"name": "test"
}
]
}
}

View File

@ -0,0 +1,33 @@
[
{
"name": "EditorExtensionMaker",
"text": "Antedit Extension",
"version": "0.0.1-a",
"actions" : [
{
"text": "__(New Extension)",
"name": "create"
},
{
"text": "__(Build)",
"name": "build"
},
{
"text": "__(Run)",
"name": "run"
},
{
"text": "__(Build release)",
"name": "release"
},
{
"text": "__(Install extension from file)",
"name": "install"
},
{
"text": "__(Install extension from URL)",
"name": "installFromURL"
}
]
}
]

View File

@ -1,16 +1,24 @@
{
"pkgname": "Antedit",
"app":"Antedit",
"name":"Antedit",
"description":"Antedit",
"name":"Antos Editor",
"description":"Antos text/code editor",
"info":{
"author": "",
"email": ""
"author": "Xuan Sang LE",
"email": "mrsang@iohub.dev"
},
"version":"0.0.1-a",
"category":"Other",
"iconclass":"fa fa-adn",
"mimes":["none"],
"dependencies":[],
"version":"0.1.0-a",
"category":"Development",
"iconclass":"bi bi-journal-code",
"mimes":[
"text/.*",
"[^/]*/json.*",
"[^/]*/.*ml",
"[^/]*/javascript",
"dir"
],
"dependencies":[
"MonacoCore@0.23.0-r"
],
"locale": {}
}

View File

@ -1,8 +1,14 @@
{
"name": "Antedit",
"css": [],
"css": ["css/main.css"],
"javascripts": [],
"coffees": ["coffees/main.coffee"],
"ts": [],
"copies": ["assets/scheme.html", "package.json", "README.md"]
"coffees": [],
"ts": [
"ts/monaco.d.ts",
"ts/BaseEditorModel.ts",
"ts/MonacoEditorModel.ts",
"ts/main.ts",
"ts/EditorExtensionMaker.ts"
],
"copies": ["extensions","assets/scheme.html", "package.json", "README.md"]
}

View File

@ -0,0 +1,570 @@
namespace OS {
export namespace application {
/**
* Extends the [[RemoteFileHandle]] interface with some useful
* properties used by [[BaseEditorModel]]
*/
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
*
* @protected
* @type {EditorFileHandle}
* @memberof BaseEditorModel
*/
protected currfile: EditorFileHandle;
/**
* Referent to the parent app
*
* @private
* @type {BaseApplication}
* @memberof BaseEditorModel
*/
private app: BaseApplication;
/**
* Reference to the editor tab bar UI
*
* @private
* @type {GUI.tag.TabBarTag}
* @memberof BaseEditorModel
*/
private tabbar: GUI.tag.TabBarTag;
/**
* Referent to the editor container
*
* @private
* @type {HTMLElement}
* @memberof BaseEditorModel
*/
private container: HTMLElement;
onstatuschange: (stat: GenericObject<any>) => void;
/**
* Editor mutex
*
* @private
* @type {boolean}
* @memberof BaseEditorModel
*/
private editormux: boolean;
/**
* Creates an instance of BaseEditorModel.
*
* @param {Antedit} app parent app
* @param {GUI.tag.TabBarTag} tabbar tabbar DOM element
* @param {HTMLElement} editorarea editor container DOM element
* @memberof BaseEditorModel
*/
constructor(app: BaseApplication, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) {
this.container = editorarea;
this.currfile = "Untitled".asFileHandle() as EditorFileHandle;
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 {EditorFileHandle} file then file handle to search
* @returns {number}
* @memberof BaseEditorModel
*/
private findTabByFile(file: EditorFileHandle): 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 {EditorFileHandle} file
* @memberof BaseEditorModel
*/
private newTab(file: EditorFileHandle): void {
file.text = file.basename ? file.basename : file.path;
if (!file.cache) {
file.cache = "";
}
file.textModel = this.newTextModelFrom(file);
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 BaseEditorModel
*/
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 EditorFileHandle
);
return false;
}
this.tabbar.selected = cnt - 1;
return false;
}
/**
* Select a tab by its index
*
* @private
* @param {number} i tab index
* @returns {void}
* @memberof BaseEditorModel
*/
private selecteTab(i: number): void {
//return if i is @tabbar.get "selidx"
const file = this.tabbar.items[i] as EditorFileHandle;
if (!file) {
return;
}
//return if file is @currfile
if (this.currfile !== file) {
this.currfile.textModel = this.getTexModel();
this.currfile.selected = false;
this.currfile = file;
}
this.editormux = true;
this.setTextModel(file.textModel);
if (this.onstatuschange)
this.onstatuschange(this.getEditorStatus());
this.focus();
}
/**
* Select an opened file, this will select the corresponding tab
*
* @param {(EditorFileHandle | string)} file
* @memberof BaseEditorModel
*/
selectFile(file: EditorFileHandle | string): void {
const i = this.findTabByFile(
file.asFileHandle() as EditorFileHandle
);
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 {EditorFileHandle} file file to open
* @returns {void}
* @memberof BaseEditorModel
*/
openFile(file: EditorFileHandle): 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 {EditorFileHandle} file
* @memberof BaseEditorModel
*/
private write(file: EditorFileHandle): 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 the current opened file
*
* @return {*} {void}
* @memberof BaseEditorModel
*/
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 BaseEditorModel
*/
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);
});
}
/**
* Get all dirty file handles in the editor
*
* @return {*} {EditorFileHandle[]}
* @memberof BaseEditorModel
*/
dirties(): EditorFileHandle[] {
const result = [];
for (let v of Array.from(this.tabbar.items)) {
if (v.dirty) {
result.push(v);
}
}
return result;
}
/**
* Context menu handle for the editor
*
* @memberof BaseEditorModel
*/
set contextmenuHandle(cb: (e: any, m: any) => void) {
this.container.contextmenuHandle = cb;
}
/**
* Close all opened files
*
* @memberof BaseEditorModel
*/
closeAll(): void {
this.tabbar.items = [];
this.resetEditor();
}
/**
* Check whether the editor is dirty
*
* @return {*} {boolean}
* @memberof BaseEditorModel
*/
isDirty(): boolean {
return this.dirties().length > 0;
}
/**
* Set up the editor instance
* Should be implemented by subclass
*
* @protected
* @abstract
* @param {HTMLElement} el
* @memberof BaseEditorModel
*/
protected abstract editorSetup(el: HTMLElement): void;
/**
* Listen to editor event
*
* Should be implemented by subclasses
*
* @abstract
* @param {string} evt_str
* @param {() => void} callback
* @memberof BaseEditorModel
*/
abstract on(evt_str: string, callback: () => void): void;
/**
* Resize the editor
*
* Should be implemented by subclasses
*
* @abstract
* @memberof BaseEditorModel
*/
abstract resize(): void;
/**
* Make the editor focused
*
* Should be implemented by subclasses
*
* @abstract
* @memberof BaseEditorModel
*/
abstract focus(): void;
/**
* Get language mode from file extension
*
* Should be implemented by subclasses
*
* @protected
* @abstract
* @param {string} path
* @return {*} {GenericObject<any>}
* @memberof BaseEditorModel
*/
protected abstract getModeForPath(path: string): GenericObject<any>;
/**
* Query the editor status
*
* Should be implemented by subclasses
*
* @abstract
* @return {*} {GenericObject<any>}
* @memberof BaseEditorModel
*/
abstract getEditorStatus(): GenericObject<any>;
/**
* Get the editor value
*
* Should be implemented by subclasses
*
* @abstract
* @return {*} {string}
* @memberof BaseEditorModel
*/
abstract getValue(): string;
/**
* Set the editor value
*
* Should be implemented by subclasses
*
* @abstract
* @param {string} value
* @memberof BaseEditorModel
*/
abstract setValue(value: string): void;
/**
* Set the editor language mode
*
* Should be implemented by subclasses
*
* @abstract
* @param {GenericObject<any>} m
* @memberof BaseEditorModel
*/
abstract setMode(m: GenericObject<any>): void;
/**
* Get textModel from the current editor session
*
* @protected
* @abstract
* @return {*} {*}
* @memberof BaseEditorModel
*/
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
*
* Should be implemented by subclasses
*
* @abstract
* @param {string} theme
* @memberof BaseEditorModel
*/
abstract setTheme(theme: string): void;
/**
* Get all language modes supported by the editor
*
* @abstract
* @return {*} {GenericObject<any>[]}
* @memberof BaseEditorModel
*/
abstract getModes(): GenericObject<any>[];
/**
* Get the real editor model
*/
abstract getEditor(): any;
}
}
}

View File

@ -0,0 +1,472 @@
namespace OS {
declare var JSZip: any;
export namespace application {
export type AnteditBaseExtension = typeof EditorBaseExtension;
}
/**
*
*
* @class EditorBaseExtension
*/
class EditorBaseExtension {
static dependencies: string[];
protected app: OS.application.Antedit;
protected name: string;
constructor(name:string, app: OS.application.Antedit) {
this.app = app;
this.name = name;
}
/**
*
*
* @returns {Promise<any>}
* @memberof EditorBaseExtension
*/
preload(): Promise<any> {
return API.require(OS.application.Antedit.extensions[this.name].dependencies);
}
/**
*
*
* @protected
* @returns {string}
* @memberof EditorBaseExtension
*/
protected basedir(): string {
return `${this.app.meta().path}/extensions/${this.name}`;
}
/**
*
*
* @protected
* @param {(string | FormattedString)} m
* @returns {void}
* @memberof EditorBaseExtension
*/
protected notify(m: string | FormattedString): void {
return this.app.notify(m);
}
/**
*
*
* @protected
* @param {(string | FormattedString)} m
* @param {Error} e
* @returns {void}
* @memberof EditorBaseExtension
*/
protected error(m: string | FormattedString, e: Error): void {
return this.app.error(m, e);
}
/**
*
*
* @protected
* @return {AnteditLogger} editor logger
* @memberof EditorBaseExtension
*/
protected logger(): any {
if (!this.app.setting.showBottomBar) {
this.app.showOutput(true);
}
else {
this.app.showOutput(false);
}
return this.app.logger;
}
/**
*
*
* @protected
* @param {string} file
* @returns {Promise<GenericObject<any>>}
* @memberof EditorBaseExtension
*/
protected metadata(file: string): Promise<GenericObject<any>> {
return new Promise((resolve, reject) => {
if (!this.app.currdir) {
return reject(
API.throwe(__("Current folder is not found"))
);
}
`${this.app.currdir.path}/${file}`
.asFileHandle()
.read("json")
.then((data) => {
if (!data.root && this.app.currdir) {
data.root = this.app.currdir.path;
}
resolve(data);
})
.catch((e) => {
// try to ask user to select a folder
this.app.openDialog("FileDialog", {
title: __("Select build directory"),
root: this.app.currdir.path,
mimes: ["dir"]
})
.then((d) => {
`${d.file.path}/${file}`
.asFileHandle()
.read("json")
.then((data) => {
if (!data.root) {
data.root = d.file.path;
}
resolve(data);
})
.catch((e1) => reject(e1))
})
.catch(
(e1) => reject(API.throwe(__("Unable to read meta-data"))
))
});
});
}
}
EditorBaseExtension.dependencies = [];
OS.application.Antedit.extensions = {};
OS.application.Antedit.EditorBaseExtension = EditorBaseExtension;
class EditorExtensionMaker extends EditorBaseExtension {
constructor(app: OS.application.Antedit) {
super("EditorExtensionMaker", app);
}
create(): void {
this.logger().clear();
this.app
.openDialog("FileDialog", {
title: "__(New CodePad extension at)",
file: { basename: __("ExtensionName") },
mimes: ["dir"],
})
.then((d) => {
return this.mktpl(d.file.path, d.name);
});
}
build(callback?: () => void): void {
this.logger().clear();
this.metadata("extension.json")
.then(async (meta) => {
try {
const jsrc = await API.VFS.cat(meta.javascripts.map(v => `${meta.root}/${v}`),"");
await `${meta.root}/build/debug/main.js`
.asFileHandle()
.setCache(jsrc)
.write("text/plain");
await `${meta.root}/build/debug/extension.json`
.asFileHandle()
.setCache(meta.meta)
.write("object");
await API.VFS.copy( meta.copies.map(v => `${meta.root}/${v}`),`${meta.root}/build/debug`);
this.logger().info(__("Files generated in {0}", `${meta.root}/build/debug`));
if(callback)
callback();
} catch (e) {
return this.logger().error(__("Unable to build extension:{0}", e.stack));
}
})
.catch((e) => this.logger().error(__("Unable to read meta-data:{0}", e.stack)));
}
run(): void {
this.logger().clear();
this.metadata("extension.json")
.then(async (meta) => {
if(!meta || !meta.meta || !meta.meta.name)
return this.logger().error(__("Invalid extension meta-data"));
try {
const path = `${meta.root}/build/debug/main.js`;
if (API.shared[path]) {
delete API.shared[path];
}
await API.requires(path);
if (this.app.extensions[meta.meta.name] && this.app.extensions[meta.meta.name].cleanup)
{
this.app.extensions[meta.meta.name].cleanup();
}
this.app.extensions[meta.meta.name] = new OS.application.Antedit.extensions[meta.meta.name](this.app);
for (let v of meta.meta.actions) {
this.app.eum.addAction(meta.meta, v, (e_name, a_name) => {
this.app.loadAndRunExtensionAction(e_name, a_name, `${meta.root}/build`);
});
}
this.app.eum.active.getEditor().trigger(meta.meta.name, 'editor.action.quickCommand');
} catch (e) {
return this.logger().error(__("Unable to run extension:{0}", e.stack));
}
})
.catch((e) => this.logger().error(__("Unable to read meta-data:{0}", e.stack)));
}
release(): void {
this.logger().clear();
this.metadata("extension.json")
.then((meta) => {
this.build(async () => {
try {
API.VFS.mkar(
`${meta.root}/build/debug`,
`${meta.root}/build/release/${meta.meta.name}.zip`
);
this.logger().info(__("Archive created at {0}", `${meta.root}/build/release/${meta.meta.name}.zip`));
} catch (e) {
return this.logger().error(
__("Unable to create archive: {0}",
e.stack
));
}
});
})
.catch((e) => this.logger().error(__("Unable to read meta-data: {0}", e.stack)));
}
install(): void {
this.logger().clear();
this.app
.openDialog("FileDialog", {
title: "__(Select extension archive)",
mimes: [".*/zip"],
})
.then(async (d) => {
try {
await this.installZip(d.file.path);
this.logger().info(__("Extension installed"));
return this.app.loadExtensionMetaData();
} catch (e) {
return this.logger().error(__("Unable to install extension: {0}", e.stack));
}
});
}
installFromURL(): void
{
this.logger().clear();
this.app
.openDialog("PromptDialog", {
title: __("Enter URI"),
label: __("Please enter extension URI:")
})
.then(async (v) => {
if(!v) return;
try {
await this.installZip(v);
this.logger().info(__("Extension installed"));
return this.app.loadExtensionMetaData();
} catch (e) {
return this.app.error(__("Unable to install extension: {0}", v));
}
});
}
/**
*
*
* @private
* @param {string} path
* @param {string} name
* @memberof EditorExtensionMaker
*/
private mktpl(path: string, name: string): void {
const rpath = `${path}/${name}`;
const dirs = [
rpath,
`${rpath}/build`,
`${rpath}/build/release`,
`${rpath}/build/debug`,
];
const files = [
["main.tpl", `${rpath}/${name}.js`],
["meta.tpl", `${rpath}/extension.json`],
];
API.VFS.mkdirAll(dirs)
.then(async () => {
try {
await API.VFS.mktpl(files, this.basedir(), (data)=>{
return data.format(name, `${path}/${name}`);
});
this.app.currdir = rpath.asFileHandle();
this.app.toggleSideBar();
return this.app.eum.active.openFile(
`${rpath}/${name}.js`.asFileHandle() as application.EditorFileHandle
);
} catch (e) {
return this.logger().error(
__("Unable to create extension template: {0}",
e.stack)
);
}
})
.catch((e) =>
this.logger().error(__("Unable to create extension directories: {0}", e.stack))
);
}
/**
*
*
* @private
* @param {string} path
* @returns {Promise<any>}
* @memberof EditorExtensionMaker
*/
private installZip(path: string): Promise<void> {
return new Promise((resolve, reject) => {
API.requires("os://scripts/jszip.min.js")
.then(() => {
path.asFileHandle()
.read("binary")
.then((data) => {
JSZip.loadAsync(data)
.then((zip: any) => {
zip.file("extension.json").async("uint8array")
.then((d) =>{
const meta = JSON.parse(new TextDecoder("utf-8").decode(d));
const pth = this.ext_dir(meta.name);
const dir = [pth];
const files = [];
for (let name in zip.files) {
const file = zip.files[name];
if (file.dir) {
dir.push(pth + "/" + name);
} else if(name != "extension.json") {
files.push(name);
}
}
if (dir.length > 0) {
API.VFS.mkdirAll(dir)
.then(() => {
this.installFiles(files, zip, meta)
.then(() => resolve())
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
} else {
this.installFiles(files, zip, meta)
.then(() => resolve())
.catch((e) => reject(__e(e)));
}
})
.catch(e => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
});
}
private ext_dir(en: string): string
{
return `${this.app.meta().path}/extensions/${en}`;
}
/**
*
*
* @private
* @param {string[]} files
* @param {*} zip
* @param {GenericObject<any>} meta
* @returns {Promise<any>}
* @memberof EditorExtensionMaker
*/
private installFiles(
files: string[],
zip: any,
meta: GenericObject<any>
): Promise<void> {
if (files.length === 0) {
return this.installMeta(meta);
}
return new Promise((resolve, reject) => {
const file = files.splice(0, 1)[0];
const path = `${this.ext_dir(meta.name)}/${file}`;
return zip
.file(file)
.async("uint8array")
.then((d: Uint8Array) => {
return path
.asFileHandle()
.setCache(new Blob([d], { type: "octet/stream" }))
.write("text/plain")
.then((r) => {
if (r.error) {
return reject(r.error);
}
return this.installFiles(files, zip, meta)
.then(() => resolve())
.catch((e) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
/**
*
*
* @private
* @param {GenericObject<any>} meta
* @returns {Promise<void>}
* @memberof EditorExtensionMaker
*/
private installMeta(meta: GenericObject<any>): Promise<void> {
return new Promise(async (resolve, reject) => {
const file = `${this.ext_dir("")}/extensions.json`.asFileHandle();
try {
const data = await file.read("json");
const names = [];
for (let v of data) {
names.push(v.name);
}
const idx = names.indexOf(meta.name);
if (idx >= 0) {
data.splice(idx, 1);
}
data.push(meta);
try {
await file.setCache(data).write("object");
return resolve();
} catch (e) {
return reject(__e(e));
}
} catch (e_1) {
// try to create new file
try {
await file.setCache([meta]).write("object");
return resolve();
} catch (e_2) {
return reject(__e(e_2));
}
}
});
}
}
OS.application.Antedit.extensions.EditorExtensionMaker = EditorExtensionMaker;
}

View File

@ -0,0 +1,271 @@
namespace OS {
export namespace application {
/**
* Wrapper model for the ACE text editor
*
* @export
* @class MonacoEditorModel
* @extends {BaseEditorModel}
*/
export class MonacoEditorModel extends BaseEditorModel {
static modes: GenericObject<monaco.languages.ILanguageExtensionPoint>;
/**
* Creates an instance of MonacoEditorModel.
* @param {MonacoEditorModel} app MonacoEditorModel instance
* @param {GUI.tag.TabBarTag} tabbar tabbar element
* @param {HTMLElement} editorarea main editor container element
* @memberof MonacoEditorModel
*/
constructor(app: BaseApplication, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) {
super(app, tabbar, editorarea);
}
/**
* Reset the editor
*
* @protected
* @memberof MonacoEditorModel
*/
protected resetEditor(): void {
this.setValue("");
// TODO create new textmodel
}
/**
* Get a text model from the current editor session
*
* @protected
* @return {*}
* @memberof MonacoEditorModel
*/
protected getTexModel() {
return {
model: this.editor.getModel(),
position: this.editor.getPosition()
}
}
/**
* Set text model to current editor session
*
* @protected
* @param {*} model
* @memberof MonacoEditorModel
*/
protected setTextModel(model: any): void {
this.editor.setModel(model.model);
if(model.position)
this.editor.setPosition(model.position);
}
/**
* Create new editor model from file
*
* @protected
* @param {EditorFileHandle} file
* @return {*} {*}
* @memberof MonacoEditorModel
*/
protected newTextModelFrom(file: EditorFileHandle): any {
if(file.path.toString() === "Untitled") {
return {
model: monaco.editor.createModel(file.cache, "textplain")
}
}
const uri = monaco.Uri.parse(file.path);
const model = monaco.editor.getModel(uri);
if(model)
{
return { model: model };
}
return {
model: monaco.editor.createModel(file.cache, undefined, uri)
}
}
/**
* Get language modes
*
* @return {*} {GenericObject<any>[]}
* @memberof MonacoEditorModel
*/
getModes(): GenericObject<any>[] {
//const list = [];
//return list;
return monaco.languages.getLanguages();
}
/**
* Set the editor theme
*
* @param {string} theme theme name
* @memberof MonacoEditorModel
*/
setTheme(theme: string): void {
}
/**
* Set editor language mode
*
* The mode object should be in the following format:
* ```ts
* {
* text: string,
* mode: string
* }
* ```
*
* @param {GenericObject<any>} m language mode object
* @memberof MonacoEditorModel
*/
setMode(m: GenericObject<any>): void {
}
/**
* Reference to the editor instance
*
* @private
* @type {GenericObject<any>}
* @memberof MonacoEditorModel
*/
private editor: GenericObject<any>;
/**
* Setup the editor
*
* @protected
* @param {HTMLElement} el editor container DOM
* @memberof MonacoEditorModel
*/
protected editorSetup(el: HTMLElement): void {
this.editor = monaco.editor.create(el, {
value: "",
language: 'textplain'
});
if(!MonacoEditorModel.modes)
{
MonacoEditorModel.modes = {};
monaco.languages.getLanguages().forEach((el) =>{
MonacoEditorModel.modes[el.id] = el;
})
}
}
/**
* Register to editor event
*
* @param {string} evt_str event name
* @param {() => void} callback callback function
* @memberof MonacoEditorModel
*/
on(evt_str: string, callback: () => void): void {
switch (evt_str) {
case "input":
this.editor.onDidChangeModelContent(callback);
break;
case "focus":
this.editor.onDidFocusEditorText(callback);
break;
case "changeCursor":
this.editor.onDidChangeCursorPosition(callback);
break;
default:
break;
}
}
/**
* Resize the editor
*
* @memberof MonacoEditorModel
*/
resize(): void {
if(this.editor)
this.editor.layout();
}
/**
* Focus on the editor
*
* @memberof MonacoEditorModel
*/
focus(): void {
if(this.editor)
this.editor.focus();
}
/**
* Get language mode from path
*
* @protected
* @param {string} path
* @return {*} {GenericObject<any>}
* @memberof MonacoEditorModel
*/
protected getModeForPath(path: string): GenericObject<any> {
return {};
}
/**
* Get the editor status
*
* @return {*} {GenericObject<any>}
* @memberof MonacoEditorModel
*/
getEditorStatus(): GenericObject<any> {
const pos = this.editor.getPosition();
const mode = MonacoEditorModel.modes[this.editor.getModel().getModeId()];
return {
row: pos.lineNumber,
column: pos.column,
line: this.editor.getModel().getLineCount(),
langmode: {
text: mode.aliases[0],
mode: mode
},
file: this.currfile.path
}
}
/**
* Get editor value
*
* @return {*} {string}
* @memberof MonacoEditorModel
*/
getValue(): string {
return this.editor.getValue();
}
/**
* Set editor value
*
* @param {string} value
* @memberof MonacoEditorModel
*/
setValue(value: string): void {
this.editor.setValue(value);
}
getEditor(): any {
return this.editor;
}
}
}
}

File diff suppressed because it is too large Load Diff

7370
Antedit/ts/monaco.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff