wip live coding mode
This commit is contained in:
111
src/lib/editor/block-eval.ts
Normal file
111
src/lib/editor/block-eval.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { EditorView, Decoration } from '@codemirror/view';
|
||||
import { EditorState, StateField, StateEffect } from '@codemirror/state';
|
||||
|
||||
interface EvalBlock {
|
||||
text: string;
|
||||
from: number | null;
|
||||
to: number | null;
|
||||
}
|
||||
|
||||
type FlashRange = [number, number];
|
||||
|
||||
const setFlash = StateEffect.define<FlashRange | null>();
|
||||
|
||||
const defaultStyle = {
|
||||
'background-color': '#FFCA2880',
|
||||
};
|
||||
|
||||
const styleObjectToString = (styleObj: Record<string, string>): string =>
|
||||
Object.entries(styleObj)
|
||||
.map(([k, v]) => `${k}:${v}`)
|
||||
.join(';');
|
||||
|
||||
export const flash = (
|
||||
view: EditorView,
|
||||
from: number | null,
|
||||
to: number | null,
|
||||
timeout: number = 150,
|
||||
) => {
|
||||
if (from === null || to === null) return;
|
||||
view.dispatch({ effects: setFlash.of([from, to]) });
|
||||
setTimeout(() => {
|
||||
view.dispatch({ effects: setFlash.of(null) });
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
export const flashField = (style: Record<string, string> = defaultStyle) =>
|
||||
StateField.define({
|
||||
create() {
|
||||
return Decoration.none;
|
||||
},
|
||||
update(flash, tr) {
|
||||
try {
|
||||
for (let e of tr.effects) {
|
||||
if (e.is(setFlash)) {
|
||||
if (e.value) {
|
||||
const [from, to] = e.value;
|
||||
const mark = Decoration.mark({
|
||||
attributes: { style: styleObjectToString(style) },
|
||||
});
|
||||
flash = Decoration.set([mark.range(from, to)]);
|
||||
} else {
|
||||
flash = Decoration.set([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return flash;
|
||||
} catch (err) {
|
||||
console.warn('flash error', err);
|
||||
return flash;
|
||||
}
|
||||
},
|
||||
provide: (f) => EditorView.decorations.from(f),
|
||||
});
|
||||
|
||||
export function getSelection(state: EditorState): EvalBlock {
|
||||
if (state.selection.main.empty) return { text: '', from: null, to: null };
|
||||
|
||||
let { from, to } = state.selection.main;
|
||||
|
||||
let text = state.doc.sliceString(from, to);
|
||||
return { text, from, to };
|
||||
}
|
||||
|
||||
export function getLine(state: EditorState): EvalBlock {
|
||||
const line = state.doc.lineAt(state.selection.main.from);
|
||||
|
||||
let { from, to } = line;
|
||||
|
||||
let text = state.doc.sliceString(from, to);
|
||||
return { text, from, to };
|
||||
}
|
||||
|
||||
export function getBlock(state: EditorState): EvalBlock {
|
||||
let { doc, selection } = state;
|
||||
let { text, number } = state.doc.lineAt(selection.main.from);
|
||||
|
||||
if (text.trim().length === 0) return { text: '', from: null, to: null };
|
||||
|
||||
let fromL, toL;
|
||||
fromL = toL = number;
|
||||
|
||||
while (fromL > 1 && doc.line(fromL - 1).text.trim().length > 0) {
|
||||
fromL -= 1;
|
||||
}
|
||||
while (toL < doc.lines && doc.line(toL + 1).text.trim().length > 0) {
|
||||
toL += 1;
|
||||
}
|
||||
|
||||
let { from } = doc.line(fromL);
|
||||
let { to } = doc.line(toL);
|
||||
|
||||
text = state.doc.sliceString(from, to);
|
||||
return { text, from, to };
|
||||
}
|
||||
|
||||
export function getDocument(state: EditorState): EvalBlock {
|
||||
const { from } = state.doc.line(1);
|
||||
const { to } = state.doc.line(state.doc.lines);
|
||||
const text = state.doc.sliceString(from, to);
|
||||
return { text, from, to };
|
||||
}
|
||||
Reference in New Issue
Block a user