also show scheme code in web playground
Some checks failed
Publish Playground / build (push) Has been cancelled
Publish Playground / deploy (push) Has been cancelled

This commit is contained in:
2026-03-21 11:39:29 -07:00
parent 9652903df1
commit f5a9aae070
6 changed files with 99 additions and 26 deletions

View File

@@ -154,6 +154,36 @@ const newtLanguage2: StreamLanguage<State> = StreamLanguage.define({
},
});
export function scheme() {
return new LanguageSupport(schemeLanguage);
}
const schemeLanguage: StreamLanguage<State> = StreamLanguage.define({
startState: () => null,
token(stream, st) {
const keywords = ["define", "let", "case", "cond", "import", "include", "lambda", "else"];
if (stream.eatSpace()) return null;
if (stream.match("--")) {
stream.skipToEnd();
return "comment";
}
if (stream.match(/[0-9A-Za-z!%&*+./:<=>?@^_~-]+/)) {
let word = stream.current();
if (keywords.includes(word)) return "keyword";
return null;
}
// unhandled
stream.next();
return null
},
languageData: {
commentTokens: {
line: ";;",
},
wordChars: "!%&*+-./:<=>?@^_~",
},
});
function newt() {
return new LanguageSupport(newtLanguage2);
}

View File

@@ -49,7 +49,7 @@ export interface API {
hoverInfo(fileName: string, row: number, col: number): HoverResult | boolean | null;
codeActionInfo(fileName: string, row: number, col: number): CodeAction[] | null;
// we need to add this to the LSP build
compile(fileName: string): string;
compile(fileName: string, language: 'javascript'|'scheme'): string;
}
export interface Message<K extends keyof API> {
@@ -116,4 +116,3 @@ export class IPC {
}
}
class IPCClient {}

View File

