Prebake in settings
Update prebake without reload
This commit is contained in:
parent
b4e171f5b4
commit
9f2237e11d
11 changed files with 292 additions and 37 deletions
|
|
@ -19,7 +19,7 @@ import { basicSetup } from './basicSetup.mjs';
|
|||
import { evalBlock } from './block_utilities.mjs';
|
||||
import { flash, isFlashEnabled } from './flash.mjs';
|
||||
import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs';
|
||||
import { keybindings } from './keybindings.mjs';
|
||||
import { keybindings, prebakeField } from './keybindings.mjs';
|
||||
import { jumpToCharacter } from './labelJump.mjs';
|
||||
import { getSliderWidgets, sliderPlugin, updateSliderWidgets } from './slider.mjs';
|
||||
import { activateTheme, initTheme, theme } from './themes.mjs';
|
||||
|
|
@ -412,6 +412,10 @@ export class StrudelMirror {
|
|||
setAutocompletionEnabled(enabled) {
|
||||
this.reconfigureExtension('isAutoCompletionEnabled', enabled);
|
||||
}
|
||||
updatePrebake(prebake) {
|
||||
logger('[repl] prebake updated');
|
||||
this.prebaked = prebake();
|
||||
}
|
||||
updateSettings(settings) {
|
||||
this.setFontSize(settings.fontSize);
|
||||
this.setFontFamily(settings.fontFamily);
|
||||
|
|
@ -472,6 +476,85 @@ export class StrudelMirror {
|
|||
}
|
||||
}
|
||||
|
||||
export class PrebakeCodeMirror {
|
||||
constructor(initialCode, storePrebake, containerRef, editorRef, settings) {
|
||||
this.storePrebake = storePrebake;
|
||||
const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()]));
|
||||
const initialSettings = Object.keys(compartments).map((key) =>
|
||||
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
|
||||
);
|
||||
initTheme(settings.theme);
|
||||
let state = EditorState.create({
|
||||
doc: initialCode,
|
||||
extensions: [
|
||||
...initialSettings,
|
||||
basicSetup,
|
||||
javascript(),
|
||||
javascriptLanguage.data.of({
|
||||
closeBrackets: { brackets: ['(', '[', '{', "'", '"', '<'] },
|
||||
bracketMatching: { brackets: ['(', '[', '{', "'", '"', '<'] },
|
||||
}),
|
||||
syntaxHighlighting(defaultHighlightStyle),
|
||||
EditorView.updateListener.of((v) => {
|
||||
if (v.docChanged) {
|
||||
this.code = v.state.doc.toString();
|
||||
}
|
||||
}),
|
||||
drawSelection({ cursorBlinkRate: 0 }),
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
{
|
||||
key: 'Ctrl-Enter',
|
||||
mac: 'Meta-Enter',
|
||||
run: () => {
|
||||
this.savePrebake();
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'Alt-Enter',
|
||||
run: () => {
|
||||
this.savePrebake();
|
||||
},
|
||||
},
|
||||
]),
|
||||
),
|
||||
prebakeField,
|
||||
],
|
||||
});
|
||||
editorRef.current = state;
|
||||
this.code = initialCode;
|
||||
this.view = new EditorView({
|
||||
state,
|
||||
parent: containerRef.current,
|
||||
});
|
||||
}
|
||||
|
||||
async savePrebake() {
|
||||
flash(this.view);
|
||||
this.storePrebake(this.code);
|
||||
logger('[prebake] prebake saved');
|
||||
}
|
||||
|
||||
toggleComment() {
|
||||
try {
|
||||
// Honor selections; toggleLineComment handles both selections and
|
||||
// single line
|
||||
toggleLineComment(this.view);
|
||||
} catch (err) {
|
||||
console.error('Error handling repl-toggle-comment event', err);
|
||||
}
|
||||
}
|
||||
|
||||
setCode(code) {
|
||||
const changes = {
|
||||
from: 0,
|
||||
to: this.view.state.doc.length,
|
||||
insert: code,
|
||||
};
|
||||
this.view.dispatch({ changes });
|
||||
}
|
||||
}
|
||||
|
||||
export function parseBooleans(value) {
|
||||
return { true: true, false: false }[value] ?? value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ export * from './flash.mjs';
|
|||
export * from './slider.mjs';
|
||||
export * from './themes.mjs';
|
||||
export * from './widget.mjs';
|
||||
export { Vim } from './keybindings.mjs';
|
||||
export { Vim, prebakeField } from './keybindings.mjs';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import { Prec, EditorState } from '@codemirror/state';
|
||||
import { Prec, EditorState, StateField } from '@codemirror/state';
|
||||
import { keymap, ViewPlugin } from '@codemirror/view';
|
||||
// import { searchKeymap } from '@codemirror/search';
|
||||
import { emacs } from '@replit/codemirror-emacs';
|
||||
|
|
@ -9,6 +9,15 @@ import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
|
|||
import { helix, commands } from 'codemirror-helix';
|
||||
import { logger } from '@strudel/core';
|
||||
|
||||
export const prebakeField = StateField.define({
|
||||
create() {
|
||||
return 'PrebakeEditor';
|
||||
},
|
||||
update(value, _tr) {
|
||||
return value;
|
||||
},
|
||||
});
|
||||
|
||||
const vscodePlugin = ViewPlugin.fromClass(
|
||||
class {
|
||||
constructor() {}
|
||||
|
|
@ -98,7 +107,11 @@ try {
|
|||
Vim.defineAction('strudelToggleComment', (cm) => {
|
||||
const view = cm?.view || cm;
|
||||
try {
|
||||
const ev = new CustomEvent('repl-toggle-comment', { detail: { source: 'vim', view }, cancelable: true });
|
||||
const toggleEventName =
|
||||
view?.cm6?.state?.field(prebakeField, false) !== undefined
|
||||
? 'prebake-toggle-comment'
|
||||
: 'repl-toggle-comment';
|
||||
const ev = new CustomEvent(toggleEventName, { detail: { source: 'vim', view }, cancelable: true });
|
||||
document.dispatchEvent(ev);
|
||||
} catch (e) {
|
||||
console.error('strudelToggleComment dispatch failed', e);
|
||||
|
|
@ -119,6 +132,17 @@ try {
|
|||
// :w to evaluate
|
||||
Vim.defineEx('write', 'w', (cm) => {
|
||||
const view = cm?.view || cm; // CM6 Vim passes either an object with view or the view itself
|
||||
const isPrebake = view?.cm6?.state?.field(prebakeField, false) !== undefined;
|
||||
if (isPrebake) {
|
||||
let prebakeEventHandled = false;
|
||||
try {
|
||||
const ev = new CustomEvent('prebake-evaluate', { detail: { source: 'vim', view }, cancelable: true });
|
||||
prebakeEventHandled = document.dispatchEvent(ev) === false; // false means preventDefault was called
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('Error dispatching repl-evaluate event', e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
view?.focus?.();
|
||||
// Let the app know this came from Vim :w
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@ import { transpiler } from './transpiler.mjs';
|
|||
export * from './transpiler.mjs';
|
||||
|
||||
export const evaluate = (code) => _evaluate(code, transpiler);
|
||||
export const evaluateUserPrebake = (code) => _evaluate(code, transpiler, { prebake: true });
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export function transpiler(input, options = {}) {
|
|||
emitMiniLocations = true,
|
||||
emitWidgets = true,
|
||||
blockBased = false,
|
||||
prebake = false,
|
||||
range = [],
|
||||
} = options;
|
||||
|
||||
|
|
@ -260,7 +261,9 @@ export function transpiler(input, options = {}) {
|
|||
// For block-based eval, add silence as the return value when block ends with declaration
|
||||
body.push(silenceExpression);
|
||||
} else {
|
||||
throw new Error('unexpected ast format without body expression');
|
||||
if (!prebake) {
|
||||
throw new Error('unexpected ast format without body expression');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -273,7 +276,7 @@ export function transpiler(input, options = {}) {
|
|||
}
|
||||
|
||||
// add return to last statement
|
||||
if (addReturn) {
|
||||
if (addReturn && !prebake) {
|
||||
const { expression } = body[body.length - 1];
|
||||
body[body.length - 1] = {
|
||||
type: 'ReturnStatement',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue