Merge remote-tracking branch 'origin/develop' into scoring-cleanup-three
This commit is contained in:
commit
13ba7d1a5e
|
@ -18,6 +18,7 @@
|
||||||
"vega": "^5.22.1",
|
"vega": "^5.22.1",
|
||||||
"vega-embed": "^6.21.0",
|
"vega-embed": "^6.21.0",
|
||||||
"vega-lite": "^5.2.0",
|
"vega-lite": "^5.2.0",
|
||||||
|
"vscode-uri": "^3.0.3",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -52,7 +53,7 @@
|
||||||
"tailwindcss": "^3.1.3",
|
"tailwindcss": "^3.1.3",
|
||||||
"ts-loader": "^9.3.0",
|
"ts-loader": "^9.3.0",
|
||||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||||
"typescript": "^4.7.3",
|
"typescript": "^4.7.4",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.10.0",
|
"webpack-cli": "^4.10.0",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { FC, Fragment, useState } from "react";
|
import React, { FC, Fragment, useState, useEffect } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
|
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
@ -35,6 +35,7 @@ interface PlaygroundProps {
|
||||||
/** If code is set, component becomes controlled */
|
/** If code is set, component becomes controlled */
|
||||||
code?: string;
|
code?: string;
|
||||||
onCodeChange?(expr: string): void;
|
onCodeChange?(expr: string): void;
|
||||||
|
onSettingsChange?(settings: any): void;
|
||||||
/** Should we show the editor? */
|
/** Should we show the editor? */
|
||||||
showEditor?: boolean;
|
showEditor?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -205,6 +206,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
showSummary = false,
|
showSummary = false,
|
||||||
code: controlledCode,
|
code: controlledCode,
|
||||||
onCodeChange,
|
onCodeChange,
|
||||||
|
onSettingsChange,
|
||||||
showEditor = true,
|
showEditor = true,
|
||||||
}) => {
|
}) => {
|
||||||
const [uncontrolledCode, setUncontrolledCode] = useState(
|
const [uncontrolledCode, setUncontrolledCode] = useState(
|
||||||
|
@ -233,6 +235,11 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
const vars = useWatch({
|
const vars = useWatch({
|
||||||
control,
|
control,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onSettingsChange?.(vars);
|
||||||
|
}, [vars, onSettingsChange]);
|
||||||
|
|
||||||
const chartSettings = {
|
const chartSettings = {
|
||||||
start: Number(vars.diagramStart),
|
start: Number(vars.diagramStart),
|
||||||
stop: Number(vars.diagramStop),
|
stop: Number(vars.diagramStop),
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { testRun } from "./TestHelpers";
|
||||||
describe("cumulative density function of a normal distribution", () => {
|
describe("cumulative density function of a normal distribution", () => {
|
||||||
test("at 3 stdevs to the right of the mean is near 1", () => {
|
test("at 3 stdevs to the right of the mean is near 1", () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(fc.float(), fc.float({ min: 1e-7 }), (mean, stdev) => {
|
fc.property(fc.integer(), fc.integer({ min: 1 }), (mean, stdev) => {
|
||||||
let threeStdevsAboveMean = mean + 3 * stdev;
|
let threeStdevsAboveMean = mean + 3 * stdev;
|
||||||
let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsAboveMean})`;
|
let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsAboveMean})`;
|
||||||
let squiggleResult = testRun(squiggleString);
|
let squiggleResult = testRun(squiggleString);
|
||||||
|
@ -16,7 +16,7 @@ describe("cumulative density function of a normal distribution", () => {
|
||||||
|
|
||||||
test("at 3 stdevs to the left of the mean is near 0", () => {
|
test("at 3 stdevs to the left of the mean is near 0", () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(fc.float(), fc.float({ min: 1e-7 }), (mean, stdev) => {
|
fc.property(fc.integer(), fc.integer({ min: 1 }), (mean, stdev) => {
|
||||||
let threeStdevsBelowMean = mean - 3 * stdev;
|
let threeStdevsBelowMean = mean - 3 * stdev;
|
||||||
let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsBelowMean})`;
|
let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsBelowMean})`;
|
||||||
let squiggleResult = testRun(squiggleString);
|
let squiggleResult = testRun(squiggleString);
|
||||||
|
|
|
@ -4,13 +4,16 @@ import * as fc from "fast-check";
|
||||||
|
|
||||||
// Beware: float64Array makes it appear in an infinite loop.
|
// Beware: float64Array makes it appear in an infinite loop.
|
||||||
let arrayGen = () =>
|
let arrayGen = () =>
|
||||||
fc.float32Array({
|
fc
|
||||||
|
.float32Array({
|
||||||
minLength: 10,
|
minLength: 10,
|
||||||
maxLength: 10000,
|
maxLength: 10000,
|
||||||
noDefaultInfinity: true,
|
noDefaultInfinity: true,
|
||||||
noNaN: true,
|
noNaN: true,
|
||||||
});
|
})
|
||||||
|
.filter(
|
||||||
|
(xs_) => Math.min(...Array.from(xs_)) != Math.max(...Array.from(xs_))
|
||||||
|
);
|
||||||
describe("cumulative density function", () => {
|
describe("cumulative density function", () => {
|
||||||
let n = 10000;
|
let n = 10000;
|
||||||
|
|
||||||
|
@ -119,11 +122,7 @@ describe("cumulative density function", () => {
|
||||||
{ sampleCount: n, xyPointLength: 100 }
|
{ sampleCount: n, xyPointLength: 100 }
|
||||||
);
|
);
|
||||||
let cdfValue = dist.cdf(x).value;
|
let cdfValue = dist.cdf(x).value;
|
||||||
if (x < Math.min(...xs)) {
|
expect(cdfValue).toBeGreaterThanOrEqual(0);
|
||||||
expect(cdfValue).toEqual(0);
|
|
||||||
} else {
|
|
||||||
expect(cdfValue).toBeGreaterThan(0);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,15 +5,11 @@ import * as fc from "fast-check";
|
||||||
describe("Scalar manipulation is well-modeled by javascript math", () => {
|
describe("Scalar manipulation is well-modeled by javascript math", () => {
|
||||||
test("in the case of natural logarithms", () => {
|
test("in the case of natural logarithms", () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(fc.float(), (x) => {
|
fc.property(fc.integer(), (x) => {
|
||||||
let squiggleString = `log(${x})`;
|
let squiggleString = `log(${x})`;
|
||||||
let squiggleResult = testRun(squiggleString);
|
let squiggleResult = testRun(squiggleString);
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
expect(squiggleResult.value).toEqual(-Infinity);
|
expect(squiggleResult.value).toEqual(-Infinity);
|
||||||
} else if (x < 0) {
|
|
||||||
expect(squiggleResult.value).toEqual(
|
|
||||||
"somemessage (confused why a test case hasn't pointed out to me that this message is bogus)"
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
expect(squiggleResult.value).toEqual(Math.log(x));
|
expect(squiggleResult.value).toEqual(Math.log(x));
|
||||||
}
|
}
|
||||||
|
@ -23,7 +19,7 @@ describe("Scalar manipulation is well-modeled by javascript math", () => {
|
||||||
|
|
||||||
test("in the case of addition (with assignment)", () => {
|
test("in the case of addition (with assignment)", () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(fc.float(), fc.float(), fc.float(), (x, y, z) => {
|
fc.property(fc.integer(), fc.integer(), fc.integer(), (x, y, z) => {
|
||||||
let squiggleString = `x = ${x}; y = ${y}; z = ${z}; x + y + z`;
|
let squiggleString = `x = ${x}; y = ${y}; z = ${z}; x + y + z`;
|
||||||
let squiggleResult = testRun(squiggleString);
|
let squiggleResult = testRun(squiggleString);
|
||||||
expect(squiggleResult.value).toBeCloseTo(x + y + z);
|
expect(squiggleResult.value).toBeCloseTo(x + y + z);
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { errorValueToString } from "../../src/js/index";
|
|
||||||
import { testRun } from "./TestHelpers";
|
import { testRun } from "./TestHelpers";
|
||||||
import * as fc from "fast-check";
|
import * as fc from "fast-check";
|
||||||
|
|
||||||
describe("Symbolic mean", () => {
|
describe("Symbolic mean", () => {
|
||||||
test("mean(triangular(x,y,z))", () => {
|
test("mean(triangular(x,y,z))", () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(fc.float(), fc.float(), fc.float(), (x, y, z) => {
|
fc.property(fc.integer(), fc.integer(), fc.integer(), (x, y, z) => {
|
||||||
if (!(x < y && y < z)) {
|
if (!(x < y && y < z)) {
|
||||||
try {
|
try {
|
||||||
let squiggleResult = testRun(`mean(triangular(${x},${y},${z}))`);
|
let squiggleResult = testRun(`mean(triangular(${x},${y},${z}))`);
|
||||||
|
|
|
@ -19,7 +19,7 @@ do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
files=`ls src/rescript/**/**/*.resi src/rescript/**/*.resi` # src/rescript/*/resi
|
files=`ls src/rescript/**/*.resi` # src/rescript/*/resi
|
||||||
for file in $files
|
for file in $files
|
||||||
do
|
do
|
||||||
current=`cat $file`
|
current=`cat $file`
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
"bisect_ppx": "^2.7.1",
|
"bisect_ppx": "^2.7.1",
|
||||||
"chalk": "^5.0.1",
|
"chalk": "^5.0.1",
|
||||||
"codecov": "^3.8.3",
|
"codecov": "^3.8.3",
|
||||||
"fast-check": "^2.25.0",
|
"fast-check": "^3.0.0",
|
||||||
"gentype": "^4.4.0",
|
"gentype": "^4.4.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"moduleserve": "^0.9.1",
|
"moduleserve": "^0.9.1",
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
"ts-jest": "^27.1.4",
|
"ts-jest": "^27.1.4",
|
||||||
"ts-loader": "^9.3.0",
|
"ts-loader": "^9.3.0",
|
||||||
"ts-node": "^10.8.1",
|
"ts-node": "^10.8.1",
|
||||||
"typescript": "^4.7.3",
|
"typescript": "^4.7.4",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.10.0"
|
"webpack-cli": "^4.10.0"
|
||||||
},
|
},
|
||||||
|
|
1
packages/vscode-ext/.gitignore
vendored
1
packages/vscode-ext/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
/media/vendor
|
/media/vendor
|
||||||
/out
|
/out
|
||||||
/*.vsix
|
/*.vsix
|
||||||
|
/syntaxes/*.json
|
||||||
|
|
|
@ -4,6 +4,17 @@ _[marketplace](https://marketplace.visualstudio.com/items?itemName=QURI.vscode-s
|
||||||
|
|
||||||
This extension provides support for [Squiggle](https://www.squiggle-language.com/) in VS Code.
|
This extension provides support for [Squiggle](https://www.squiggle-language.com/) in VS Code.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- Preview `.squiggle` files in a preview pane
|
||||||
|
- Syntax highlighting for `.squiggle` and `.squiggleU` files
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Some preview settings, e.g. whether to show the summary table or types of outputs, can be configurable on in the VS Code settings and persist between different preview sessions.
|
||||||
|
|
||||||
|
Check out the full list of Squiggle settings in the main VS Code settings.
|
||||||
|
|
||||||
# Build locally
|
# Build locally
|
||||||
|
|
||||||
We assume you ran `yarn` at the monorepo level for all dependencies.
|
We assume you ran `yarn` at the monorepo level for all dependencies.
|
||||||
|
|
18
packages/vscode-ext/language-configuration.json
Normal file
18
packages/vscode-ext/language-configuration.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"comments": {
|
||||||
|
"lineComment": "//",
|
||||||
|
"blockComment": ["/*", "*/"]
|
||||||
|
},
|
||||||
|
"brackets": [
|
||||||
|
["{", "}"],
|
||||||
|
["[", "]"],
|
||||||
|
["(", ")"]
|
||||||
|
],
|
||||||
|
"autoClosingPairs": [
|
||||||
|
{ "open": "{", "close": "}" },
|
||||||
|
{ "open": "[", "close": "]" },
|
||||||
|
{ "open": "(", "close": ")" },
|
||||||
|
{ "open": "'", "close": "'", "notIn": ["string", "comment"] },
|
||||||
|
{ "open": "\"", "close": "\"", "notIn": ["string", "comment"] }
|
||||||
|
]
|
||||||
|
}
|
41
packages/vscode-ext/media/previewWebview.js
Normal file
41
packages/vscode-ext/media/previewWebview.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
(function () {
|
||||||
|
const vscode = acquireVsCodeApi();
|
||||||
|
|
||||||
|
const container = document.getElementById("root");
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(container);
|
||||||
|
function updateContent(text, showSettings) {
|
||||||
|
root.render(
|
||||||
|
React.createElement(squiggle_components.SquigglePlayground, {
|
||||||
|
code: text,
|
||||||
|
showEditor: false,
|
||||||
|
showTypes: Boolean(showSettings.showTypes),
|
||||||
|
showControls: Boolean(showSettings.showControls),
|
||||||
|
showSummary: Boolean(showSettings.showSummary),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle messages sent from the extension to the webview
|
||||||
|
window.addEventListener("message", (event) => {
|
||||||
|
const message = event.data; // The json data that the extension sent
|
||||||
|
switch (message.type) {
|
||||||
|
case "update":
|
||||||
|
const { text, showSettings } = message;
|
||||||
|
|
||||||
|
// Update our webview's content
|
||||||
|
updateContent(text, showSettings);
|
||||||
|
|
||||||
|
// Then persist state information.
|
||||||
|
// This state is returned in the call to `vscode.getState` below when a webview is reloaded.
|
||||||
|
vscode.setState({ text, showSettings });
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = vscode.getState();
|
||||||
|
if (state) {
|
||||||
|
updateContent(state.text, state.showSettings);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -3,7 +3,7 @@
|
||||||
"displayName": "Squiggle",
|
"displayName": "Squiggle",
|
||||||
"description": "Squiggle language support",
|
"description": "Squiggle language support",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "0.0.4",
|
"version": "0.1.2",
|
||||||
"publisher": "QURI",
|
"publisher": "QURI",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -18,10 +18,45 @@
|
||||||
"Visualization"
|
"Visualization"
|
||||||
],
|
],
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onCustomEditor:squiggle.wysiwyg"
|
"onCustomEditor:squiggle.wysiwyg",
|
||||||
|
"onCommand:squiggle.preview"
|
||||||
],
|
],
|
||||||
"main": "./out/extension.js",
|
"main": "./out/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"id": "squiggle",
|
||||||
|
"extensions": [
|
||||||
|
".squiggle"
|
||||||
|
],
|
||||||
|
"aliases": [
|
||||||
|
"Squiggle"
|
||||||
|
],
|
||||||
|
"configuration": "./language-configuration.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "squiggleU",
|
||||||
|
"extensions": [
|
||||||
|
".squiggleU"
|
||||||
|
],
|
||||||
|
"aliases": [
|
||||||
|
"SquiggleU"
|
||||||
|
],
|
||||||
|
"configuration": "./language-configuration.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"grammars": [
|
||||||
|
{
|
||||||
|
"language": "squiggle",
|
||||||
|
"scopeName": "source.squiggle",
|
||||||
|
"path": "./syntaxes/squiggle.tmLanguage.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "squiggleU",
|
||||||
|
"scopeName": "source.squiggle",
|
||||||
|
"path": "./syntaxes/squiggle.tmLanguage.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
"customEditors": [
|
"customEditors": [
|
||||||
{
|
{
|
||||||
"viewType": "squiggle.wysiwyg",
|
"viewType": "squiggle.wysiwyg",
|
||||||
|
@ -31,29 +66,82 @@
|
||||||
"filenamePattern": "*.squiggle"
|
"filenamePattern": "*.squiggle"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"priority": "default"
|
"priority": "option"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"commands": []
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "squiggle.preview",
|
||||||
|
"title": "Open Preview",
|
||||||
|
"category": "Squiggle",
|
||||||
|
"when": "editorLangId == squiggle",
|
||||||
|
"icon": "$(open-preview)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"menus": {
|
||||||
|
"editor/title": [
|
||||||
|
{
|
||||||
|
"command": "squiggle.preview",
|
||||||
|
"when": "editorLangId == squiggle",
|
||||||
|
"group": "navigation"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commandPalette": [
|
||||||
|
{
|
||||||
|
"command": "squiggle.preview",
|
||||||
|
"when": "editorLangId == squiggle"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"keybindings": [
|
||||||
|
{
|
||||||
|
"command": "squiggle.preview",
|
||||||
|
"key": "ctrl+k v",
|
||||||
|
"mac": "cmd+k v",
|
||||||
|
"when": "editorLangId == squiggle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration": {
|
||||||
|
"title": "Squiggle",
|
||||||
|
"properties": {
|
||||||
|
"squiggle.playground.showTypes": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Whether to show the types of outputs in the playground"
|
||||||
|
},
|
||||||
|
"squiggle.playground.showControls": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Whether to show the log scale controls in the playground"
|
||||||
|
},
|
||||||
|
"squiggle.playground.showSummary": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Whether to show the summary table in the playground"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "yarn run compile",
|
"vscode:prepublish": "yarn run compile",
|
||||||
"compile:tsc": "tsc -p ./",
|
"compile:tsc": "tsc -p ./",
|
||||||
|
"compile:grammar": "js-yaml syntaxes/squiggle.tmLanguage.yaml >syntaxes/squiggle.tmLanguage.json",
|
||||||
"compile:vendor": "(cd ../squiggle-lang && yarn run build) && (cd ../components && yarn run bundle && yarn run build:css) && mkdir -p media/vendor && cp ../components/dist/bundle.js media/vendor/components.js && cp ../components/dist/main.css media/vendor/components.css && cp ../../node_modules/react/umd/react.production.min.js media/vendor/react.js && cp ../../node_modules/react-dom/umd/react-dom.production.min.js media/vendor/react-dom.js && cp ../website/static/img/quri-logo.png media/vendor/icon.png",
|
"compile:vendor": "(cd ../squiggle-lang && yarn run build) && (cd ../components && yarn run bundle && yarn run build:css) && mkdir -p media/vendor && cp ../components/dist/bundle.js media/vendor/components.js && cp ../components/dist/main.css media/vendor/components.css && cp ../../node_modules/react/umd/react.production.min.js media/vendor/react.js && cp ../../node_modules/react-dom/umd/react-dom.production.min.js media/vendor/react-dom.js && cp ../website/static/img/quri-logo.png media/vendor/icon.png",
|
||||||
"compile": "yarn run compile:tsc && yarn run compile:vendor",
|
"compile": "yarn run compile:tsc && yarn run compile:grammar && yarn run compile:vendor",
|
||||||
"watch": "tsc -watch -p ./",
|
"watch": "tsc -watch -p ./",
|
||||||
"pretest": "yarn run compile && yarn run lint",
|
"pretest": "yarn run compile && yarn run lint",
|
||||||
"lint": "eslint src --ext ts",
|
"lint": "eslint src --ext ts",
|
||||||
"format": "eslint src --ext ts --fix"
|
"format": "eslint src --ext ts --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/vscode": "^1.68.0",
|
|
||||||
"@types/glob": "^7.2.0",
|
"@types/glob": "^7.2.0",
|
||||||
"@types/node": "18.x",
|
"@types/node": "18.x",
|
||||||
|
"@types/vscode": "^1.68.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
||||||
"@typescript-eslint/parser": "^5.27.0",
|
"@typescript-eslint/parser": "^5.27.0",
|
||||||
"eslint": "^8.18.0",
|
"eslint": "^8.18.0",
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
"typescript": "^4.7.2"
|
"js-yaml": "^4.1.0",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
|
import { getWebviewContent } from "./utils";
|
||||||
function getNonce() {
|
|
||||||
let text = "";
|
|
||||||
const possible =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
for (let i = 0; i < 32; i++) {
|
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider {
|
export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider {
|
||||||
public static register(context: vscode.ExtensionContext): vscode.Disposable {
|
public static register(context: vscode.ExtensionContext): vscode.Disposable {
|
||||||
|
@ -26,8 +17,6 @@ export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when our custom editor is opened.
|
* Called when our custom editor is opened.
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public async resolveCustomTextEditor(
|
public async resolveCustomTextEditor(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
|
@ -37,7 +26,12 @@ export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider {
|
||||||
webviewPanel.webview.options = {
|
webviewPanel.webview.options = {
|
||||||
enableScripts: true,
|
enableScripts: true,
|
||||||
};
|
};
|
||||||
webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview);
|
webviewPanel.webview.html = getWebviewContent({
|
||||||
|
webview: webviewPanel.webview,
|
||||||
|
script: "media/wysiwygWebview.js",
|
||||||
|
title: "Squiggle Editor",
|
||||||
|
context: this.context,
|
||||||
|
});
|
||||||
|
|
||||||
function updateWebview() {
|
function updateWebview() {
|
||||||
webviewPanel.webview.postMessage({
|
webviewPanel.webview.postMessage({
|
||||||
|
@ -79,57 +73,6 @@ export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider {
|
||||||
updateWebview();
|
updateWebview();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the static html used for the editor webviews.
|
|
||||||
*/
|
|
||||||
private getHtmlForWebview(webview: vscode.Webview): string {
|
|
||||||
// Local path to main script run in the webview
|
|
||||||
|
|
||||||
const styleUri = webview.asWebviewUri(
|
|
||||||
vscode.Uri.joinPath(
|
|
||||||
this.context.extensionUri,
|
|
||||||
"media/vendor/components.css"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const scriptUris = [
|
|
||||||
// vendor files are copied over by `yarn run compile`
|
|
||||||
"media/vendor/react.js",
|
|
||||||
"media/vendor/react-dom.js",
|
|
||||||
"media/vendor/components.js",
|
|
||||||
"media/wysiwyg.js",
|
|
||||||
].map((script) =>
|
|
||||||
webview.asWebviewUri(
|
|
||||||
vscode.Uri.joinPath(this.context.extensionUri, script)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use a nonce to whitelist which scripts can be run
|
|
||||||
const nonce = getNonce();
|
|
||||||
|
|
||||||
return /* html */ `
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<!--
|
|
||||||
Use a content security policy to only allow loading images from https or from our extension directory,
|
|
||||||
and only allow scripts that have a specific nonce.
|
|
||||||
-->
|
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-${nonce}' 'unsafe-eval';">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link href="${styleUri}" rel="stylesheet" />
|
|
||||||
<title>Squiggle Editor</title>
|
|
||||||
</head>
|
|
||||||
<body style="background-color: white; color: black; padding: 12px">
|
|
||||||
<div id="root"></div>
|
|
||||||
${scriptUris
|
|
||||||
.map((uri) => `<script nonce="${nonce}" src="${uri}"></script>`)
|
|
||||||
.join("")}
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateTextDocument(document: vscode.TextDocument, text: string) {
|
private updateTextDocument(document: vscode.TextDocument, text: string) {
|
||||||
const edit = new vscode.WorkspaceEdit();
|
const edit = new vscode.WorkspaceEdit();
|
||||||
|
|
|
@ -2,12 +2,15 @@
|
||||||
// Import the module and reference it with the alias vscode in your code below
|
// Import the module and reference it with the alias vscode in your code below
|
||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
import { SquiggleEditorProvider } from "./squiggleEditor";
|
import { SquiggleEditorProvider } from "./editor";
|
||||||
|
import { registerPreviewCommand } from "./preview";
|
||||||
|
|
||||||
// this method is called when your extension is activated
|
// this method is called when your extension is activated
|
||||||
// your extension is activated the very first time the command is executed
|
// your extension is activated the very first time the command is executed
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
context.subscriptions.push(SquiggleEditorProvider.register(context));
|
context.subscriptions.push(SquiggleEditorProvider.register(context));
|
||||||
|
|
||||||
|
registerPreviewCommand(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method is called when your extension is deactivated
|
// this method is called when your extension is deactivated
|
||||||
|
|
53
packages/vscode-ext/src/preview.ts
Normal file
53
packages/vscode-ext/src/preview.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import * as path from "path";
|
||||||
|
import { getWebviewContent } from "./utils";
|
||||||
|
|
||||||
|
export const registerPreviewCommand = (context: vscode.ExtensionContext) => {
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.commands.registerTextEditorCommand("squiggle.preview", (editor) => {
|
||||||
|
// Create and show a new webview
|
||||||
|
const title = `Preview ${path.basename(editor.document.uri.path)}`;
|
||||||
|
|
||||||
|
const panel = vscode.window.createWebviewPanel(
|
||||||
|
"squigglePreview",
|
||||||
|
title,
|
||||||
|
vscode.ViewColumn.Beside,
|
||||||
|
{} // Webview options. More on these later.
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.webview.options = {
|
||||||
|
enableScripts: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
panel.webview.html = getWebviewContent({
|
||||||
|
context,
|
||||||
|
webview: panel.webview,
|
||||||
|
title,
|
||||||
|
script: "media/previewWebview.js",
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateWebview = () => {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
type: "update",
|
||||||
|
text: editor.document.getText(),
|
||||||
|
showSettings:
|
||||||
|
vscode.workspace.getConfiguration("squiggle").playground,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
updateWebview();
|
||||||
|
|
||||||
|
const changeDocumentSubscription =
|
||||||
|
vscode.workspace.onDidChangeTextDocument((e) => {
|
||||||
|
if (e.document.uri.toString() === editor.document.uri.toString()) {
|
||||||
|
updateWebview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make sure we get rid of the listener when our editor is closed.
|
||||||
|
panel.onDidDispose(() => {
|
||||||
|
changeDocumentSubscription.dispose();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
59
packages/vscode-ext/src/utils.ts
Normal file
59
packages/vscode-ext/src/utils.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
const getNonce = () => {
|
||||||
|
let text = "";
|
||||||
|
const possible =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
for (let i = 0; i < 32; i++) {
|
||||||
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWebviewContent = ({
|
||||||
|
webview,
|
||||||
|
title,
|
||||||
|
script,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
webview: vscode.Webview;
|
||||||
|
title: string;
|
||||||
|
script: string;
|
||||||
|
context: vscode.ExtensionContext;
|
||||||
|
}) => {
|
||||||
|
const nonce = getNonce();
|
||||||
|
|
||||||
|
const styleUri = webview.asWebviewUri(
|
||||||
|
vscode.Uri.joinPath(context.extensionUri, "media/vendor/components.css")
|
||||||
|
);
|
||||||
|
|
||||||
|
const scriptUris = [
|
||||||
|
// vendor files are copied over by `yarn run compile`
|
||||||
|
"media/vendor/react.js",
|
||||||
|
"media/vendor/react-dom.js",
|
||||||
|
"media/vendor/components.js",
|
||||||
|
script,
|
||||||
|
].map((script) =>
|
||||||
|
webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, script))
|
||||||
|
);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<!-- Use a content security policy to only allow scripts that have a specific nonce. -->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-${nonce}' 'unsafe-eval';">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="${styleUri}" rel="stylesheet" />
|
||||||
|
<title>${title}</title>
|
||||||
|
</head>
|
||||||
|
<body style="background-color: white; color: black; padding: 8px 12px">
|
||||||
|
<div id="root"></div>
|
||||||
|
${scriptUris
|
||||||
|
.map((uri) => `<script nonce="${nonce}" src="${uri}"></script>`)
|
||||||
|
.join("")}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
};
|
93
packages/vscode-ext/syntaxes/squiggle.tmLanguage.yaml
Normal file
93
packages/vscode-ext/syntaxes/squiggle.tmLanguage.yaml
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
scopeName: source.squiggle
|
||||||
|
patterns:
|
||||||
|
- include: "#statement"
|
||||||
|
- include: "#expression"
|
||||||
|
- include: "#comment-block"
|
||||||
|
- include: "#comment-line"
|
||||||
|
repository:
|
||||||
|
statement:
|
||||||
|
patterns:
|
||||||
|
- include: "#let"
|
||||||
|
- include: "#defun"
|
||||||
|
expression:
|
||||||
|
patterns:
|
||||||
|
- include: "#integer"
|
||||||
|
- include: "#float"
|
||||||
|
- include: "#string"
|
||||||
|
- include: "#block"
|
||||||
|
- include: "#function-call"
|
||||||
|
- include: "#keywords"
|
||||||
|
let:
|
||||||
|
match: ^\s*(\w+)\s*=
|
||||||
|
captures:
|
||||||
|
"1":
|
||||||
|
name: variable.other.squiggle
|
||||||
|
defun:
|
||||||
|
begin: ^\s*(\w+)\s*(\()
|
||||||
|
end: (\))\s*=
|
||||||
|
beginCaptures:
|
||||||
|
"1":
|
||||||
|
name: entity.name.function.squiggle
|
||||||
|
"2":
|
||||||
|
name: punctuation.definition.arguments.begin.squiggle
|
||||||
|
endCaptures:
|
||||||
|
"1":
|
||||||
|
name: punctuation.definition.arguments.end.squiggle
|
||||||
|
patterns:
|
||||||
|
- include: "#array-parameters"
|
||||||
|
array-parameters:
|
||||||
|
begin: \b([\$_a-z]+[\$_a-zA-Z0-9]*)
|
||||||
|
end: \s*(?:(,)|(?=\)))
|
||||||
|
beginCaptures:
|
||||||
|
"1":
|
||||||
|
name: variable.parameter.function.squiggle
|
||||||
|
function-call:
|
||||||
|
begin: (\w+)\s*(\()
|
||||||
|
end: (\))
|
||||||
|
beginCaptures:
|
||||||
|
"1":
|
||||||
|
name: entity.name.function.squiggle
|
||||||
|
"2":
|
||||||
|
name: punctuation.definition.arguments.begin.squiggle
|
||||||
|
endCaptures:
|
||||||
|
"1":
|
||||||
|
name: punctuation.definition.arguments.end.squiggle
|
||||||
|
patterns:
|
||||||
|
- include: "$self"
|
||||||
|
comment-block:
|
||||||
|
begin: /\*
|
||||||
|
end: \*/
|
||||||
|
name: comment.block.squiggle
|
||||||
|
comment-line:
|
||||||
|
patterns:
|
||||||
|
- include: "#comment-line-double-slash"
|
||||||
|
- include: "#comment-line-number-sign"
|
||||||
|
comment-line-double-slash:
|
||||||
|
match: //.*
|
||||||
|
name: comment.line.double-slash.squiggle
|
||||||
|
comment-line-number-sign:
|
||||||
|
match: "#.*"
|
||||||
|
name: comment.line.number-sign.squiggle
|
||||||
|
block:
|
||||||
|
begin: "{"
|
||||||
|
end: "}"
|
||||||
|
beginCaptures:
|
||||||
|
"0":
|
||||||
|
name: punctuation.definition.block.squiggle
|
||||||
|
endCaptures:
|
||||||
|
"0":
|
||||||
|
name: punctuation.definition.block.squiggle
|
||||||
|
patterns:
|
||||||
|
- include: "$self"
|
||||||
|
keywords:
|
||||||
|
match: \b(if|then|else|to)\b
|
||||||
|
name: keyword.control.squiggle
|
||||||
|
integer:
|
||||||
|
match: \b\d+([_a-zA-Z]+[_a-zA-Z0-9]*)?
|
||||||
|
name: constant.numeric.integer.squiggle
|
||||||
|
float:
|
||||||
|
match: \b(\d+\.\d*|\.?\d+)([eE]-?\d+)?([_a-zA-Z]+[_a-zA-Z0-9]*)?
|
||||||
|
name: constant.numeric.float.squiggle
|
||||||
|
string:
|
||||||
|
match: \".*?\"
|
||||||
|
name: string.quoted.double.squiggle
|
|
@ -6,11 +6,13 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta
|
||||||
|
|
||||||
We assume you ran `yarn` at monorepo level.
|
We assume you ran `yarn` at monorepo level.
|
||||||
|
|
||||||
The website depends on `squiggle-lang`, which you have to build manually.
|
The website depends on `squiggle-lang` and `components`, which you have to build manually.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd ../squiggle-lang
|
cd ../squiggle-lang
|
||||||
yarn build
|
yarn build
|
||||||
|
cd ../components
|
||||||
|
yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
Generate static content, to the `build` directory.
|
Generate static content, to the `build` directory.
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
"@docusaurus/core": "2.0.0-beta.21",
|
"@docusaurus/core": "2.0.0-beta.21",
|
||||||
"@docusaurus/preset-classic": "2.0.0-beta.21",
|
"@docusaurus/preset-classic": "2.0.0-beta.21",
|
||||||
"@quri/squiggle-components": "^0.2.20",
|
"@quri/squiggle-components": "^0.2.20",
|
||||||
|
"base64-js": "^1.5.1",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
"hast-util-is-element": "2.1.2",
|
"hast-util-is-element": "2.1.2",
|
||||||
|
"pako": "^2.0.4",
|
||||||
"prism-react-renderer": "^1.3.3",
|
"prism-react-renderer": "^1.3.3",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
|
@ -1,8 +1,52 @@
|
||||||
|
import { deflate, inflate } from "pako";
|
||||||
|
import { toByteArray, fromByteArray } from "base64-js";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Layout from "@theme/Layout";
|
import Layout from "@theme/Layout";
|
||||||
import { SquigglePlayground } from "../components/SquigglePlayground";
|
import { SquigglePlayground } from "../components/SquigglePlayground";
|
||||||
|
|
||||||
|
const HASH_PREFIX = "#code=";
|
||||||
|
function getHashData() {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const hash = window.location.hash;
|
||||||
|
if (!hash.startsWith(HASH_PREFIX)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const compressed = toByteArray(
|
||||||
|
decodeURIComponent(hash.slice(HASH_PREFIX.length))
|
||||||
|
);
|
||||||
|
const text = inflate(compressed, { to: "string" });
|
||||||
|
return JSON.parse(text);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHashData(data) {
|
||||||
|
const text = JSON.stringify({ ...getHashData(), ...data });
|
||||||
|
const compressed = deflate(text, { level: 9 });
|
||||||
|
window.history.replaceState(
|
||||||
|
undefined,
|
||||||
|
"",
|
||||||
|
HASH_PREFIX + encodeURIComponent(fromByteArray(compressed))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function PlaygroundPage() {
|
export default function PlaygroundPage() {
|
||||||
|
const playgroundProps = {
|
||||||
|
initialSquiggleString: "normal(0,1)",
|
||||||
|
height: 700,
|
||||||
|
showTypes: true,
|
||||||
|
...getHashData(),
|
||||||
|
onCodeChange: (code) => setHashData({ initialSquiggleString: code }),
|
||||||
|
onSettingsChange: (settings) => {
|
||||||
|
const { showTypes, showControls, showSummary, showEditor } = settings;
|
||||||
|
setHashData({ showTypes, showControls, showSummary, showEditor });
|
||||||
|
},
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Layout title="Playground" description="Squiggle Playground">
|
<Layout title="Playground" description="Squiggle Playground">
|
||||||
<div
|
<div
|
||||||
|
@ -10,11 +54,7 @@ export default function PlaygroundPage() {
|
||||||
maxWidth: 2000,
|
maxWidth: 2000,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SquigglePlayground
|
<SquigglePlayground {...playgroundProps} />
|
||||||
initialSquiggleString="normal(0,1)"
|
|
||||||
height={700}
|
|
||||||
showTypes={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user