@@ -11,7 +11,7 @@ import {
TopData,
Marker,
} from "./types.ts";
import { CMEditor } from "./cmeditor.ts";
import { CMEditor, scheme } from "./cmeditor.ts";
import { deflate } from "./deflate.ts";
import { inflate } from "./inflate.ts";
import { IPC, Position } from "./ipc.ts";
@@ -82,11 +82,23 @@ if (!state.javascript.value) {
const fileName = state.currentFile.value;
// maybe send fileName, src?
await ipc.sendMessage("updateFile", [fileName, src]);
let js = await ipc.sendMessage("compile", [fileName]);
let js = await ipc.sendMessage("compile", [fileName, "javascript"]);
state.javascript.value = bundle(js);
}
}
async function refreshScheme() {
if (!state.scheme.value) {
let src = state.editor.value!.getValue();
console.log("SEND TO", iframe.contentWindow);
const fileName = state.currentFile.value;
// maybe send fileName, src?
await ipc.sendMessage("updateFile", [fileName, src]);
let scheme = await ipc.sendMessage("compile", [fileName, "scheme"]);
state.scheme.value = bundle(scheme);
}
}
async function runOutput() {
await refreshJS()
const src = state.javascript.value;
@@ -153,15 +165,23 @@ function getSavedCode() {
return value;
}
const RESULTS = "Output";
const JAVASCRIPT = "JS";
const SCHEME = "Scheme";
const CONSOLE = "Console";
const HELP = "Help";
const state = {
output: signal(""),
toast: signal(""),
javascript: signal(""),
scheme: signal(""),
messages: signal<string[]>([]),
editor: signal<AbstractEditor | null>(null),
dark: signal(false),
files: signal<string[]>(["Tour.newt"]),
currentFile: signal<string>(localStorage.currentFile ?? "Tour.newt"),
selected: signal(localStorage.tab ?? RESULTS),
};
// Monitor dark mode state (TODO - let user override system setting)
@@ -264,7 +284,12 @@ const language: EditorDelegate = {
}
setOutput(res.output)
// less flashy version
ipc.sendMessage("compile", [fileName]).then(js => state.javascript.value = bundle(js));
if (state.selected.value === JAVASCRIPT)
ipc.sendMessage("compile", [fileName, "javascript"]).then(js => state.javascript.value = bundle(js));
if (state.selected.value === SCHEME)
ipc.sendMessage("compile", [fileName, "scheme"]).then(scheme=> state.scheme.value = scheme);
// UI will update
state.scheme.value = "";
return diags;
} catch (e) {
console.log("ERR", e);
@@ -287,26 +312,26 @@ function Editor({ initialValue }: EditorProps) {
return h("div", { id: "editor", ref });
}
// for extra credit, we could have a read-only monaco
function JavaScript() {
const text = state.javascript.value;
interface ViewerProps {
language: 'javascript' | 'scheme'
}
function SourceViewer({language}: ViewerProps) {
const text = state[language].value;
// return h("div", { id: "javascript" }, text);
const ref = useRef<HTMLDivElement>(null);
const editorView = useRef<EditorView>(null);
const themeRef = useRef<Compartment>(null);
useEffect(() => {
console.log('JSEFFECT')
const container = ref.current!;
themeRef.current = new Compartment();
const editor = new EditorView({
doc: text,
parent: container,
extensions: [
basicSetup,
themeRef.current.of(state.dark.value ? oneDark : EditorView.baseTheme({})),
javascript(),
language == 'javascript' ? javascript() : scheme(),
EditorState.readOnly.of(true),
EditorView.editable.of(false),
],
@@ -347,16 +372,11 @@ function Console() {
);
}
const RESULTS = "Output";
const JAVASCRIPT = "JS";
const CONSOLE = "Console";
const HELP = "Help";
function Tabs() {
const [selected, setSelected] = useState(localStorage.tab ?? RESULTS);
const selected = state.selected.value
const Tab = (label: string) => {
let onClick = () => {
setSelected(label);
state.selected.value = label;
localStorage.tab = label;
};
let className = "tab";
@@ -365,20 +385,24 @@ function Tabs() {
};
useEffect(() => {
if (state.messages.value.length) setSelected(CONSOLE);
if (state.messages.value.length) state.selected.value = CONSOLE;
}, [state.messages.value]);
useEffect(() => {
if (selected === JAVASCRIPT && !state.javascript.value) refreshJS();
}, [selected, state.javascript.value]);
if (selected === SCHEME && !state.scheme.value) refreshScheme();
}, [selected, state.javascript.value, state.scheme.value]);
let body;
switch (selected) {
case RESULTS:
body = h(Result, {});
break;
case SCHEME:
body = h(SourceViewer, {language: 'scheme'});
break;
case JAVASCRIPT:
body = h(JavaScript, {});
body = h(SourceViewer, {language:'javascript'});
break;
case CONSOLE:
body = h(Console, {});
@@ -398,6 +422,7 @@ function Tabs() {
{ className: "tabBar" },
Tab(RESULTS),
Tab(JAVASCRIPT),
Tab(SCHEME),
Tab(CONSOLE),
Tab(HELP),
),

View File

@@ -5,3 +5,4 @@ export function LSP_checkFile(name: string): Diagnostic[];
export function LSP_hoverInfo(name: string, row: number, col: number): HoverResult | boolean | null;
export function LSP_codeActionInfo(name: string, row: number, col: number): CodeAction[] | null;
export function LSP_compileJS(name: string): string;
export function LSP_compileToScheme(name: string): string;

View File

@@ -1,7 +1,7 @@
import { shim } from "./emul";
import { API, Message, ResponseMSG } from "./ipc";
import { archive, preload } from "./preload";
import { LSP_checkFile, LSP_codeActionInfo, LSP_compileJS, LSP_hoverInfo, LSP_updateFile } from './newt';
import { LSP_checkFile, LSP_codeActionInfo, LSP_compileJS, LSP_compileToScheme, LSP_hoverInfo, LSP_updateFile } from './newt';
const LOG = console.log
@@ -25,7 +25,10 @@ const api: API = {
},
hoverInfo: LSP_hoverInfo,
codeActionInfo: LSP_codeActionInfo,
compile: LSP_compileJS,
compile: (fn, lang) => {
if (lang == 'scheme') return LSP_compileToScheme(fn);
return LSP_compileJS(fn);
}
}
const handleMessage = async function <K extends keyof API>(ev: { data: Message<K> }) {

View File

@@ -17,6 +17,7 @@ import Lib.ProcessDecl
import Lib.Prettier
import Lib.Error
import Lib.CompileJS
import Lib.CompileScheme
pfunc js_castArray : Array JSObject JSObject := `x => x`
pfunc js_castInt : Int JSObject := `x => x`
@@ -278,7 +279,6 @@ compileJS fn = unsafePerformIO $ do
when (st.baseDir /= base) $ \ _ => resetState base
repo <- lspFileSource
(Right (top, src)) <- (do
putStrLn "woo"
mod <- processModule emptyFC repo Nil modName
docs <- compile
let src = unlines $
@@ -290,4 +290,19 @@ compileJS fn = unsafePerformIO $ do
modifyIORef state [ topContext := top ]
pure $ js_castStr src
#export updateFile checkFile hoverInfo codeActionInfo compileJS docSymbols
compileToScheme : String JSObject
compileToScheme fn = unsafePerformIO $ do
let (base, modName) = decomposeName fn
st <- readIORef state
when (st.baseDir /= base) $ \ _ => resetState base
repo <- lspFileSource
(Right (top, src)) <- (do
mod <- processModule emptyFC repo Nil modName
docs <- compileScheme
let src = unlines docs
pure src).runM st.topContext
| Left err => pure $ js_castStr ";; \{errorMsg err}"
modifyIORef state [ topContext := top ]
pure $ js_castStr src
#export updateFile checkFile hoverInfo codeActionInfo compileJS docSymbols compileToScheme