Antedit v0.2.0

- fix compatible bug with new Monaco version
- the editor now can support diff view of two files
This commit is contained in:
lxsang 2022-07-04 18:11:05 +02:00
parent 21f2e58768
commit c994d1c738
10 changed files with 268 additions and 50 deletions

View File

@ -6,6 +6,7 @@ The editor functionality can be extended by its extension mechanism.
Extension can be developed/released/isntalled by the editor itself. Extension can be developed/released/isntalled by the editor itself.
### Change logs ### Change logs
- 0.2.0-b: Support diff mode in editor + fix new Monaco version compatible bug
- 0.1.17-b: Fix extension keybinding bug with the new monaco editor - 0.1.17-b: Fix extension keybinding bug with the new monaco editor
- 0.1.16-b: use the new version of monaco editor - 0.1.16-b: use the new version of monaco editor
- 0.1.14-b: improve output log display - 0.1.14-b: improve output log display

View File

@ -6,6 +6,7 @@ The editor functionality can be extended by its extension mechanism.
Extension can be developed/released/isntalled by the editor itself. Extension can be developed/released/isntalled by the editor itself.
### Change logs ### Change logs
- 0.2.0-b: Support diff mode in editor + fix new Monaco version compatible bug
- 0.1.17-b: Fix extension keybinding bug with the new monaco editor - 0.1.17-b: Fix extension keybinding bug with the new monaco editor
- 0.1.16-b: use the new version of monaco editor - 0.1.16-b: use the new version of monaco editor
- 0.1.14-b: improve output log display - 0.1.14-b: improve output log display

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"email": "mrsang@iohub.dev" "email": "mrsang@iohub.dev"
}, },
"version": "0.1.17-b", "version": "0.2.0-b",
"category": "Development", "category": "Development",
"iconclass": "bi bi-journal-code", "iconclass": "bi bi-journal-code",
"mimes": [ "mimes": [

Binary file not shown.

View File

@ -7,7 +7,7 @@
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"email": "mrsang@iohub.dev" "email": "mrsang@iohub.dev"
}, },
"version": "0.1.17-b", "version": "0.2.0-b",
"category": "Development", "category": "Development",
"iconclass": "bi bi-journal-code", "iconclass": "bi bi-journal-code",
"mimes": [ "mimes": [

View File

@ -1,12 +1,90 @@
namespace OS { namespace OS {
//declare var $:any;
export namespace API
{
export namespace VFS
{
export class DiffEditorFileHandle extends OS.API.VFS.BaseFileHandle
{
text: string;
textModel:any;
selected: boolean;
constructor(files: OS.API.VFS.BaseFileHandle[]) {
super("");
this.path = `${files[0].path} -> ${files[1].path}`;
this.cache = files;
this.basename = `${files[0].basename} -> ${files[1].basename}`;
this.info = {
type: "file",
mime: undefined,
size: 0,
name: this.basename,
path: this.path
};
this.ready = true;
}
meta(): Promise<any> {
return new Promise(async (resolve, reject) =>
{
try
{
await Promise.all([this.cache[0].meta(), this.cache[1].meta]);
resolve({
result: this.info,
error: false,
});
}
catch(e)
{
reject(e);
}
});
}
protected _rd(_t: string): Promise<any> {
return new Promise(async (resolve, reject) => {
try
{
this.cache[0].cache = await this.cache[0].read();
this.cache[1].cache = await this.cache[1].read();
resolve(this.cache);
}
catch(e)
{
reject(e);
}
});
}
protected _wr(t: string, d: any): Promise<any> {
this.cache = d;
return new Promise((resolve, reject) =>
{
resolve({
result: true,
error: false,
})
});
}
setPath(s: string)
{
// do nothing
}
}
}
}
export namespace application { export namespace application {
/** /**
* Extends the [[RemoteFileHandle]] interface with some useful * Extends the [[RemoteFileHandle]] interface with some useful
* properties used by [[BaseEditorModel]] * properties used by [[BaseEditorModel]]
*/ */
export type EditorFileHandle = API.VFS.RemoteFileHandle & { export type EditorFileHandle = OS.API.VFS.BaseFileHandle & {
/** /**
* The text will be displayed on the tab bar when opened * The text will be displayed on the tab bar when opened
* *
@ -81,7 +159,7 @@ namespace OS {
* @type {boolean} * @type {boolean}
* @memberof BaseEditorModel * @memberof BaseEditorModel
*/ */
private editormux: boolean; //private editormux: boolean;
/** /**
@ -98,7 +176,7 @@ namespace OS {
this.tabbar = tabbar; this.tabbar = tabbar;
this.editorSetup(editorarea); this.editorSetup(editorarea);
this.app = app; this.app = app;
this.editormux = false; // this.editormux = false;
this.onstatuschange = undefined; this.onstatuschange = undefined;
this.on("focus", () => { this.on("focus", () => {
@ -106,11 +184,14 @@ namespace OS {
this.onstatuschange(this.getEditorStatus()); this.onstatuschange(this.getEditorStatus());
}); });
this.on("input", () => { this.on("input", () => {
if (this.editormux) { // console.log(this.editormux, this.currfile.dirty);
/*if (this.editormux) {
this.editormux = false; this.editormux = false;
console.log("set editor mux to false");
return false; return false;
} }*/
if (!this.currfile.dirty) { if (!this.currfile.dirty) {
console.log("dirty", this.currfile.path);
this.currfile.dirty = true; this.currfile.dirty = true;
this.currfile.text += "*"; this.currfile.text += "*";
return this.tabbar.update(undefined); return this.tabbar.update(undefined);
@ -232,10 +313,8 @@ namespace OS {
this.currfile = file; this.currfile = file;
} }
this.editormux = true; // this.editormux = true;
this.setTextModel(file.textModel); this.setTextModel(file.textModel);
if (this.onstatuschange)
this.onstatuschange(this.getEditorStatus());
this.focus(); this.focus();
} }
@ -274,7 +353,6 @@ namespace OS {
this.newTab(file); this.newTab(file);
return; return;
} }
file.read() file.read()
.then((d) => { .then((d) => {
file.cache = d || ""; file.cache = d || "";
@ -295,16 +373,16 @@ namespace OS {
* @param {EditorFileHandle} file * @param {EditorFileHandle} file
* @memberof BaseEditorModel * @memberof BaseEditorModel
*/ */
private write(file: EditorFileHandle): void { private write(): void {
this.currfile.cache = this.getValue(); this.currfile.cache = this.getValue();
file.write("text/plain") this.currfile.write("text/plain")
.then((d) => { .then((d) => {
file.dirty = false; this.currfile.dirty = false;
file.text = file.basename; this.currfile.text = this.currfile.basename;
this.tabbar.update(undefined); this.tabbar.update(undefined);
}) })
.catch((e) => .catch((e) =>
this.app.error(__("Unable to save file: {0}", file.path), e) this.app.error(__("Unable to save file: {0}", this.currfile.path), e)
); );
} }
@ -318,7 +396,7 @@ namespace OS {
save(): void { save(): void {
this.currfile.cache = this.getValue(); this.currfile.cache = this.getValue();
if (this.currfile.basename) { if (this.currfile.basename) {
return this.write(this.currfile); return this.write();
} }
return this.saveAs(); return this.saveAs();
} }
@ -339,7 +417,7 @@ namespace OS {
d = d.parent(); d = d.parent();
} }
this.currfile.setPath(`${d.path}/${f.name}`); this.currfile.setPath(`${d.path}/${f.name}`);
this.write(this.currfile); this.write();
}); });
} }
@ -376,6 +454,7 @@ namespace OS {
*/ */
closeAll(): void { closeAll(): void {
this.tabbar.items = []; this.tabbar.items = [];
this.openFile("Untitled".asFileHandle() as EditorFileHandle);
this.resetEditor(); this.resetEditor();
} }

View File

@ -30,8 +30,6 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
protected resetEditor(): void { protected resetEditor(): void {
this.setValue("");
// TODO create new textmodel
} }
@ -76,20 +74,32 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
protected newTextModelFrom(file: EditorFileHandle): any { protected newTextModelFrom(file: EditorFileHandle): any {
if(file.path.toString() === "Untitled") { if(Array.isArray(file.cache))
{
return { return {
model: monaco.editor.createModel(file.cache, "textplain") model: {
original: this.newTextModelFrom(file.cache[0]).model,
modified: this.newTextModelFrom(file.cache[1]).model
}
} }
} }
const uri = monaco.Uri.parse(file.protocol + "://antedit/file/" + file.genealogy.join("/")); else
const model = monaco.editor.getModel(uri);
if(model)
{ {
model.setValue(file.cache); if(file.path.toString() === "Untitled") {
return { model: model }; return {
} model: monaco.editor.createModel(file.cache, "textplain")
return { }
model: monaco.editor.createModel(file.cache, undefined, uri) }
const uri = monaco.Uri.parse(file.protocol + "://antedit/file/" + file.genealogy.join("/"));
const model = monaco.editor.getModel(uri);
if(model)
{
model.setValue(file.cache);
return { model: model };
}
return {
model: monaco.editor.createModel(file.cache, undefined, uri)
}
} }
} }
@ -103,7 +113,11 @@ namespace OS {
//const list = []; //const list = [];
//return list; //return list;
return monaco.languages.getLanguages().map(e=>{ return monaco.languages.getLanguages().map(e=>{
(e as GenericObject<any>).text = e.aliases[0]; const item = (e as GenericObject<any>);
if(e.aliases)
item.text = e.aliases[0];
else
item.text = e.id;
return e; return e;
}); });
} }
@ -134,12 +148,24 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
setMode(m: GenericObject<any>): void { setMode(m: GenericObject<any>): void {
monaco.editor.setModelLanguage(this.editor.getModel(), m.id); if(this.editor == this._code_editor)
{
monaco.editor.setModelLanguage(this.editor.getModel(), m.id);
}
else
{
for(const model of this.editor.getModel())
{
monaco.editor.setModelLanguage(model, m.id);
}
}
if(this.onstatuschange) if(this.onstatuschange)
this.onstatuschange(this.getEditorStatus()); this.onstatuschange(this.getEditorStatus());
} }
private code_container: JQuery<HTMLElement>;
private diff_container: JQuery<HTMLElement>;
/** /**
* Reference to the editor instance * Reference to the editor instance
@ -148,7 +174,30 @@ namespace OS {
* @type {GenericObject<any>} * @type {GenericObject<any>}
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
private editor: GenericObject<any>; private _code_editor: GenericObject<any>;
/**
* Reference to the diff editor instance
*
* @private
* @type {GenericObject<any>}
* @memberof MonacoEditorModel
*/
private _diff_editor: GenericObject<any>;
/**
* Getter get current editor instance based on current file
*
* @private
* @type {GenericObject<any>}
* @memberof MonacoEditorModel
*/
private get editor(): GenericObject<any>
{
if(Array.isArray(this.currfile.cache))
{
return this._diff_editor;
}
return this._code_editor;
}
/** /**
@ -159,10 +208,24 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
protected editorSetup(el: HTMLElement): void { protected editorSetup(el: HTMLElement): void {
this.editor = monaco.editor.create(el, { // create two editor instancs for code mode and diff mode
this.code_container = $("<div />")
.css("width", "100%")
.css("height", "100%");
this.diff_container = $("<div />")
.css("width", "100%")
.css("height", "100%")
.css("display", "none");
$(el).append(this.code_container);
$(el).append(this.diff_container);
this._code_editor = monaco.editor.create(this.code_container[0], {
value: "", value: "",
language: 'textplain' language: 'textplain'
}); });
this._diff_editor = monaco.editor.createDiffEditor(this.diff_container[0],{
readOnly: true
});
if(!MonacoEditorModel.modes) if(!MonacoEditorModel.modes)
{ {
MonacoEditorModel.modes = {}; MonacoEditorModel.modes = {};
@ -183,13 +246,17 @@ namespace OS {
on(evt_str: string, callback: () => void): void { on(evt_str: string, callback: () => void): void {
switch (evt_str) { switch (evt_str) {
case "input": case "input":
this.editor.onDidChangeModelContent(callback); this._code_editor.onDidChangeModelContent(callback);
break; break;
case "focus": case "focus":
this.editor.onDidFocusEditorText(callback); this._code_editor.onDidFocusEditorText(callback);
this._diff_editor.getOriginalEditor().onDidFocusEditorText(callback);
this._diff_editor.getModifiedEditor().onDidFocusEditorText(callback);
break; break;
case "changeCursor": case "changeCursor":
this.editor.onDidChangeCursorPosition(callback); this._code_editor.onDidChangeCursorPosition(callback);
this._diff_editor.getOriginalEditor().onDidChangeCursorPosition(callback);
this._diff_editor.getModifiedEditor().onDidChangeCursorPosition(callback);
break; break;
default: default:
break; break;
@ -214,8 +281,21 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
focus(): void { focus(): void {
if(Array.isArray(this.currfile.cache))
{
this.code_container.hide();
this.diff_container.show();
}
else
{
this.code_container.show();
this.diff_container.hide();
}
if(this.editor) if(this.editor)
{
this.editor.layout();
this.editor.focus(); this.editor.focus();
}
} }
@ -238,14 +318,34 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
getEditorStatus(): GenericObject<any> { getEditorStatus(): GenericObject<any> {
const pos = this.editor.getPosition();
const mode = MonacoEditorModel.modes[this.editor.getModel().getLanguageId()]; let ed = undefined;
if(this.editor == this._code_editor)
{
ed = this.editor;
}
else
{
ed = this.editor.getOriginalEditor();
if(this.editor.getModifiedEditor().hasTextFocus())
{
ed = this.editor.getModifiedEditor();
}
}
const pos = ed.getPosition();
let mode = undefined;
const model = ed.getModel();
if(model)
{
mode = MonacoEditorModel.modes[model.getLanguageId()];
}
return { return {
row: pos.lineNumber, row: pos.lineNumber,
column: pos.column, column: pos.column,
line: this.editor.getModel().getLineCount(), line: model?model.getLineCount(): 0,
langmode: { langmode: {
text: mode.aliases[0], text: mode?mode.aliases[0]: "",
mode: mode mode: mode
}, },
file: this.currfile.path file: this.currfile.path
@ -260,7 +360,9 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
getValue(): string { getValue(): string {
return this.editor.getValue(); if(this.editor == this._code_editor)
return this.editor.getValue();
return this.currfile.cache;
} }
@ -271,7 +373,8 @@ namespace OS {
* @memberof MonacoEditorModel * @memberof MonacoEditorModel
*/ */
setValue(value: string): void { setValue(value: string): void {
this.editor.setValue(value); if(this.editor == this._code_editor)
this.editor.setValue(value);
} }
getEditor(): any { getEditor(): any {

View File

@ -234,6 +234,15 @@ namespace OS {
* @memberof Antedit * @memberof Antedit
*/ */
private split_mode: boolean; private split_mode: boolean;
/**
* Buffer for open diff
*
* @private
* @type {EditorFileHandle[]}
* @memberof Antedit
*/
private diff_buffer: EditorFileHandle[];
/** /**
* Reference to the editor logger * Reference to the editor logger
@ -270,6 +279,7 @@ namespace OS {
constructor(args: AppArgumentsType[]) { constructor(args: AppArgumentsType[]) {
super("Antedit", args); super("Antedit", args);
this.currdir = undefined; this.currdir = undefined;
this.diff_buffer = [undefined, undefined];
} }
/** /**
@ -374,7 +384,6 @@ namespace OS {
break; break;
case "close-all": case "close-all":
model.closeAll(); model.closeAll();
model.openFile("Untitled".asFileHandle() as EditorFileHandle)
break; break;
case "mv-side": case "mv-side":
if(!tab) if(!tab)
@ -435,13 +444,21 @@ namespace OS {
this.on("focus", () => this.eum.active.focus()); this.on("focus", () => this.eum.active.focus());
this.fileview.contextmenuHandle = (e, m) => { this.fileview.contextmenuHandle = (e, m) => {
m.items = [ let file: API.VFS.BaseFileHandle | API.FileInfoType = this
.fileview.selectedFile;
const items = [
{ text: "__(New file)", id: "new" }, { text: "__(New file)", id: "new" },
{ text: "__(New folder)", id: "newdir" }, { text: "__(New folder)", id: "newdir" },
{ text: "__(Rename)", id: "rename" }, { text: "__(Rename)", id: "rename" },
{ text: "__(Delete)", id: "delete" }, { text: "__(Delete)", id: "delete" },
{ text: "__(Upload)", id: "upload" }, { text: "__(Upload)", id: "upload" },
]; ];
if(file && file.type === "file")
{
items.push( { text: "__(Select for compare)", id: "diff-org" });
items.push( { text: "__(Compare with selected)", id: "diff-mod" });
}
m.items = items;
m.onmenuselect = (e) => { m.onmenuselect = (e) => {
return this.ctxFileMenuHandle(e); return this.ctxFileMenuHandle(e);
}; };
@ -512,6 +529,7 @@ namespace OS {
this.eum.addAction(extension, action, async (e) => this.eum.addAction(extension, action, async (e) =>
{ {
try{ try{
const data = await this.openDialog("SelectionDialog", { const data = await this.openDialog("SelectionDialog", {
"title": __("Select language"), "title": __("Select language"),
data: this.eum.active.getModes() data: this.eum.active.getModes()
@ -519,7 +537,7 @@ namespace OS {
this.eum.active.setMode(data); this.eum.active.setMode(data);
}catch(e) }catch(e)
{ {
console.log(e);
} }
}); });
$(this.find("txt_ext_search")).keyup((e) => this.extension_search(e)); $(this.find("txt_ext_search")).keyup((e) => this.extension_search(e));
@ -686,6 +704,12 @@ namespace OS {
this.showBottomBar(true); this.showBottomBar(true);
this.bottombar.selectedIndex = 0; this.bottombar.selectedIndex = 0;
} }
openDiff(files: EditorFileHandle[])
{
const diff_file = new API.VFS.DiffEditorFileHandle(files);
this.eum.active.openFile(diff_file as EditorFileHandle);
}
/** /**
* Apply [[showBottomBar]] from user setting value * Apply [[showBottomBar]] from user setting value
@ -953,6 +977,16 @@ namespace OS {
}) })
.catch((e) => this.error(__("Unable to upload file: {e}", e.toString()), __e(e))); .catch((e) => this.error(__("Unable to upload file: {e}", e.toString()), __e(e)));
break; break;
case "diff-org":
if(!file) return;
this.diff_buffer[0] = file.path.asFileHandle() as EditorFileHandle;
break;
case "diff-mod":
if(!file) return;
if(!this.diff_buffer[0]) return;
this.diff_buffer[1] = file.path.asFileHandle() as EditorFileHandle;
this.openDiff(this.diff_buffer);
break;
default: default:
} }
} }

View File

@ -45,7 +45,7 @@
"description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Antedit/README.md", "description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Antedit/README.md",
"category": "Development", "category": "Development",
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"version": "0.1.17-b", "version": "0.2.0-b",
"dependencies": ["MonacoCore@0.33.0-r"], "dependencies": ["MonacoCore@0.33.0-r"],
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Antedit/build/release/Antedit.zip" "download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Antedit/build/release/Antedit.zip"
}, },