-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add persistence to online sandbox editor #323
Changes from all commits
51bedc0
fa92ace
5704e9c
1ed362c
9f04b23
3472ecb
c6dd9f9
2731e3e
18b062f
9949b58
478899e
9c60891
16afdf2
d744941
7cf0632
a979d8d
b1d114a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"use strict"; | ||
|
||
module.exports = { presets: ["@babel/preset-env"] }; | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import LZString from "../vendor/lz-string/lz-string.js"; | ||
|
||
/** | ||
* @typedef {Object} SandboxState | ||
* @property {string} grammar | ||
* @property {string} input | ||
*/ | ||
|
||
// The key used to store the sandbox in local/session storage | ||
export const stateStorageKey = `sandbox-code`; | ||
|
||
/** | ||
* @param {SandboxState} state | ||
*/ | ||
export const saveSandboxStateToStorage = (state) => { | ||
localStorage.setItem(stateStorageKey, JSON.stringify(state)); | ||
}; | ||
|
||
/** | ||
* @typedef {SandboxState} SandboxExample | ||
* @property {string} name | ||
*/ | ||
|
||
/** @type {Array<SandboxState>} */ | ||
export const examples = [ | ||
{ | ||
name: "Simple arithmetic grammar", | ||
grammar: ` | ||
// Simple Arithmetics Grammar | ||
// ========================== | ||
// | ||
// Accepts expressions like "2 * (3 + 4)" and computes their value. | ||
|
||
Expression | ||
= head:Term tail:(_ ("+" / "-") _ Term)* { | ||
return tail.reduce(function(result, element) { | ||
if (element[1] === "+") { return result + element[3]; } | ||
if (element[1] === "-") { return result - element[3]; } | ||
}, head); | ||
} | ||
|
||
Term | ||
= head:Factor tail:(_ ("*" / "/") _ Factor)* { | ||
return tail.reduce(function(result, element) { | ||
if (element[1] === "*") { return result * element[3]; } | ||
if (element[1] === "/") { return result / element[3]; } | ||
}, head); | ||
} | ||
|
||
Factor | ||
= "(" _ expr:Expression _ ")" { return expr; } | ||
/ Integer | ||
|
||
Integer "integer" | ||
= _ [0-9]+ { return parseInt(text(), 10); } | ||
|
||
_ "whitespace" | ||
= [ \\t\\n\\r]*`, | ||
input: `2 * (3 + 4)`, | ||
}, | ||
]; | ||
|
||
/** | ||
* @param {URL} url | ||
* @returns {SandboxState} | ||
*/ | ||
export function getSandboxInitialState(url) { | ||
if (url.hash.startsWith("#state/")) { | ||
const state = url.hash.substring(7); | ||
try { | ||
const decodedState = JSON.parse( | ||
LZString.decompressFromEncodedURIComponent(state) | ||
); | ||
return decodedState; | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
|
||
const storedStateRaw = localStorage.getItem(stateStorageKey); | ||
if (storedStateRaw !== null) { | ||
try { | ||
/** @type {SandboxState} */ | ||
const storedState = JSON.parse(storedStateRaw); | ||
return storedState; | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
|
||
return { | ||
grammar: examples[0].grammar, | ||
input: examples[0].input, | ||
}; | ||
} | ||
|
||
/** | ||
* @param {SandboxState} state | ||
* @param {URL | string | undefined} baseUrl | ||
* @returns {string} | ||
*/ | ||
export function getEncodedSandboxUrl(state, baseUrl = undefined) { | ||
const encodedState = LZString.compressToEncodedURIComponent( | ||
JSON.stringify(state) | ||
); | ||
if (baseUrl) { | ||
return `${ | ||
typeof baseUrl === "string" ? baseUrl : baseUrl.toString() | ||
}#state/${encodedState}`; | ||
} else { | ||
return `#state/${encodedState}`; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,14 +8,18 @@ | |
"/vendor/codemirror/codemirror.css", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be vendored? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could load it directly from the CDN, but then it wouldn't work in the tests anymore, because we reference the library directly there. I don't believe there is any way in Jest/Node to load from URLs directly, unfortunately. Another advantage of vendoring is that I changed the library to use standard (In the long run, we should just |
||
"/vendor/codemirror/lint.css" | ||
] | ||
modules: [ | ||
"/vendor/lz-string/lz-string.js", | ||
"/js/sandbox.js", | ||
"/js/online.js" | ||
] | ||
scripts: [ | ||
"https://unpkg.com/[email protected]/dist/jquery.min.js", | ||
"https://unpkg.com/[email protected]/dist/FileSaver.min.js", | ||
"https://unpkg.com/[email protected]/dist/inspect.js", | ||
"/vendor/peggy/peggy.min.js", | ||
"/vendor/codemirror/codemirror.js", | ||
"/vendor/codemirror/lint.js", | ||
"/js/online.js" | ||
"/vendor/codemirror/lint.js" | ||
] | ||
--- | ||
|
||
|
@@ -34,43 +38,15 @@ | |
<h2 class="suggestion top"> | ||
<span class="step-number">1</span> | ||
<div class="step-title">Write your Peggy grammar</div> | ||
<button id="copy-link">Copy link</button> | ||
</h2> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<div class="textarea-wrapper"> | ||
<textarea class="code" id="grammar" autocomplete="off" autocorrect="off" | ||
autocapitalize="off" spellcheck="false" disabled>// Simple Arithmetics Grammar | ||
// ========================== | ||
// | ||
// Accepts expressions like "2 * (3 + 4)" and computes their value. | ||
|
||
Expression | ||
= head:Term tail:(_ ("+" / "-") _ Term)* { | ||
return tail.reduce(function(result, element) { | ||
if (element[1] === "+") { return result + element[3]; } | ||
if (element[1] === "-") { return result - element[3]; } | ||
}, head); | ||
} | ||
|
||
Term | ||
= head:Factor tail:(_ ("*" / "/") _ Factor)* { | ||
return tail.reduce(function(result, element) { | ||
if (element[1] === "*") { return result * element[3]; } | ||
if (element[1] === "/") { return result / element[3]; } | ||
}, head); | ||
} | ||
|
||
Factor | ||
= "(" _ expr:Expression _ ")" { return expr; } | ||
/ Integer | ||
|
||
Integer "integer" | ||
= _ [0-9]+ { return parseInt(text(), 10); } | ||
|
||
_ "whitespace" | ||
= [ \t\n\r]*</textarea> | ||
autocapitalize="off" spellcheck="false" disabled></textarea> | ||
</div> | ||
</td> | ||
</tr> | ||
|
@@ -95,7 +71,7 @@ <h2 class="suggestion top"> | |
<td> | ||
<div class="textarea-wrapper"> | ||
<textarea class="code" id="input" autocomplete="off" autocorrect="off" autocapitalize="off" | ||
spellcheck="false" disabled>2 * (3 + 4)</textarea> | ||
spellcheck="false" disabled></textarea> | ||
</div> | ||
</td> | ||
</tr> | ||
|
@@ -134,4 +110,4 @@ <h2 class="suggestion"> | |
</table> | ||
</td> | ||
</tr> | ||
</table> | ||
</table> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,5 +17,9 @@ module.exports = { | |
], | ||
"transform": { | ||
"^.+\\.ts$": "ts-jest", | ||
"^.+\\.js$": "babel-jest", | ||
}, | ||
"transformIgnorePatterns": [ | ||
"/test/cli/fixtures/bad", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jest was trying to parse this file using Babel which was failing, so we ignore it here |
||
], | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This handles transpiling the ESM syntax for the Jest tests