From c3616b25f360a8232f673e34b9e3cf3370d09a5c Mon Sep 17 00:00:00 2001 From: Joseph Abbey Date: Tue, 25 Oct 2022 09:50:17 +0100 Subject: [PATCH] context menus --- examples/binary_counter.vhdl.sbd | 2 +- examples/binary_counter.vhdl.sbe | 2 +- examples/ieee.numeric_std_unsigned.sbi | 41 ++-- scratch-vhdl-vscode/media/main.js | 230 +++++++++++++++++-- scratch-vhdl-vscode/src/scratchVHDLEditor.ts | 98 +++++++- 5 files changed, 329 insertions(+), 44 deletions(-) diff --git a/examples/binary_counter.vhdl.sbd b/examples/binary_counter.vhdl.sbd index 1cf358c..522b487 100644 --- a/examples/binary_counter.vhdl.sbd +++ b/examples/binary_counter.vhdl.sbd @@ -1 +1 @@ -{"blocks":{"languageVersion":0,"blocks":[{"type":"process","id":"avqozk~ylO6~9`OoO-tF","x":463,"y":13,"extraState":{"depCount":1},"fields":{"1":{"id":"5Y9_SLHF8Bd4|n|ymNgJ"}},"inputs":{"body":{"block":{"type":"controls_if","id":"7)2aH8jQ`CxLSWy}UAgr","inputs":{"IF0":{"block":{"type":"logic_rising_edge","id":"0QWfBso?I$XYQ8@mej5}","fields":{"dep":{"id":"5Y9_SLHF8Bd4|n|ymNgJ"}}}},"DO0":{"block":{"type":"controls_if","id":"|iCK:,3?*n,e{^9zO]%y","extraState":{"hasElse":true},"inputs":{"IF0":{"block":{"type":"logic_compare","id":"B=Og_Cm*AO)T}CKQAZXF","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"Db8Ohuaw7PUe(HOT?d:%","fields":{"VAR":{"id":"2G:Z~BB|CAam0[(.o??k"}}}},"B":{"block":{"type":"value_std_logic","id":"HV[(76@,1o#o|NIyg{4L","fields":{"VALUE":1}}}}}},"DO0":{"block":{"type":"variables_set","id":")~8k1(;U?4ft4XPFi{@o","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}},"inputs":{"VALUE":{"block":{"type":"value_std_logic","id":"Xxc}cOExM?./jx~mV8*O","fields":{"VALUE":0}}}},"next":{"block":{"type":"variables_set","id":"Cf-SgU0h/|v-!Q^9k-b6","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}},"inputs":{"VALUE":{"block":{"type":"math_number","id":"ma#QQ4qfMTB$a6#(=M?p","fields":{"NUM":0}}}}}}}},"ELSE":{"block":{"type":"controls_if","id":"f-,*:@2#uyD21rpE2f)f","extraState":{"elseIfCount":1},"inputs":{"IF0":{"block":{"type":"logic_compare","id":"2nY3KBpnFQ.2yXV[x68L","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"{x_Iv}av0ah8zVeQ%V+;","fields":{"VAR":{"id":"r]hfmAi8AfzG[s)uc9w+"}}}},"B":{"block":{"type":"value_std_logic","id":"~(@!41XyTF6o`q~PKqdq","fields":{"VALUE":1}}}}}},"DO0":{"block":{"type":"variables_set","id":"}K[KHLm0?`E~d/(XP1vh","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}},"inputs":{"VALUE":{"block":{"type":"value_std_logic","id":"UV9mO#P,pc?1F+kN-|~]","fields":{"VALUE":1}}}}}},"IF1":{"block":{"type":"logic_compare","id":"u1MTJoP`db#/(ia?gQFr","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"bjF?^Q8dAC9N)lm6sD/v","fields":{"VAR":{"id":"Mx0l_Ufp-l?o.oic9dG("}}}},"B":{"block":{"type":"value_std_logic","id":":/K-oZGH+Df@l|.cNM7W","fields":{"VALUE":1}}}}}},"DO1":{"block":{"type":"variables_set","id":":2}zwaU/+0U_^^X6SdVZ","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}},"inputs":{"VALUE":{"block":{"type":"value_std_logic","id":"j]v$V?;+-|3c6IA86}%b","fields":{"VALUE":0}}}}}}},"next":{"block":{"type":"controls_if","id":"+w#Q=H/Nwo|sX*WnGu_7","inputs":{"IF0":{"block":{"type":"logic_operation","id":"ct^,b/*0mfrz)hZ96%SR","fields":{"OPERATION":"and"},"inputs":{"A":{"block":{"type":"logic_compare","id":"ar033LzB:8~Asbfhqc1G","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"14Nh=)5qx3Kp7CnM{_3Q","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}}}},"B":{"block":{"type":"value_std_logic","id":"`Z:vS*c~|rDt;H^|*;6/","fields":{"VALUE":1}}}}}},"B":{"block":{"type":"logic_compare","id":"wjWT^X5D+m=*SCJgao1i","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"w%M}[tqe1tZ~$hGT{Auh","fields":{"VAR":{"id":"vLZ;k9sxoNVKVc?I}kk|"}}}},"B":{"block":{"type":"value_std_logic","id":"ivUlYk}vMq%}C_o|+hpe","fields":{"VALUE":1}}}}}}}}},"DO0":{"block":{"type":"controls_if","id":"02fAV_-?A;IM5h+kf{B4","extraState":{"hasElse":true},"icons":{"comment":{"text":"to prevent natural overflow","pinned":true,"height":80,"width":160}},"inputs":{"IF0":{"block":{"type":"logic_compare","id":"RzN!wxca#NjI~DODa)C=","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"%@),sr{`cx-V9bcvV48S","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}}}},"B":{"block":{"type":"math_number","id":"[BHDfT^}`|fQA`/=mn}5","fields":{"NUM":15}}}}}},"DO0":{"block":{"type":"variables_set","id":":uxhh-O5/%w=;qFRPM3e","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}},"inputs":{"VALUE":{"block":{"type":"math_number","id":"d7`j(wM,Ck,x#R.h4hX+","fields":{"NUM":0}}}}}},"ELSE":{"block":{"type":"math_change","id":"q$0[C|3apsd3w^;44!%{","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}},"inputs":{"DELTA":{"shadow":{"type":"math_number","id":"N][!U{H/z_@V-SvgJBKp","fields":{"NUM":1}}}}}}}}}}}}}}}}}}}}}},{"type":"process_direct_set","id":"2WV_x^9Fn^K5~HN@?IpG","x":-12,"y":13,"fields":{"VAR":{"id":"zZ.FFw)dey[B4Vwn^yh`"}},"inputs":{"VALUE":{"block":{"type":"ieee.numeric_std_unsigned_to_stdulogicvector","id":")LdWhR+dpy:CwuP0dPr@","inputs":{"NUM":{"block":{"type":"variables_get","id":"+]QX^TgSSU{H:QQ1jOID","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}}}}}}}}}]},"variables":[{"name":"cnt","id":"gWcVR:~_ouWxb9@{IwGa"},{"name":"run","id":"E0tQW(i(Up||M4R4RYG["},{"name":"clk","id":"5Y9_SLHF8Bd4|n|ymNgJ"},{"name":"reset","id":"2G:Z~BB|CAam0[(.o??k"},{"name":"incr","id":"vLZ;k9sxoNVKVc?I}kk|"},{"name":"buttons","id":"N;q[+Y+(ah3cQgJ:uCc]"},{"name":"leds","id":"zZ.FFw)dey[B4Vwn^yh`"},{"name":"start","id":"r]hfmAi8AfzG[s)uc9w+"},{"name":"stop","id":"Mx0l_Ufp-l?o.oic9dG("}],"signals":{"cnt":"integer range 0 to 15","run":"std_logic"}} \ No newline at end of file +{"blocks":{"languageVersion":0,"blocks":[{"type":"process","id":"avqozk~ylO6~9`OoO-tF","x":463,"y":13,"extraState":{"depCount":1},"fields":{"1":{"id":"5Y9_SLHF8Bd4|n|ymNgJ"}},"inputs":{"body":{"block":{"type":"controls_if","id":"7)2aH8jQ`CxLSWy}UAgr","inputs":{"IF0":{"block":{"type":"logic_rising_edge","id":"0QWfBso?I$XYQ8@mej5}","fields":{"dep":{"id":"5Y9_SLHF8Bd4|n|ymNgJ"}}}},"DO0":{"block":{"type":"controls_if","id":"|iCK:,3?*n,e{^9zO]%y","extraState":{"hasElse":true},"inputs":{"IF0":{"block":{"type":"logic_compare","id":"B=Og_Cm*AO)T}CKQAZXF","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"Db8Ohuaw7PUe(HOT?d:%","fields":{"VAR":{"id":"2G:Z~BB|CAam0[(.o??k"}}}},"B":{"block":{"type":"value_std_logic","id":"HV[(76@,1o#o|NIyg{4L","fields":{"VALUE":1}}}}}},"DO0":{"block":{"type":"variables_set","id":")~8k1(;U?4ft4XPFi{@o","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}},"inputs":{"VALUE":{"block":{"type":"value_std_logic","id":"Xxc}cOExM?./jx~mV8*O","fields":{"VALUE":0}}}},"next":{"block":{"type":"variables_set","id":"Cf-SgU0h/|v-!Q^9k-b6","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}},"inputs":{"VALUE":{"block":{"type":"math_number","id":"ma#QQ4qfMTB$a6#(=M?p","fields":{"NUM":0}}}}}}}},"ELSE":{"block":{"type":"controls_if","id":"f-,*:@2#uyD21rpE2f)f","extraState":{"elseIfCount":1},"inputs":{"IF0":{"block":{"type":"logic_compare","id":"2nY3KBpnFQ.2yXV[x68L","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"{x_Iv}av0ah8zVeQ%V+;","fields":{"VAR":{"id":"r]hfmAi8AfzG[s)uc9w+"}}}},"B":{"block":{"type":"value_std_logic","id":"~(@!41XyTF6o`q~PKqdq","fields":{"VALUE":1}}}}}},"DO0":{"block":{"type":"variables_set","id":"}K[KHLm0?`E~d/(XP1vh","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}},"inputs":{"VALUE":{"block":{"type":"value_std_logic","id":"UV9mO#P,pc?1F+kN-|~]","fields":{"VALUE":1}}}}}},"IF1":{"block":{"type":"logic_compare","id":"u1MTJoP`db#/(ia?gQFr","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"bjF?^Q8dAC9N)lm6sD/v","fields":{"VAR":{"id":"Mx0l_Ufp-l?o.oic9dG("}}}},"B":{"block":{"type":"value_std_logic","id":":/K-oZGH+Df@l|.cNM7W","fields":{"VALUE":1}}}}}},"DO1":{"block":{"type":"variables_set","id":":2}zwaU/+0U_^^X6SdVZ","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}},"inputs":{"VALUE":{"block":{"type":"value_std_logic","id":"j]v$V?;+-|3c6IA86}%b","fields":{"VALUE":0}}}}}}},"next":{"block":{"type":"controls_if","id":"+w#Q=H/Nwo|sX*WnGu_7","inputs":{"IF0":{"block":{"type":"logic_operation","id":"ct^,b/*0mfrz)hZ96%SR","fields":{"OPERATION":"and"},"inputs":{"A":{"block":{"type":"logic_compare","id":"ar033LzB:8~Asbfhqc1G","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"14Nh=)5qx3Kp7CnM{_3Q","fields":{"VAR":{"id":"E0tQW(i(Up||M4R4RYG["}}}},"B":{"block":{"type":"value_std_logic","id":"`Z:vS*c~|rDt;H^|*;6/","fields":{"VALUE":1}}}}}},"B":{"block":{"type":"logic_compare","id":"wjWT^X5D+m=*SCJgao1i","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"w%M}[tqe1tZ~$hGT{Auh","fields":{"VAR":{"id":"vLZ;k9sxoNVKVc?I}kk|"}}}},"B":{"block":{"type":"value_std_logic","id":"ivUlYk}vMq%}C_o|+hpe","fields":{"VALUE":1}}}}}}}}},"DO0":{"block":{"type":"controls_if","id":"02fAV_-?A;IM5h+kf{B4","extraState":{"hasElse":true},"icons":{"comment":{"text":"to prevent natural overflow","pinned":true,"height":80,"width":160}},"inputs":{"IF0":{"block":{"type":"logic_compare","id":"RzN!wxca#NjI~DODa)C=","fields":{"OP":"EQ"},"inputs":{"A":{"block":{"type":"variables_get","id":"%@),sr{`cx-V9bcvV48S","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}}}},"B":{"block":{"type":"math_number","id":"[BHDfT^}`|fQA`/=mn}5","fields":{"NUM":15}}}}}},"DO0":{"block":{"type":"variables_set","id":":uxhh-O5/%w=;qFRPM3e","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}},"inputs":{"VALUE":{"block":{"type":"math_number","id":"d7`j(wM,Ck,x#R.h4hX+","fields":{"NUM":0}}}}}},"ELSE":{"block":{"type":"math_change","id":"q$0[C|3apsd3w^;44!%{","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}},"inputs":{"DELTA":{"shadow":{"type":"math_number","id":"N][!U{H/z_@V-SvgJBKp","fields":{"NUM":1}}}}}}}}}}}}}}}}}}}}}},{"type":"process_direct_set","id":"2WV_x^9Fn^K5~HN@?IpG","x":-12,"y":13,"fields":{"VAR":{"id":"zZ.FFw)dey[B4Vwn^yh`"}},"inputs":{"VALUE":{"block":{"type":"ieee.numeric_std_unsigned.to_stdulogicvector","id":")LdWhR+dpy:CwuP0dPr@","inputs":{"NUM":{"block":{"type":"variables_get","id":"+]QX^TgSSU{H:QQ1jOID","fields":{"VAR":{"id":"gWcVR:~_ouWxb9@{IwGa"}}}}}}}}}]},"variables":[{"name":"cnt","id":"gWcVR:~_ouWxb9@{IwGa"},{"name":"run","id":"E0tQW(i(Up||M4R4RYG["},{"name":"clk","id":"5Y9_SLHF8Bd4|n|ymNgJ"},{"name":"reset","id":"2G:Z~BB|CAam0[(.o??k"},{"name":"incr","id":"vLZ;k9sxoNVKVc?I}kk|"},{"name":"buttons","id":"N;q[+Y+(ah3cQgJ:uCc]"},{"name":"leds","id":"zZ.FFw)dey[B4Vwn^yh`"},{"name":"start","id":"r]hfmAi8AfzG[s)uc9w+"},{"name":"stop","id":"Mx0l_Ufp-l?o.oic9dG("}],"signals":{"cnt":"integer range 0 to 15","run":"std_logic"}} \ No newline at end of file diff --git a/examples/binary_counter.vhdl.sbe b/examples/binary_counter.vhdl.sbe index 54f22aa..c32afd3 100644 --- a/examples/binary_counter.vhdl.sbe +++ b/examples/binary_counter.vhdl.sbe @@ -15,7 +15,7 @@ }, "libraries": { "ieee": { - "numeric_std_unsigned": "ieee.numeric_std_unsigned.sbi" + "numeric_std_unsigned": "./ieee.numeric_std_unsigned.sbi" } } } diff --git a/examples/ieee.numeric_std_unsigned.sbi b/examples/ieee.numeric_std_unsigned.sbi index ce26d05..019615e 100644 --- a/examples/ieee.numeric_std_unsigned.sbi +++ b/examples/ieee.numeric_std_unsigned.sbi @@ -1,20 +1,23 @@ -[ - { - "block": { - "kind": "block", - "type": "ieee.numeric_std_unsigned_to_stdulogicvector", - "message0": "to_stdulogicvector %1", - colour, - "args0": [ - { - "type": "input_value", - "name": "NUM" - } - ], - "output": null, - }, - "generator": function (block) { - return ["to_stdulogicvector(" + generator.valueToCode(block, "NUM", generator.ORDER_NONE) + ")", generator.ORDER_FUNCTION_CALL] +({ + name: "ieee.numeric_std_undinged", + blocks: [ + { + "block": { + "kind": "block", + "type": "ieee.numeric_std_unsigned.to_stdulogicvector", + "message0": "to_stdulogicvector %1", + colour, + "args0": [ + { + "type": "input_value", + "name": "NUM" + } + ], + "output": null, + }, + "generator": function (block) { + return ["to_stdulogicvector(" + generator.valueToCode(block, "NUM", generator.ORDER_NONE) + ")", generator.ORDER_FUNCTION_CALL] + } } - } -] \ No newline at end of file + ] +}) \ No newline at end of file diff --git a/scratch-vhdl-vscode/media/main.js b/scratch-vhdl-vscode/media/main.js index d477d50..c3a526b 100644 --- a/scratch-vhdl-vscode/media/main.js +++ b/scratch-vhdl-vscode/media/main.js @@ -57,6 +57,23 @@ document.addEventListener('DOMContentLoaded', function () { }); return new Promise((resolve) => requests[rid++] = resolve); }; + window.addEventListener("message", (message) => message.data.type == "select" && requests[message.data.id](message.data.body)); + const select = (o) => { + vscode.postMessage({ + type: "select", + body: o, + id: rid + }); + return new Promise((resolve) => requests[rid++] = resolve); + }; + window.addEventListener("message", (message) => message.data.type == "selectFile" && requests[message.data.id](message.data.body)); + const selectFile = () => { + vscode.postMessage({ + type: "selectFile", + id: rid + }); + return new Promise((resolve) => requests[rid++] = resolve); + }; window.addEventListener("message", (message) => message.data.type == "getFile" && requests[message.data.id](message.data.body)); const getFile = (m) => { vscode.postMessage({ @@ -744,7 +761,7 @@ document.addEventListener('DOMContentLoaded', function () { }); })(); - const theme = Blockly.Theme.defineTheme('dark', { + const theme = Blockly.Theme.defineTheme('vscode', { 'base': Blockly.Themes.Classic, 'componentStyles': { 'workspaceBackgroundColour': 'var(--vscode-editor-background)', @@ -910,7 +927,7 @@ document.addEventListener('DOMContentLoaded', function () { }; const ws = Blockly.inject('root', { - toolbox: toolbox, + toolbox: structuredClone(toolbox), grid: { spacing: 25, length: 3, @@ -922,11 +939,7 @@ document.addEventListener('DOMContentLoaded', function () { }); let entity = {}; - let libraries = { - "ieee": { - "std_logic_1164": null - } - }; + let libraries = {}; let signals = {}; let constants = {}; let aliases = {}; @@ -935,6 +948,184 @@ document.addEventListener('DOMContentLoaded', function () { let constantsCode = ""; let aliasesCode = ""; + Blockly.ContextMenuRegistry.registry.register({ + displayText: "View Code", + preconditionFn() { + return "enabled"; + }, + callback() { + vscode.postMessage({ + type: "code" + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'viewCode', + weight: -1, + }); + + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Add port", + preconditionFn() { + return "enabled"; + }, + callback() { + prompt("New port name: ").then((n) => { + prompt("New port direction: ").then((d) => { + prompt("New port type: ").then((t) => { + entity.entity[n] = [d, t]; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'addPort', + weight: 9.0, + }); + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Remove port", + preconditionFn() { + return "enabled"; + }, + callback() { + select(Object.keys(entity.entity)).then((s) => { + delete entity.entity[s]; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'removePort', + weight: 9.1, + }); + + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Add library", + preconditionFn() { + return "enabled"; + }, + callback() { + selectFile().then(async (p) => { + const lib = eval("((colour, generator)=>" + await getFile(p) + ")")().name.split("."); + entity.libraries = entity.libraries || {}; + entity.libraries[lib[0]] = entity.libraries[lib[0]] || {}; + entity.libraries[lib[0]][lib[1]] = p; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'addLibrary', + weight: 10.0, + }); + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Remove library", + preconditionFn() { + return "enabled"; + }, + callback() { + const libs = Object.keys(entity.libraries).flatMap((l) => Object.keys(entity.libraries[l]).map((p) => l + "." + p)); + select(libs).then((s) => { + const lib = s.split("."); + delete entity.libraries[lib[0]][lib[1]]; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'removeLibrary', + weight: 10.1, + }); + + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Add constant", + preconditionFn() { + return "enabled"; + }, + callback() { + prompt("New constant name: ").then((n) => { + prompt("New constant type: ").then((t) => { + prompt("New constant value: ").then((v) => { + entity.constants[n] = [t, v]; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'addConstant', + weight: 11.0, + }); + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Remove constant", + preconditionFn() { + return "enabled"; + }, + callback() { + select(Object.keys(entity.constants)).then((s) => { + delete entity.constants[s]; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'removeConstant', + weight: 11.1, + }); + + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Add alias", + preconditionFn() { + return "enabled"; + }, + callback() { + prompt("New alias name: ").then((n) => { + prompt("New alias value: ").then((v) => { + entity.aliases[n] = v; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'addAlias', + weight: 12.0, + }); + Blockly.ContextMenuRegistry.registry.register({ + displayText: "Remove alias", + preconditionFn() { + return "enabled"; + }, + callback() { + select(Object.keys(entity.aliases)).then((s) => { + delete entity.aliases[s]; + vscode.postMessage({ + type: "updateEntity", + body: JSON.stringify(entity) + }); + }); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: 'removeAlias', + weight: 12.1, + }); + Blockly.serialization.registry.register( 'signals', { @@ -951,8 +1142,8 @@ document.addEventListener('DOMContentLoaded', function () { if (!args[2] && args[0] == "CREATE_VARIABLE") { ws.removeButtonCallback("CREATE_VARIABLE"); ws.registerButtonCallback("CREATE_VARIABLE", (d) => { - prompt("New variable type: ").then((t) => { - prompt("New variable name: ").then((n) => { + prompt("New variable name: ").then((n) => { + prompt("New variable type: ").then((t) => { d.getTargetWorkspace().createVariable(n); signals[n] = t; }); @@ -962,7 +1153,6 @@ document.addEventListener('DOMContentLoaded', function () { } })(); - // TODO: this should get the name from the vscode api let name = "hi"; window.addEventListener('message', @@ -1024,7 +1214,10 @@ document.addEventListener('DOMContentLoaded', function () { constants = entity.constants || constants; aliases = entity.aliases || aliases; - if (entity.constants) { + // reset toolbox + ws.updateToolbox(structuredClone(toolbox)); + + if (constants) { Blockly.defineBlocksWithJsonArray([{ type: "constant", message0: "%1", @@ -1070,7 +1263,11 @@ document.addEventListener('DOMContentLoaded', function () { })); } - libraries = mergeDeep(libraries, entity.libraries); + libraries = mergeDeep({ + "ieee": { + "std_logic_1164": null + } + }, entity.libraries); categories = []; for (const a in libraries) { @@ -1082,7 +1279,7 @@ document.addEventListener('DOMContentLoaded', function () { else if (libraries[a][lib] === "builtin") def = builtinLibs[a][lib]; else - def = eval("((colour, generator)=>" + await getFile(libraries[a][lib]) + ")")(colour, VHDLGenerator); + def = eval("((colour, generator)=>" + await getFile(libraries[a][lib]) + ")")(colour, VHDLGenerator).blocks; c.push({ kind: "category", colour, @@ -1120,6 +1317,13 @@ document.addEventListener('DOMContentLoaded', function () { ws.addChangeListener((e) => { if (e.isUiEvent) return; + if (e.type == Blockly.Events.VAR_DELETE) + delete signals[e.varName]; + else if (e.type == Blockly.Events.VAR_DELETE) { + signals[e.newName] = signals[e.oldName]; + delete signals[e.oldName]; + } + vscode.postMessage({ type: "update", body: JSON.stringify(Blockly.serialization.workspaces.save(ws)) diff --git a/scratch-vhdl-vscode/src/scratchVHDLEditor.ts b/scratch-vhdl-vscode/src/scratchVHDLEditor.ts index b3bd64a..c9b4b1e 100644 --- a/scratch-vhdl-vscode/src/scratchVHDLEditor.ts +++ b/scratch-vhdl-vscode/src/scratchVHDLEditor.ts @@ -22,10 +22,10 @@ class ScratchVHDLDocument extends Disposable implements vscode.CustomDocument { uri, await ScratchVHDLDocument.readFile(uri), (await ScratchVHDLDocument.readFile( - vscode.Uri.parse(uri.toString() + '.sbd') + vscode.Uri.parse(uri.toString() + '.sbd', true) )) || '{}', (await ScratchVHDLDocument.readFile( - vscode.Uri.parse(uri.toString() + '.sbe') + vscode.Uri.parse(uri.toString() + '.sbe', true) )) || '{"entity":{}}', delegate ); @@ -42,7 +42,10 @@ class ScratchVHDLDocument extends Disposable implements vscode.CustomDocument { readLocalFile(p: string): Promise { return ScratchVHDLDocument.readFile( - vscode.Uri.parse(path.dirname(this.uri.toString()) + '/' + p) + vscode.Uri.parse( + 'file:///' + path.resolve(path.dirname(this.uri.fsPath), p), + true + ) ); } @@ -81,6 +84,10 @@ class ScratchVHDLDocument extends Disposable implements vscode.CustomDocument { return this._scratchData; } + public set entityData(d: string) { + this._entityData = d; + } + public get entityData(): string { return this._entityData; } @@ -452,6 +459,18 @@ export class ScratchVHDLEditorProvider margin: 0; line-height: 1.5; } + + .vscode-theme .blocklyContextMenu { + background-color: var(--vscode-menu-background); + } + + .vscode-theme .blocklyContextMenu .blocklyMenuItem.blocklyMenuItemDisabled > .blocklyMenuItemContent { + color: var(--vscode-disabledForeground); + } + + .vscode-theme .blocklyContextMenu .blocklyMenuItem:not(.blocklyMenuItemDisabled) > .blocklyMenuItemContent { + color: var(--vscode-menu-foreground); + } @@ -511,6 +530,15 @@ export class ScratchVHDLEditorProvider }); return; + case 'updateEntity': + document.entityData = message.body; + panel.webview.postMessage({ + type: 'entity', + body: document.entityData, + file_name: path.basename(document.uri.fsPath).split('.')[0], + }); + return; + case 'requestUpdate': panel.webview.postMessage({ type: 'contentUpdate', @@ -518,6 +546,24 @@ export class ScratchVHDLEditorProvider }); return; + case 'code': + vscode.commands.executeCommand( + 'vscode.openWith', + document.uri, + 'default' + ); + return; + + case 'getFile': + document.readLocalFile(message.path).then((value) => + panel.webview.postMessage({ + type: 'getFile', + id: message.id, + body: value, + }) + ); + return; + case 'alert': vscode.window.showInformationMessage(message.body); return; @@ -546,15 +592,47 @@ export class ScratchVHDLEditorProvider ); return; - case 'getFile': - document.readLocalFile(message.path).then((value) => - panel.webview.postMessage({ - type: 'getFile', - id: message.id, - body: value, + case 'select': + vscode.window + .showQuickPick(message.body, { + canPickMany: false, }) - ); + .then((value) => + panel.webview.postMessage({ + type: 'select', + id: message.id, + body: value, + }) + ); + return; + + case 'selectFile': { + vscode.window + .showOpenDialog({ + canSelectMany: false, + openLabel: 'Select', + canSelectFiles: true, + canSelectFolders: false, + }) + .then((fileUri) => { + if (fileUri) + panel.webview.postMessage({ + type: 'selectFile', + id: message.id, + body: path.relative( + path.dirname(document.uri.fsPath), + fileUri[0].fsPath + ), + }); + else + panel.webview.postMessage({ + type: 'selectFile', + id: message.id, + body: undefined, + }); + }); return; + } } } }