Merge branch 'main' into add-wgsl

pull/3887/head
David Neto 2 years ago committed by GitHub
commit 6163a51770
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,9 @@
# Monaco Editor Changelog
## [0.37.1]
- Fixes Inline Completions feature
## [0.37.0]
- New `registerLinkOpener` API

4
package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "monaco-editor",
"version": "0.37.0",
"version": "0.37.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "monaco-editor",
"version": "0.37.0",
"version": "0.37.1",
"hasInstallScript": true,
"license": "MIT",
"devDependencies": {

@ -1,7 +1,7 @@
{
"name": "monaco-editor",
"version": "0.37.0",
"vscodeRef": "9eba21c20f8720575cbc6c531d60c042c554d465",
"version": "0.37.1",
"vscodeRef": "8f74fbfd1f2d8f6268a42df131726b218aafe511",
"private": true,
"description": "A browser based code editor",
"homepage": "https://github.com/microsoft/monaco-editor",

@ -14,6 +14,8 @@ import { exit } from "process";
"yarn",
[
"tsc",
"--target",
"es6",
"--noEmit",
"--allowJs",
"--checkJs",

@ -21,12 +21,24 @@ export interface IMonacoSetup {
monacoTypesUrl: string | undefined;
}
let loadMonacoPromise: Promise<typeof monaco> | undefined;
let loading = false;
let resolve: (value: typeof monaco) => void;
let reject: (error: unknown) => void;
let loadMonacoPromise = new Promise<typeof monaco>((res, rej) => {
resolve = res;
reject = rej;
});
export async function waitForLoadedMonaco(): Promise<typeof monaco> {
return loadMonacoPromise;
}
export async function loadMonaco(
setup: IMonacoSetup = prodMonacoSetup
): Promise<typeof monaco> {
if (!loadMonacoPromise) {
loadMonacoPromise = _loadMonaco(setup);
if (!loading) {
loading = true;
_loadMonaco(setup).then(resolve, reject);
}
return loadMonacoPromise;
}

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { loadMonaco } from "../monaco-loader";
import { IMessage, IPreviewState } from "../shared";
import { IMessageFromRunner, IMessageToRunner, IPreviewState } from "../shared";
import "./style.scss";
window.addEventListener("message", (event) => {
@ -14,7 +14,7 @@ window.addEventListener("message", (event) => {
console.error("not in sandbox");
return;
}
const e = event.data as IMessage | { kind: undefined };
const e = event.data as IMessageToRunner | { kind: undefined };
if (e.kind === "initialize") {
initialize(e.state);
} else if (e.kind === "update-css") {
@ -51,8 +51,10 @@ async function initialize(state: IPreviewState) {
document.body.innerHTML += state.html;
const js = massageJs(state.js);
try {
eval(state.js);
eval(js);
} catch (err) {
const pre = document.createElement("pre");
pre.appendChild(
@ -61,3 +63,42 @@ async function initialize(state: IPreviewState) {
document.body.insertBefore(pre, document.body.firstChild);
}
}
function sendMessageToParent(message: IMessageFromRunner) {
window.parent.postMessage(message, "*");
}
(globalThis as any).$sendMessageToParent = sendMessageToParent;
(globalThis as any).$bindModelToCodeStr = function bindModel(
model: any,
codeStringName: string
) {
model.onDidChangeContent(() => {
const value = model.getValue();
sendMessageToParent({
kind: "update-code-string",
codeStringName,
value,
});
});
};
function massageJs(js: string) {
/*
Alternate experimental syntax: // bind to code string: `editor.getModel()` -> codeString
const bindToCodeStringRegexp = /\/\/ bind to code string: `(.*?)` -> (.*?)(\n|$)/g;
js = js.replaceAll(bindToCodeStringRegexp, (match, p1, p2) => {
return `globalThis.bindModelToCodeStr(${p1}, ${JSON.stringify(p2)})\n`;
});
*/
const setFromRegexp = /\/*\Wset from `(.*?)`:\W*\//g;
for (const m of js.matchAll(setFromRegexp)) {
const p1 = m[1];
const target = JSON.stringify("set from `" + p1 + "`");
js += `\n try { globalThis.$bindModelToCodeStr(${p1}, ${target}); } catch (e) { console.error(e); }`;
}
return js;
}

@ -5,7 +5,7 @@
import { IMonacoSetup } from "./monaco-loader";
export type IMessage =
export type IMessageToRunner =
| {
kind: "initialize";
state: IPreviewState;
@ -15,6 +15,16 @@ export type IMessage =
css: string;
};
export type IMessageFromRunner =
| {
kind: "update-code-string";
codeStringName: string;
value: string;
}
| {
kind: "reload";
};
export interface IPlaygroundProject {
js: string;
css: string;

@ -1,8 +1,18 @@
var originalModel = monaco.editor.createModel("heLLo world!", "text/plain");
var modifiedModel = monaco.editor.createModel("hello orlando!", "text/plain");
const originalModel = monaco.editor.createModel(
/* set from `originalModel`: */ `hello world`,
"text/plain"
);
const modifiedModel = monaco.editor.createModel(
/* set from `modifiedModel`: */ `Hello World!`,
"text/plain"
);
var diffEditor = monaco.editor.createDiffEditor(
document.getElementById("container")
const diffEditor = monaco.editor.createDiffEditor(
document.getElementById("container"),
{
originalEditable: true,
automaticLayout: true,
}
);
diffEditor.setModel({
original: originalModel,

@ -1,10 +1,10 @@
const text = `function hello() {
const value = /* set from `myEditor.getModel()`: */ `function hello() {
alert('Hello world!');
}`;
// Hover on each property to see its docs!
monaco.editor.create(document.getElementById("container"), {
value: text,
const myEditor = monaco.editor.create(document.getElementById("container"), {
value,
language: "javascript",
automaticLayout: true,
});

@ -11,10 +11,12 @@ customElements.define(
const shadowRoot = this.attachShadow({ mode: "open" });
// Copy over editor styles
const style = document.querySelector(
"link[rel='stylesheet'][data-name='vs/editor/editor.main']"
const styles = document.querySelectorAll(
"link[rel='stylesheet'][data-name^='vs/']"
);
shadowRoot.appendChild(style.cloneNode(true));
for (const style of styles) {
shadowRoot.appendChild(style.cloneNode(true));
}
const template = /** @type HTMLTemplateElement */ (
document.getElementById("editor-template")

@ -12,7 +12,11 @@ import {
reaction,
runInAction,
} from "mobx";
import { IMonacoSetup, loadMonaco } from "../../../monaco-loader";
import {
IMonacoSetup,
loadMonaco,
waitForLoadedMonaco,
} from "../../../monaco-loader";
import { IPlaygroundProject, IPreviewState } from "../../../shared";
import { monacoEditorVersion } from "../../monacoEditorVersion";
import { Debouncer } from "../../utils/Debouncer";
@ -56,12 +60,23 @@ export class PlaygroundModel {
public readonly serializer = new StateUrlSerializer(this);
reload(): void {
public reload(): void {
this.reloadKey++;
}
private readonly _previewHandlers = new Set<IPreviewHandler>();
private _wasEverNonFullScreen = false;
public get wasEverNonFullScreen() {
if (this._wasEverNonFullScreen) {
return true;
}
if (!this.settings.previewFullScreen) {
this._wasEverNonFullScreen = true;
}
return this._wasEverNonFullScreen;
}
@computed.struct
get monacoSetup(): IMonacoSetup {
const sourceOverride = this.serializer.sourceOverride;
@ -125,27 +140,61 @@ export class PlaygroundModel {
}
}
private readonly debouncer = new Debouncer(250);
private readonly debouncer = new Debouncer(700);
@observable
public isDirty = false;
constructor() {
let lastState = this.state;
this.dispose.track({
dispose: reaction(
() => ({ state: this.state }),
({ state }) => {
this.debouncer.run(() => {
if (!this.settings.autoReload) {
if (
JSON.stringify(state.monacoSetup) ===
JSON.stringify(lastState.monacoSetup) &&
state.key === lastState.key
) {
this.isDirty = true;
return;
}
}
const action = () => {
this.isDirty = false;
lastState = state;
for (const handler of this._previewHandlers) {
handler.handlePreview(state);
}
});
};
if (state.key !== lastState.key) {
action(); // sync update
} else {
this.debouncer.run(action);
}
},
{ name: "update preview" }
),
});
const observablePromise = new ObservablePromise(loadMonaco());
const observablePromise = new ObservablePromise(waitForLoadedMonaco());
let disposable: Disposable | undefined = undefined;
loadMonaco().then((m) => {
waitForLoadedMonaco().then((m) => {
this.dispose.track(
monaco.editor.addEditorAction({
id: "reload",
label: "Reload",
run: (editor, ...args) => {
this.reload();
},
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
})
);
const options =
monaco.languages.typescript.javascriptDefaults.getCompilerOptions();
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions(
@ -190,6 +239,28 @@ export class PlaygroundModel {
});
}
setCodeString(codeStringName: string, value: string) {
function escapeRegexpChars(str: string) {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
}
const regexp = new RegExp(
"(\\b" +
escapeRegexpChars(codeStringName) +
":[^\\w`]*`)([^`\\\\]|\\n|\\\\\\\\|\\\\`)*`"
);
const js = this.js;
const str = value
.replaceAll("\\", "\\\\")
.replaceAll("$", "\\$")
.replaceAll("`", "\\`");
const newJs = js.replace(regexp, "$1" + str + "`");
const autoReload = this.settings.autoReload;
this.settings.autoReload = false;
this.js = newJs;
this.settings.autoReload = autoReload;
}
public showSettingsDialog(): void {
this.settingsDialogModel = new SettingsDialogModel(
this.settings.settings

@ -18,7 +18,7 @@ import { PlaygroundModel } from "./PlaygroundModel";
import { Preview } from "./Preview";
import { SettingsDialog } from "./SettingsDialog";
import { Button, Col, Row, Stack } from "../../components/bootstrap";
import { ButtonGroup } from "react-bootstrap";
import { ButtonGroup, FormCheck } from "react-bootstrap";
@hotComponent(module)
@observer
@ -37,88 +37,122 @@ export class PlaygroundPageContent extends React.Component<
className="h-100 g-2"
style={{ flexWrap: "wrap-reverse" }}
>
<Col
md
className={
model.settings.previewFullScreen ? "d-none" : ""
}
>
<Vertical>
<div style={{ flex: 1 }}>
<LabeledEditor
label="JavaScript"
titleBarItems={
<div
className="hstack"
style={{ marginLeft: "auto" }}
>
<span
style={{ marginRight: 8 }}
{model.wasEverNonFullScreen && (
<Col
md
className={
model.settings.previewFullScreen
? "d-none"
: ""
}
>
<Vertical>
<div style={{ flex: 1 }}>
<LabeledEditor
label="JavaScript"
titleBarItems={
<div
className="hstack"
style={{
marginLeft: "auto",
}}
>
Example:
</span>
<Select<PlaygroundExample>
values={getPlaygroundExamples().map(
(e) => ({
groupTitle:
e.chapterTitle,
items: e.examples,
})
)}
value={ref(
model,
"selectedExample"
)}
getLabel={(i) => i.title}
/>
</div>
}
>
<Editor
language={"javascript"}
value={ref(model, "js")}
/>
</LabeledEditor>
</div>
<span
style={{
marginRight: 8,
}}
>
Example:
</span>
<Select<PlaygroundExample>
values={getPlaygroundExamples().map(
(e) => ({
groupTitle:
e.chapterTitle,
items: e.examples,
})
)}
value={ref(
model,
"selectedExample"
)}
getLabel={(i) =>
i.title
}
/>
</div>
}
>
<Editor
language={"javascript"}
value={ref(model, "js")}
/>
</LabeledEditor>
</div>
<div>
<LabeledEditor label="HTML">
<Editor
height={{
kind: "dynamic",
maxHeight: 200,
}}
language={"html"}
value={ref(model, "html")}
/>
</LabeledEditor>
</div>
<div>
<LabeledEditor label="HTML">
<Editor
height={{
kind: "dynamic",
maxHeight: 200,
}}
language={"html"}
value={ref(model, "html")}
/>
</LabeledEditor>
</div>
<div>
<LabeledEditor label="CSS">
<Editor
height={{
kind: "dynamic",
maxHeight: 200,
}}
language={"css"}
value={ref(model, "css")}
/>
</LabeledEditor>
</div>
</Vertical>
</Col>
<div>
<LabeledEditor label="CSS">
<Editor
height={{
kind: "dynamic",
maxHeight: 200,
}}
language={"css"}
value={ref(model, "css")}
/>
</LabeledEditor>
</div>
</Vertical>
</Col>
)}
<Col md>
<LabeledEditor
label="Preview"
titleBarItems={
<div
style={{ marginLeft: "auto" }}
className="d-flex gap-2"
className="d-flex gap-2 align-items-center"
>
{model.settings.previewFullScreen || (
<FormCheck
label="Auto-Reload"
className="text-nowrap"
checked={
model.settings.autoReload
}
onChange={(e) => {
model.settings.autoReload =
e.target.checked;
if (
e.target.checked &&
model.isDirty
) {
model.reload();
}
}}
/>
)}
<Button
type="button"
className="btn btn-light settings bi-arrow-clockwise"
className={
"btn settings bi-arrow-clockwise " +
(model.isDirty
? "btn-primary"
: "btn-light")
}
style={{
fontSize: 20,
padding: "0px 4px",
@ -414,7 +448,16 @@ class Editor extends React.Component<{
() => {
const value = this.props.value.get();
if (!this.ignoreChange) {
this.editor!.setValue(value);
this.model.pushEditOperations(
null,
[
{
range: this.model.getFullModelRange(),
text: value,
},
],
() => null
);
}
},
{ name: "update text" }
@ -424,6 +467,7 @@ class Editor extends React.Component<{
componentWillUnmount() {
this.disposables.forEach((d) => d.dispose());
this.model.dispose();
}
}

@ -2,7 +2,11 @@ import * as React from "react";
import { IPreviewHandler, PlaygroundModel } from "./PlaygroundModel";
import { observer } from "mobx-react";
import { observable } from "mobx";
import { IMessage, IPreviewState } from "../../../shared";
import {
IMessageFromRunner,
IMessageToRunner,
IPreviewState,
} from "../../../shared";
@observer
export class Preview
@ -40,7 +44,7 @@ export class Preview
return;
}
const message: IMessage = {
const message: IMessageToRunner = {
kind: "initialize",
state: this.currentState,
};
@ -48,6 +52,17 @@ export class Preview
targetOrigin: "*",
});
});
window.addEventListener("message", (e) => {
if (e.source !== iframe.contentWindow) {
return;
}
const data = e.data as IMessageFromRunner;
if (data.kind === "update-code-string") {
this.props.model.setCodeString(data.codeStringName, data.value);
} else if (data.kind === "reload") {
this.props.model.reload();
}
});
};
componentDidMount() {
@ -68,7 +83,7 @@ export class Preview
{
kind: "update-css",
css: state.css,
} as IMessage,
} as IMessageToRunner,
{
targetOrigin: "*",
}

@ -33,6 +33,14 @@ export class SettingsModel {
this.setSettings({ ...this._settings, previewFullScreen: value });
}
get autoReload() {
return this._settings.autoReload ?? true;
}
set autoReload(value: boolean) {
this.setSettings({ ...this._settings, autoReload: value });
}
constructor() {
const settingsStr = localStorage.getItem(this.settingsKey);
if (settingsStr) {
@ -70,6 +78,7 @@ export interface Settings {
customConfig: JsonString<IMonacoSetup>;
previewFullScreen: boolean;
autoReload: boolean | undefined;
}
export type JsonString<T> = string;
@ -167,6 +176,7 @@ export function getDefaultSettings(): Settings {
loaderPathsConfig: "",
}),
previewFullScreen: false,
autoReload: true,
};
return defaultSettings;
}

Loading…
Cancel
Save