mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-07-28 04:08:05 +00:00
editor: lazy load
This makes the required changes needed to make the editor components and sagas lazy-load to improve the time before the first render of the app. This is done by making sure we only import types from the monaco editor module anywhere other than the saga and the component modules and using React.lazy to lazy-load the component and start the sagas. This cuts the main bundle time to less than 1/2 so load time should be over twice as fast now.
This commit is contained in:
committed by
David Lechner
parent
0f56bb7174
commit
c49f37f5e4
@@ -7,6 +7,9 @@
|
||||
### Added
|
||||
- Added feature to close editor tab on middle click ([support#758]).
|
||||
|
||||
### Fixed
|
||||
- Fixed long time until UI visible on resource-constrained systems.
|
||||
|
||||
### Removed
|
||||
- Removed word-based autocompletion ([support#757]).
|
||||
|
||||
|
||||
+19
-3
@@ -3,13 +3,12 @@
|
||||
|
||||
import 'react-splitter-layout/lib/index.css';
|
||||
import './app.scss';
|
||||
import { Classes } from '@blueprintjs/core';
|
||||
import { Classes, Spinner } from '@blueprintjs/core';
|
||||
import docsPackage from '@pybricks/ide-docs/package.json';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
import { useLocalStorage, useTernaryDarkMode } from 'usehooks-ts';
|
||||
import Activities from '../activities/Activities';
|
||||
import Editor from '../editor/Editor';
|
||||
import { InstallPybricksDialog } from '../firmware/installPybricksDialog/InstallPybricksDialog';
|
||||
import RestoreOfficialDialog from '../firmware/restoreOfficialDialog/RestoreOfficialDialog';
|
||||
import { useSettingIsShowDocsEnabled } from '../settings/hooks';
|
||||
@@ -20,6 +19,19 @@ import Tour from '../tour/Tour';
|
||||
import { isMacOS } from '../utils/os';
|
||||
import { httpServerHeadersVersion } from './constants';
|
||||
|
||||
const Editor = React.lazy(async () => {
|
||||
const [sagaModule, componentModule] = await Promise.all([
|
||||
import('../editor/sagas'),
|
||||
import('../editor/Editor'),
|
||||
]);
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('pb-lazy-saga', { detail: { saga: sagaModule.default } }),
|
||||
);
|
||||
|
||||
return componentModule;
|
||||
});
|
||||
|
||||
const Docs: React.VFC = () => {
|
||||
const { setIsSettingShowDocsEnabled } = useSettingIsShowDocsEnabled();
|
||||
|
||||
@@ -186,7 +198,11 @@ const App: React.VFC = () => {
|
||||
>
|
||||
<div className="pb-app-editor">
|
||||
<Toolbar />
|
||||
<Editor />
|
||||
<React.Suspense
|
||||
fallback={<Spinner className="pb-editor" />}
|
||||
>
|
||||
<Editor />
|
||||
</React.Suspense>
|
||||
</div>
|
||||
<div className="pb-app-terminal">
|
||||
<Terminal />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { monaco } from 'react-monaco-editor';
|
||||
import type { monaco } from 'react-monaco-editor';
|
||||
|
||||
/** The Pybricks MicroPython language identifier. */
|
||||
export const pybricksMicroPythonId = 'pybricks-micropython';
|
||||
@@ -39,7 +39,7 @@ export const conf: monaco.languages.LanguageConfiguration = {
|
||||
beforeText: new RegExp(
|
||||
'^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$',
|
||||
),
|
||||
action: { indentAction: monaco.languages.IndentAction.Indent },
|
||||
action: { indentAction: <monaco.languages.IndentAction.Indent>1 },
|
||||
},
|
||||
],
|
||||
folding: {
|
||||
@@ -361,7 +361,7 @@ export const templateSnippetCompletions = <monaco.languages.CompletionItemProvid
|
||||
.filter((x) => x.label.startsWith(textUntilPosition))
|
||||
.map<monaco.languages.CompletionItem>((x) => ({
|
||||
detail: x.insertText,
|
||||
kind: monaco.languages.CompletionItemKind.Snippet,
|
||||
kind: <monaco.languages.CompletionItemKind.Snippet>27,
|
||||
range,
|
||||
...x,
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { SagaGenerator, getContext, put, select, take } from 'typed-redux-saga/macro';
|
||||
import { RootState } from '../reducers';
|
||||
import { editorGetValueRequest, editorGetValueResponse } from './actions';
|
||||
|
||||
/**
|
||||
* Saga that gets the current value from the editor.
|
||||
* @returns The value.
|
||||
* @throws Error if editor.isReady state is false.
|
||||
*/
|
||||
export function* editorGetValue(): SagaGenerator<string> {
|
||||
const nextMessageId = yield* getContext<() => number>('nextMessageId');
|
||||
|
||||
const isReady = yield* select((s: RootState) => s.editor.isReady);
|
||||
|
||||
if (!isReady) {
|
||||
throw new Error('editorGetValue() called before editor.isReady');
|
||||
}
|
||||
|
||||
const request = yield* put(editorGetValueRequest(nextMessageId()));
|
||||
const response = yield* take(
|
||||
editorGetValueResponse.when((a) => a.id === request.id),
|
||||
);
|
||||
|
||||
return response.value;
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import type { DatabaseChangeType, IDatabaseChange } from 'dexie-observable/api';
|
||||
import { monaco } from 'react-monaco-editor';
|
||||
import { EventChannel, buffers, eventChannel } from 'redux-saga';
|
||||
import {
|
||||
SagaGenerator,
|
||||
call,
|
||||
cancelled,
|
||||
delay,
|
||||
@@ -67,28 +66,6 @@ import { EditorError } from './error';
|
||||
import { ActiveFileHistoryManager, OpenFileManager } from './lib';
|
||||
import { pybricksMicroPythonId } from './pybricksMicroPython';
|
||||
|
||||
/**
|
||||
* Saga that gets the current value from the editor.
|
||||
* @returns The value.
|
||||
* @throws Error if editor.isReady state is false.
|
||||
*/
|
||||
export function* editorGetValue(): SagaGenerator<string> {
|
||||
const nextMessageId = yield* getContext<() => number>('nextMessageId');
|
||||
|
||||
const isReady = yield* select((s: RootState) => s.editor.isReady);
|
||||
|
||||
if (!isReady) {
|
||||
throw new Error('editorGetValue() called before editor.isReady');
|
||||
}
|
||||
|
||||
const request = yield* put(editorGetValueRequest(nextMessageId()));
|
||||
const response = yield* take(
|
||||
editorGetValueResponse.when((a) => a.id === request.id),
|
||||
);
|
||||
|
||||
return response.value;
|
||||
}
|
||||
|
||||
function* handleEditorGetValueRequest(
|
||||
editor: monaco.editor.ICodeEditor,
|
||||
action: ReturnType<typeof editorGetValueRequest>,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { monaco } from 'react-monaco-editor';
|
||||
import type { monaco } from 'react-monaco-editor';
|
||||
|
||||
export class UntitledHintContribution implements monaco.editor.IEditorContribution {
|
||||
public static readonly ID = 'editor.contrib.untitledHint';
|
||||
@@ -79,7 +79,7 @@ class UntitledHintContentWidget implements monaco.editor.IContentWidget {
|
||||
getPosition(): monaco.editor.IContentWidgetPosition | null {
|
||||
return {
|
||||
position: { lineNumber: 1, column: 1 },
|
||||
preference: [monaco.editor.ContentWidgetPositionPreference.EXACT],
|
||||
preference: [<monaco.editor.ContentWidgetPositionPreference.EXACT>0],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import {
|
||||
} from '../ble-pybricks-service/actions';
|
||||
import { FileFormat } from '../ble-pybricks-service/protocol';
|
||||
import { bleDidConnectPybricks } from '../ble/actions';
|
||||
import { editorGetValue } from '../editor/sagas';
|
||||
import { editorGetValue } from '../editor/sagaLib';
|
||||
import {
|
||||
compile,
|
||||
didCompile,
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import wasmV5 from '@pybricks/mpy-cross-v5/build/mpy-cross.wasm';
|
||||
import { compile as mpyCrossCompileV6 } from '@pybricks/mpy-cross-v6';
|
||||
import wasmV6 from '@pybricks/mpy-cross-v6/build/mpy-cross-v6.wasm';
|
||||
import { call, getContext, put, select, takeEvery } from 'typed-redux-saga/macro';
|
||||
import { editorGetValue } from '../editor/sagas';
|
||||
import { editorGetValue } from '../editor/sagaLib';
|
||||
import { FileContents, FileStorageDb } from '../fileStorage';
|
||||
import { findImportedModules, resolveModule } from '../pybricksMicropython/lib';
|
||||
import { RootState } from '../reducers';
|
||||
|
||||
+22
-3
@@ -1,12 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020-2022 The Pybricks Authors
|
||||
|
||||
import { all } from 'typed-redux-saga/macro';
|
||||
import { eventChannel } from 'redux-saga';
|
||||
import { all, spawn, take } from 'typed-redux-saga/macro';
|
||||
import alerts, { AlertsSagaContext } from './alerts/sagas';
|
||||
import app from './app/sagas';
|
||||
import blePybricksService from './ble-pybricks-service/sagas';
|
||||
import ble from './ble/sagas';
|
||||
import editor from './editor/sagas';
|
||||
import errorLog from './error-log/sagas';
|
||||
import explorer from './explorer/sagas';
|
||||
import fileStorage, { FileStorageSageContext } from './fileStorage/sagas';
|
||||
@@ -18,6 +18,25 @@ import mpy from './mpy/sagas';
|
||||
import notifications from './notifications/sagas';
|
||||
import terminal, { TerminalSagaContext } from './terminal/sagas';
|
||||
|
||||
/**
|
||||
* Listens to the 'pb-lazy-saga' event to spawn sagas from a React.lazy() initializer.
|
||||
*/
|
||||
function* lazySagas(): Generator {
|
||||
const chan = eventChannel<CustomEvent<{ saga: () => void }>>((emit) => {
|
||||
window.addEventListener('pb-lazy-saga', emit as EventListener);
|
||||
return () => window.removeEventListener('pb-lazy-saga', emit as EventListener);
|
||||
});
|
||||
|
||||
try {
|
||||
for (;;) {
|
||||
const event = yield* take(chan);
|
||||
yield* spawn(event.detail.saga);
|
||||
}
|
||||
} finally {
|
||||
chan.close();
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export default function* (): Generator {
|
||||
yield* all([
|
||||
@@ -25,7 +44,6 @@ export default function* (): Generator {
|
||||
app(),
|
||||
blePybricksService(),
|
||||
ble(),
|
||||
editor(),
|
||||
fileStorage(),
|
||||
lwp3BootloaderBle(),
|
||||
lwp3BootloaderProtocol(),
|
||||
@@ -36,6 +54,7 @@ export default function* (): Generator {
|
||||
mpy(),
|
||||
notifications(),
|
||||
terminal(),
|
||||
lazySagas(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user