Skip to content

Commit f282abe

Browse files
committed
feat: version selector + define_text_rewriter
1 parent 59899dd commit f282abe

File tree

4 files changed

+93
-21
lines changed

4 files changed

+93
-21
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"build": "npm-run-all --parallel build:*",
88
"watch:js": "node ./esbuild.serve.js",
99
"watch:css": "yarn build:css --watch",
10-
"serve": "npm-run-all --parallel watch:*"
10+
"dev": "npm-run-all --parallel watch:*"
1111
},
1212
"dependencies": {
1313
"@bjorn3/browser_wasi_shim": "^0.2.18",

src/app.js

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ puts greet.call(hello: 'martian')
1616

1717
const DEFAULT_PREVIEW = `# Here you will see the transpiled source code.`;
1818

19-
const CONFIG = `# Here you can define custom source rewriters.`;
19+
const CONFIG = `# Here you can define custom source rewriters.
20+
# For example, you can add ":=" operator (if you miss C) as follows:
21+
#
22+
# RubyNext.define_text_rewriter "operator_assign" do
23+
# def safe_rewrite(source)
24+
# source.gsub(":=", "=")
25+
# end
26+
# end
27+
`;
2028

2129
const OUTPUT = "// Here will be the output of your program";
2230

@@ -32,6 +40,7 @@ export default class App {
3240
this.monaco = monaco;
3341

3442
this.onSelectEditor = this.onSelectEditor.bind(this);
43+
this.invalidatePreview = this.invalidatePreview.bind(this);
3544
}
3645

3746
bootstrap() {
@@ -71,12 +80,7 @@ export default class App {
7180

7281
this.el
7382
.querySelector('[target="transpile-btn"]')
74-
.addEventListener("click", () => {
75-
const result = this.transpile(this.codeEditor.getValue());
76-
77-
this.previewEditor.setValue(result);
78-
this.showEditor("previewEditor");
79-
});
83+
.addEventListener("click", this.invalidatePreview);
8084

8185
this.el
8286
.querySelector('[target="run-btn"]')
@@ -96,15 +100,39 @@ export default class App {
96100

97101
this.el.addEventListener("change", this.onSelectEditor);
98102

99-
this.setCurrentVersion();
103+
this.versionSelect = document.getElementById("versionSelect");
104+
105+
if (theme === "dark") this.versionSelect.classList.add("sl-theme-dark");
106+
107+
this.versionSelect.addEventListener("sl-change", this.invalidatePreview);
108+
109+
this.setCurrentVMVersion();
100110
}
101111

102-
transpile(code) {
103-
const result = this.vm
104-
.eval("RubyNext.transform(%q(" + code + "))")
105-
.toString();
112+
transpile(code, opts = {}) {
113+
let rubyOptions = "{";
114+
115+
if (opts.version) {
116+
rubyOptions += `version: "${opts.version}"`;
117+
}
118+
119+
rubyOptions += "}";
106120

107-
return result;
121+
try {
122+
// eval configuration
123+
// first, reset custom rewriters
124+
this.vm.eval("RubyNext.custom_rewriters.clear");
125+
this.vm.eval(this.configEditor.getValue());
126+
127+
const result = this.vm
128+
.eval("RubyNext.transform(%q(" + code + "), **" + rubyOptions + ")")
129+
.toString();
130+
131+
return result;
132+
} catch (e) {
133+
console.error(e);
134+
return e.message;
135+
}
108136
}
109137

110138
execute(source) {
@@ -116,7 +144,7 @@ export default class App {
116144
}
117145
}
118146

119-
async setCurrentVersion() {
147+
async setCurrentVMVersion() {
120148
const versionContainer = document.getElementById("currentVersion");
121149
if (!versionContainer) return;
122150

@@ -137,6 +165,14 @@ export default class App {
137165
});
138166
}
139167

168+
invalidatePreview() {
169+
const version = this.versionSelect.value;
170+
const newSource = this.transpile(this.codeEditor.getValue(), { version });
171+
this.previewEditor.setValue(newSource);
172+
173+
this.showEditor("previewEditor");
174+
}
175+
140176
showEditor(editorName) {
141177
const editor = this.el.querySelector(`#${editorName}`);
142178
if (!editor) return;

src/index.html

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
<title>Ruby Next Playground</title>
77
<link rel="stylesheet" type="text/css" href="/app.css">
88
<link rel="stylesheet" type="text/css" href="/index.css">
9+
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/themes/light.css" />
11+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/themes/dark.css" />
12+
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/shoelace-autoloader.js"></script>
913
</head>
1014
<body class="w-full min-h-full">
1115
<main class="w-full h-full bg-slate-100 dark:bg-slate-900 text-slate-800 dark:text-slate-200">
@@ -43,16 +47,33 @@ <h2>Initializing Ruby Next playground...</h2>
4347
</div>
4448
<div id="app" class="hidden w-full h-screen flex flex-col">
4549
<nav class="flex-shrink flex flex-row justify-between items-middle border-b-1 border-slate-300 shadow-md">
46-
<div class="flex flex-row items-center justify-start">
50+
<div class="flex flex-row items-center justify-start ml-2">
4751
<img class="mx-2 p-1 h-12 w-auto" src="/assets/logo.png" alt="Ruby Next">
4852
<h1 class="text-2xl">Playground</h1>
4953
<span class="text-sm ml-4 h-4" id="currentVersion"></span>
5054
</div>
51-
<div class="flex flex-row space-x-2 self-center justify-center items-center px-2">
52-
<button target="transpile-btn">Transpile</button>
53-
<button target="run-btn">Run</button>
55+
<div class="flex flex-row space-x-2 self-center justify-center items-center px-2 align-middle">
56+
<button target="transpile-btn" class="bg-purple-500 hover:bg-purple-400 active:bg-purple-600 text-slate-50 dark:bg-purple-700 dark:hover:bg-purple-600 dark:active:bg-purple-800 dark:text-slate-100 py-2 px-4 font-semibold rounded inline-flex items-center">
57+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" data-slot="icon" class="w-6 h-6 mr-2">
58+
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z" />
59+
</svg>
60+
<span>Transpile</span>
61+
</button>
62+
<sl-select id="versionSelect" value="2.5.0" class="w-32">
63+
<sl-option value="2.5.0">2.5.0</sl-option>
64+
<sl-option value="2.7.0">2.7.0</sl-option>
65+
<sl-option value="3.1.0">3.1.0</sl-option>
66+
<sl-option value="3.2.0">3.2.0</sl-option>
67+
<sl-option value="3.3.0">3.3.0</sl-option>
68+
</sl-select>
69+
<button target="run-btn" class="bg-green-500 hover:bg-green-400 active:bg-green-600 text-slate-50 dark:bg-green-700 dark:hover:bg-green-600 dark:active:bg-green-800 dark:text-slate-100 py-2 px-4 font-semibold rounded inline-flex items-center">
70+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-2">
71+
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z" />
72+
</svg>
73+
<span>Run</span>
74+
</button>
5475
</div>
55-
<div class="flex flex-row space-x-2 justify-center items-center px-2">
76+
<div class="flex flex-row space-x-2 justify-center items-center px-2 mr-4">
5677
<a href="https://github.com/ruby-next/ruby-next" target="_blank" class="flex flex-row items-center space-x-1 hover:opacity-75 cursor-pointer">
5778
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" stroke="none" class="w-6 h-6 inline" data-slot="icon">
5879
<g buffered-rendering="static">

src/vm.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,25 @@ export default async function initVM() {
4040
require "ruby-next/language/rewriters/edge"
4141
require "ruby-next/language/rewriters/proposed"
4242
43+
4344
module RubyNext
45+
class << self
46+
attr_accessor :custom_rewriters
47+
end
48+
49+
self.custom_rewriters = []
50+
51+
def self.define_text_rewriter(name, &block)
52+
Class.new(RubyNext::Language::Rewriters::Text, &block).tap do |rw|
53+
rw.const_set(:NAME, name)
54+
rw.const_set(:MIN_SUPPORTED_VERSION, Gem::Version.new(RubyNext::NEXT_VERSION))
55+
custom_rewriters << rw
56+
end
57+
end
58+
4459
def self.transform(code, version: RUBY_VERSION, using: false)
4560
options = {using:}
46-
options[:rewriters] = Language.rewriters.select { |rw| rw.unsupported_version?(version) }
61+
options[:rewriters] = Language.rewriters.select { |rw| rw.unsupported_version?(version) } + custom_rewriters
4762
4863
source = Language.transform(code, **options)
4964
source += "\n # Transformed with RubyNext v#{RubyNext::VERSION} for Ruby #{version}"

0 commit comments

Comments
 (0)