diff --git a/newt-vscode/language-configuration.json b/newt-vscode/language-configuration.json index 5fa3abb..87764cc 100644 --- a/newt-vscode/language-configuration.json +++ b/newt-vscode/language-configuration.json @@ -33,7 +33,7 @@ ], "onEnterRules": [ { - "beforeText": "\\b(where|of|case)$", + "beforeText": "\\b(where|of|do)\\s*$", "action": { "indent": "indent" } }, { diff --git a/newt-vscode/src/abbrev.ts b/newt-vscode/src/abbrev.ts index a2e22ae..2edf5b6 100644 --- a/newt-vscode/src/abbrev.ts +++ b/newt-vscode/src/abbrev.ts @@ -7,4 +7,9 @@ export const ABBREV: Record = { "\\circ": "∘", "\\1": "₁", "\\2": "₂", + "\\<": "⟨", + "\\>": "⟩", + "\\_0": "₀", + "\\_1": "₁", + "\\bN": "ℕ" }; diff --git a/playground/src/cmeditor.ts b/playground/src/cmeditor.ts index 72789fd..1b76e1d 100644 --- a/playground/src/cmeditor.ts +++ b/playground/src/cmeditor.ts @@ -1,10 +1,12 @@ import { AbstractEditor, EditorDelegate, Marker } from "./types"; import { basicSetup } from "codemirror"; -import { EditorView, hoverTooltip, Tooltip } from "@codemirror/view"; -import { Compartment } from "@codemirror/state"; +import { defaultKeymap, indentMore, indentLess, toggleLineComment } from "@codemirror/commands"; +import { EditorView, hoverTooltip, keymap, Tooltip } from "@codemirror/view"; +import { Compartment, Prec } from "@codemirror/state"; import { oneDark } from "@codemirror/theme-one-dark"; import { linter } from "@codemirror/lint"; import { + indentService, LanguageSupport, StreamLanguage, StringStream, @@ -61,7 +63,7 @@ interface State { } function tokenizer(stream: StringStream, state: State): string | null { - stream.eatSpace(); + if (stream.eatSpace()) return null if (stream.match("--")) { stream.skipToEnd(); return "comment"; @@ -70,17 +72,19 @@ function tokenizer(stream: StringStream, state: State): string | null { state.tokenizer = commentTokenizer; return state.tokenizer(stream, state); } - if (stream.match(/^[\w_][\w\d_']*/)) { + + // TODO match tokenizer better.. + if (stream.match(/[^\\(){}[\],.@;\s][^()\\{}\[\],.@;\s]*/)) { let word = stream.current(); if (keywords.includes(word)) return "keyword"; if (word[0] >= "A" && word[0] <= "Z") return "typename"; + console.log('IDENT', ) return "identifier"; } // unhandled - stream.next(); + stream.next() return null; } - function commentTokenizer(stream: StringStream, state: State): string | null { console.log("ctok"); let dash = false; @@ -100,6 +104,12 @@ const newtLanguage2 = StreamLanguage.define({ token(stream, st) { return st.tokenizer(stream, st); }, + languageData: { + commentTokens: { + line: "--" + }, + wordChars: "!#$%^&*_+-=<>|", + } }); function newt() { @@ -120,12 +130,46 @@ export class CMEditor implements AbstractEditor { extensions: [ basicSetup, linter((view) => this.delegate.lint(view)), - this.theme.of(EditorView.baseTheme({})), + // For indent on return + indentService.of((ctx, pos) => { + let line = ctx.lineAt(pos) + if (!line) return null + let prevLine = ctx.lineAt(line.from - 1); + if (prevLine.text.trimEnd().match(/\b(of|where|do)\s*$/)) { + let pindent = prevLine.text.match(/^\s*/)?.[0].length ?? 0 + return pindent + 2 + } + return null + }), + Prec.highest(keymap.of([ + { + key: "Tab", + preventDefault: true, + run: indentMore, + }, + { + key: "Cmd-/", + run: toggleLineComment, + }, + // ok, we can do multiple keys (we'll want this for Idris) + { + key: "c-c c-s", + run: () => { + console.log("C-c C-s") + return false + } + }, + { + key: "Shift-Tab", + preventDefault: true, + run: indentLess, + }, + ])), EditorView.updateListener.of((update) => { let doc = update.state.doc; update.changes.iterChanges((fromA, toA, fromB, toB, inserted) => { - if (" ')\\".includes(inserted.toString())) { + if (" ')\\_".includes(inserted.toString())) { console.log("changes", update.changes, update.changes.desc); let line = doc.lineAt(fromA); let e = fromA - line.from; @@ -146,6 +190,7 @@ export class CMEditor implements AbstractEditor { } }); }), + this.theme.of(EditorView.baseTheme({})), hoverTooltip((view, pos) => { let cursor = this.view.state.doc.lineAt(pos); let line = cursor.number; @@ -155,7 +200,8 @@ export class CMEditor implements AbstractEditor { let col = range.from - cursor.from; let word = this.view.state.doc.sliceString(range.from, range.to); let entry = this.delegate.getEntry(word, line, col); - console.log("entry", entry); + if (!entry) entry = this.delegate.getEntry('_'+word+'_', line, col); + console.log("entry for", word, "is", entry); if (entry) { let rval: Tooltip = { pos: range.head, diff --git a/playground/src/main.ts b/playground/src/main.ts index 57a88dd..54115ce 100644 --- a/playground/src/main.ts +++ b/playground/src/main.ts @@ -185,6 +185,7 @@ const language: EditorDelegate = { return topData?.context.find((entry) => entry.name === word); }, onChange(value) { + // run via the linter now // clearTimeout(timeout); // timeout = setTimeout(() => { // run(value); @@ -199,15 +200,17 @@ const language: EditorDelegate = { async lint(view) { console.log("LINT"); let src = view.state.doc.toString(); + localStorage.code = src + // we'll want to pull it from the file. const fileName = state.currentFile.value; console.log("FN", fileName); - // console.log("SRC", src); try { let out = await runCommand({ id: nextID(), type: "compileRequest", fileName, src, + compile: false, }); console.log("OUT", out); let markers = processOutput(out); diff --git a/playground/src/types.ts b/playground/src/types.ts index 6cdd5f7..ea45e66 100644 --- a/playground/src/types.ts +++ b/playground/src/types.ts @@ -6,6 +6,7 @@ export interface CompileReq { type: "compileRequest"; fileName: string; src: string; + compile: boolean; } export interface CompileRes { diff --git a/playground/src/worker.ts b/playground/src/worker.ts index 1875ff2..575460a 100644 --- a/playground/src/worker.ts +++ b/playground/src/worker.ts @@ -3,8 +3,8 @@ import { archive, preload } from "./preload"; import { CompileReq, CompileRes } from "./types"; console.log = (m) => { - shim.stdout += '\n' + m -} + shim.stdout += "\n" + m; +}; const handleMessage = async function (ev: { data: CompileReq }) { console.log("message", ev.data); @@ -12,7 +12,10 @@ const handleMessage = async function (ev: { data: CompileReq }) { shim.archive = archive; let { id, src, fileName } = ev.data; const outfile = "out.js"; - shim.process.argv = ["browser", "newt", fileName, "-o", outfile, "--top"]; + if (ev.data.compile) + shim.process.argv = ["browser", "newt", fileName, "-o", outfile, "--top"]; + else + shim.process.argv = ["browser", "newt", fileName, "--top"]; shim.files[fileName] = new TextEncoder().encode(src); shim.files[outfile] = new TextEncoder().encode("No JS output"); shim.stdout = ""; @@ -29,7 +32,7 @@ const handleMessage = async function (ev: { data: CompileReq }) { console.log(`process ${fileName} in ${duration} ms`); let javascript = new TextDecoder().decode(shim.files[outfile]); let output = shim.stdout; - sendResponse({ id, type: 'compileResult', javascript, output, duration }); + sendResponse({ id, type: "compileResult", javascript, output, duration }); }; // hooks for worker.html to override diff --git a/src/Lib/Compile.newt b/src/Lib/Compile.newt index 0a03f42..c8ed6c1 100644 --- a/src/Lib/Compile.newt +++ b/src/Lib/Compile.newt @@ -286,10 +286,6 @@ stmtToDoc (JError str) = text "throw new Error(" ++ text (quoteString str) ++ te stmtToDoc (JCase sc alts) = text "switch (" ++ expToDoc sc ++ text ")" <+> bracket "{" (stack $ map altToDoc alts) "}" -mkArgs : Nat -> List String -> List String -mkArgs Z acc = acc -mkArgs (S k) acc = mkArgs k ("h\{show k}" :: acc) - -- use iife to turn stmts into expr maybeWrap : JSStmt Return -> JSExp maybeWrap (JReturn exp) = exp @@ -399,6 +395,7 @@ process name = do eraseEntries liftWhere entries <- readIORef ref + -- Now working with defs exprs <- mapM defToCExp $ toList entries let cexpMap = foldMap const EmptyMap exprs cexpMap <- tailCallOpt cexpMap