remove monaco, add input method to codemirror
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
mkdir -p public
|
mkdir -p public
|
||||||
echo build monaco worker
|
|
||||||
esbuild --bundle node_modules/monaco-editor/esm/vs/editor/editor.worker.js > public/workerMain.js
|
|
||||||
echo build newt worker
|
echo build newt worker
|
||||||
esbuild src/worker.ts --bundle --format=esm > public/worker.js
|
esbuild src/worker.ts --bundle --format=esm > public/worker.js
|
||||||
esbuild src/frame.ts --bundle --format=esm > public/frame.js
|
esbuild src/frame.ts --bundle --format=esm > public/frame.js
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@preact/signals": "^1.3.0",
|
"@preact/signals": "^1.3.0",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"monaco-editor": "^0.52.0",
|
|
||||||
"preact": "^10.24.3"
|
"preact": "^10.24.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
StreamLanguage,
|
StreamLanguage,
|
||||||
StringStream,
|
StringStream,
|
||||||
} from "@codemirror/language";
|
} from "@codemirror/language";
|
||||||
|
import { ABBREV } from "./abbrev.js";
|
||||||
|
|
||||||
let parserWithMetadata = parser.configure({
|
let parserWithMetadata = parser.configure({
|
||||||
props: [
|
props: [
|
||||||
@@ -32,7 +33,7 @@ const newtLanguage = LRLanguage.define({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// prettier did this...
|
// prettier flattened this...
|
||||||
const keywords = [
|
const keywords = [
|
||||||
"let",
|
"let",
|
||||||
"in",
|
"in",
|
||||||
@@ -113,7 +114,6 @@ function commentTokenizer(stream: StringStream, state: State): string | null {
|
|||||||
}
|
}
|
||||||
dash = ch === "-";
|
dash = ch === "-";
|
||||||
}
|
}
|
||||||
console.log("XX", stream.current());
|
|
||||||
return "comment";
|
return "comment";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +143,27 @@ export class CMEditor implements AbstractEditor {
|
|||||||
basicSetup,
|
basicSetup,
|
||||||
linter((view) => this.delegate.lint(view)),
|
linter((view) => this.delegate.lint(view)),
|
||||||
this.theme.of(EditorView.baseTheme({})),
|
this.theme.of(EditorView.baseTheme({})),
|
||||||
|
EditorView.updateListener.of((update) => {
|
||||||
|
let doc = update.state.doc
|
||||||
|
|
||||||
|
update.changes.iterChanges((fromA, toA, fromB, toB, inserted)=> {
|
||||||
|
if (" ')\\".includes(inserted.toString())) {
|
||||||
|
console.log('changes', update.changes, update.changes.desc)
|
||||||
|
let line = doc.lineAt(fromA)
|
||||||
|
let e = fromA - line.from
|
||||||
|
const m = line.text.slice(0, e).match(/(\\[^ ]+)$/);
|
||||||
|
if (m) {
|
||||||
|
let s = e - m[0].length
|
||||||
|
let key = line.text.slice(s, e)
|
||||||
|
if (ABBREV[key]) {
|
||||||
|
this.view.dispatch({
|
||||||
|
changes: { from: line.from + s, to: fromA, insert: ABBREV[key] },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
hoverTooltip((view, pos) => {
|
hoverTooltip((view, pos) => {
|
||||||
let cursor = this.view.state.doc.lineAt(pos);
|
let cursor = this.view.state.doc.lineAt(pos);
|
||||||
let line = cursor.number;
|
let line = cursor.number;
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
import { ABBREV } from "./abbrev.ts";
|
|
||||||
import { newtConfig, newtTokens } from "./monarch.ts";
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import { AbstractEditor, EditorDelegate, Marker } from "./types.ts";
|
|
||||||
|
|
||||||
// we need to fix the definition of word
|
|
||||||
monaco.languages.register({ id: "newt" });
|
|
||||||
monaco.languages.setMonarchTokensProvider("newt", newtTokens);
|
|
||||||
monaco.languages.setLanguageConfiguration("newt", newtConfig);
|
|
||||||
|
|
||||||
self.MonacoEnvironment = {
|
|
||||||
getWorkerUrl(moduleId, _label) {
|
|
||||||
console.log("Get worker", moduleId);
|
|
||||||
return moduleId;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export class MonacoEditor implements AbstractEditor {
|
|
||||||
editor: monaco.editor.IStandaloneCodeEditor;
|
|
||||||
delegate: EditorDelegate;
|
|
||||||
|
|
||||||
constructor(container: HTMLElement, value: string, language: EditorDelegate) {
|
|
||||||
this.delegate = language;
|
|
||||||
let editor = (this.editor = monaco.editor.create(container, {
|
|
||||||
value,
|
|
||||||
language: "newt",
|
|
||||||
fontFamily: "Comic Code, Menlo, Monaco, Courier New, sans",
|
|
||||||
automaticLayout: true,
|
|
||||||
acceptSuggestionOnEnter: "off",
|
|
||||||
unicodeHighlight: { ambiguousCharacters: false },
|
|
||||||
minimap: { enabled: false },
|
|
||||||
}));
|
|
||||||
monaco.languages.registerDefinitionProvider("newt", {
|
|
||||||
provideDefinition(model, position, token) {
|
|
||||||
const info = model.getWordAtPosition(position);
|
|
||||||
if (!info) return;
|
|
||||||
let entry = language.getEntry(
|
|
||||||
info.word,
|
|
||||||
position.lineNumber,
|
|
||||||
info.startColumn
|
|
||||||
);
|
|
||||||
if (!entry) return;
|
|
||||||
|
|
||||||
// HACK - we don't know our filename which was generated from `module` decl, so
|
|
||||||
// assume the last context entry is in our file.
|
|
||||||
|
|
||||||
let file = language.getFileName();
|
|
||||||
if (!entry || entry.fc.file !== file) return;
|
|
||||||
let lineNumber = entry.fc.line + 1;
|
|
||||||
let column = entry.fc.col + 1;
|
|
||||||
let word = model.getWordAtPosition({ lineNumber, column });
|
|
||||||
let range = new monaco.Range(lineNumber, column, lineNumber, column);
|
|
||||||
if (word) {
|
|
||||||
range = new monaco.Range(
|
|
||||||
lineNumber,
|
|
||||||
word.startColumn,
|
|
||||||
lineNumber,
|
|
||||||
word.endColumn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return { uri: model.uri, range };
|
|
||||||
},
|
|
||||||
});
|
|
||||||
monaco.languages.registerHoverProvider("newt", {
|
|
||||||
provideHover(model, position, token, context) {
|
|
||||||
const info = model.getWordAtPosition(position);
|
|
||||||
if (!info) return;
|
|
||||||
let entry = language.getEntry(
|
|
||||||
info.word,
|
|
||||||
position.lineNumber,
|
|
||||||
info.startColumn
|
|
||||||
);
|
|
||||||
if (!entry) return;
|
|
||||||
return {
|
|
||||||
range: new monaco.Range(
|
|
||||||
position.lineNumber,
|
|
||||||
info.startColumn,
|
|
||||||
position.lineNumber,
|
|
||||||
info.endColumn
|
|
||||||
),
|
|
||||||
contents: [{ value: `${entry.name} : ${entry.type}` }],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
editor.onDidChangeModelContent((ev) => {
|
|
||||||
this.delegate.onChange(editor.getValue());
|
|
||||||
|
|
||||||
let last = ev.changes[ev.changes.length - 1];
|
|
||||||
const model = editor.getModel();
|
|
||||||
// figure out heuristics here, what chars do we want to trigger?
|
|
||||||
// the lean one will only be active if it sees you type \
|
|
||||||
// and bail if it decides you've gone elsewhere
|
|
||||||
// it maintains an underline annotation, too.
|
|
||||||
if (model && last.text && " ')\\".includes(last.text)) {
|
|
||||||
console.log("LAST", last);
|
|
||||||
let { startLineNumber, startColumn } = last.range;
|
|
||||||
const text = model.getValueInRange(
|
|
||||||
new monaco.Range(
|
|
||||||
startLineNumber,
|
|
||||||
Math.max(1, startColumn - 10),
|
|
||||||
startLineNumber,
|
|
||||||
startColumn
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const m = text.match(/(\\[^ ]+)$/);
|
|
||||||
if (m) {
|
|
||||||
let cand = m[0];
|
|
||||||
console.log("GOT", cand);
|
|
||||||
let text = ABBREV[cand];
|
|
||||||
if (text) {
|
|
||||||
const range = new monaco.Range(
|
|
||||||
startLineNumber,
|
|
||||||
startColumn - cand.length,
|
|
||||||
startLineNumber,
|
|
||||||
startColumn
|
|
||||||
);
|
|
||||||
editor.executeEdits("replaceSequence", [{ range, text: text }]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setValue(value: string) {
|
|
||||||
this.editor.setValue(value);
|
|
||||||
}
|
|
||||||
getValue() {
|
|
||||||
return this.editor.getValue();
|
|
||||||
}
|
|
||||||
setMarkers(markers: Marker[]) {
|
|
||||||
let model = this.editor.getModel()!;
|
|
||||||
const mapMarker = (marker: Marker): monaco.editor.IMarkerData => {
|
|
||||||
let severity =
|
|
||||||
marker.severity === "ERROR"
|
|
||||||
? monaco.MarkerSeverity.Error
|
|
||||||
: monaco.MarkerSeverity.Info;
|
|
||||||
// translate to surrounding word
|
|
||||||
// FIXME - we have `getWord` in monaco, but probably belongs to the delegate
|
|
||||||
// and eventually we have better FC
|
|
||||||
let { message, startColumn, endColumn, startLineNumber, endLineNumber } =
|
|
||||||
marker;
|
|
||||||
let word = model.getWordAtPosition({
|
|
||||||
column: startColumn,
|
|
||||||
lineNumber: startLineNumber,
|
|
||||||
});
|
|
||||||
if (word) endColumn = word.endColumn;
|
|
||||||
return {
|
|
||||||
message,
|
|
||||||
startColumn,
|
|
||||||
endColumn,
|
|
||||||
startLineNumber,
|
|
||||||
endLineNumber,
|
|
||||||
severity,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
monaco.editor.setModelMarkers(model, "newt", markers.map(mapMarker));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// scratch
|
|
||||||
const processOutput = (
|
|
||||||
editor: monaco.editor.IStandaloneCodeEditor,
|
|
||||||
output: string
|
|
||||||
) => {
|
|
||||||
let model = editor.getModel()!;
|
|
||||||
let markers: monaco.editor.IMarkerData[] = [];
|
|
||||||
let lines = output.split("\n");
|
|
||||||
let m = lines[0].match(/.*Process (.*)/);
|
|
||||||
let fn = "";
|
|
||||||
if (m) fn = m[1];
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
const line = lines[i];
|
|
||||||
const match = line.match(/(INFO|ERROR) at (.*):\((\d+), (\d+)\):\s*(.*)/);
|
|
||||||
if (match) {
|
|
||||||
let [_full, kind, file, line, col, message] = match;
|
|
||||||
let lineNumber = +line + 1;
|
|
||||||
let column = +col + 1;
|
|
||||||
// FIXME - pass the real path in
|
|
||||||
if (fn && fn !== file) {
|
|
||||||
lineNumber = column = 0;
|
|
||||||
}
|
|
||||||
let start = { column, lineNumber };
|
|
||||||
// we don't have the full range, so grab the surrounding word
|
|
||||||
let endColumn = column + 1;
|
|
||||||
let word = model.getWordAtPosition(start);
|
|
||||||
if (word) endColumn = word.endColumn;
|
|
||||||
|
|
||||||
// heuristics to grab the entire message:
|
|
||||||
// anything indented
|
|
||||||
// Context:, or Goal: are part of PRINTME
|
|
||||||
// unexpected / expecting appear in parse errors
|
|
||||||
while (lines[i + 1]?.match(/^( )/)) {
|
|
||||||
message += "\n" + lines[++i];
|
|
||||||
}
|
|
||||||
const severity =
|
|
||||||
kind === "ERROR"
|
|
||||||
? monaco.MarkerSeverity.Error
|
|
||||||
: monaco.MarkerSeverity.Info;
|
|
||||||
if (kind === "ERROR" || lineNumber)
|
|
||||||
markers.push({
|
|
||||||
severity,
|
|
||||||
message,
|
|
||||||
startLineNumber: lineNumber,
|
|
||||||
endLineNumber: lineNumber,
|
|
||||||
startColumn: column,
|
|
||||||
endColumn,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
monaco.editor.setModelMarkers(model, "newt", markers);
|
|
||||||
};
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
import * as monaco from "monaco-editor";
|
|
||||||
|
|
||||||
export let newtConfig: monaco.languages.LanguageConfiguration = {
|
|
||||||
// see singleton in Tokenizer.idr
|
|
||||||
wordPattern: /[^()\\{}\[\],.@\s]+/,
|
|
||||||
comments: {
|
|
||||||
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
|
||||||
lineComment: "--",
|
|
||||||
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
|
|
||||||
blockComment: ["/-", "-/"],
|
|
||||||
},
|
|
||||||
// symbols used as brackets
|
|
||||||
brackets: [
|
|
||||||
["{", "}"],
|
|
||||||
["[", "]"],
|
|
||||||
["(", ")"],
|
|
||||||
],
|
|
||||||
// symbols that are auto closed when typing
|
|
||||||
autoClosingPairs: [
|
|
||||||
{ open: "{", close: "}" },
|
|
||||||
{ open: "[", close: "]" },
|
|
||||||
{ open: "(", close: ")" },
|
|
||||||
{ open: '"', close: '"' },
|
|
||||||
// { open: "'", close: "'" }, causes problems with foo''
|
|
||||||
{ open: "/-", close: "-/" },
|
|
||||||
],
|
|
||||||
// symbols that can be used to surround a selection
|
|
||||||
surroundingPairs: [
|
|
||||||
{ open: "{", close: "}" },
|
|
||||||
{ open: "[", close: "]" },
|
|
||||||
{ open: "(", close: ")" },
|
|
||||||
{ open: '"', close: '"' },
|
|
||||||
{ open: "'", close: "'" },
|
|
||||||
],
|
|
||||||
onEnterRules: [
|
|
||||||
{
|
|
||||||
beforeText: /^\s+$/,
|
|
||||||
action: {
|
|
||||||
indentAction: monaco.languages.IndentAction.Outdent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beforeText: /\b(where|of|case)$/,
|
|
||||||
action: {
|
|
||||||
indentAction: monaco.languages.IndentAction.Indent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beforeText: /\/-/,
|
|
||||||
afterText: /-\//,
|
|
||||||
action: {
|
|
||||||
indentAction: monaco.languages.IndentAction.IndentOutdent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export let newtTokens: monaco.languages.IMonarchLanguage = {
|
|
||||||
// Set defaultToken to invalid to see what you do not tokenize yet
|
|
||||||
// defaultToken: "invalid",
|
|
||||||
|
|
||||||
keywords: [
|
|
||||||
"let",
|
|
||||||
"in",
|
|
||||||
"where",
|
|
||||||
"case",
|
|
||||||
"record",
|
|
||||||
"of",
|
|
||||||
"data",
|
|
||||||
"forall",
|
|
||||||
"∀",
|
|
||||||
"U",
|
|
||||||
"module",
|
|
||||||
"ptype",
|
|
||||||
"pfunc",
|
|
||||||
"if",
|
|
||||||
"uses",
|
|
||||||
"then",
|
|
||||||
"else",
|
|
||||||
"class",
|
|
||||||
"instance",
|
|
||||||
"module",
|
|
||||||
"infixl",
|
|
||||||
"infixr",
|
|
||||||
"infix",
|
|
||||||
],
|
|
||||||
specialOps: ["=>", "->", ":", "=", ":=", "<-"],
|
|
||||||
tokenizer: {
|
|
||||||
root: [
|
|
||||||
// char literal, but I don't think there is a class for that.
|
|
||||||
[/'\\?.'/, "string"],
|
|
||||||
[
|
|
||||||
/[a-z_$][\w$']*/,
|
|
||||||
{ cases: { "@keywords": "keyword", "@default": "identifier" } },
|
|
||||||
],
|
|
||||||
[/[A-Z][\w\$]*/, "type.identifier"],
|
|
||||||
[/\\|λ/, "keyword"],
|
|
||||||
{ include: "@whitespace" },
|
|
||||||
[/[{}()\[\]]/, "@brackets"],
|
|
||||||
[
|
|
||||||
/[:!#$%&*+.<=>?@\\^|~\/-]+/,
|
|
||||||
{
|
|
||||||
cases: {
|
|
||||||
"@specialOps": "keyword",
|
|
||||||
"@default": "operator",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
[/\d+/, "number"],
|
|
||||||
|
|
||||||
// strings
|
|
||||||
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
|
|
||||||
[/"/, { token: "string.quote", bracket: "@open", next: "@string" }],
|
|
||||||
],
|
|
||||||
comment: [
|
|
||||||
[/[^-]+/, "comment"],
|
|
||||||
["-/", "comment", "@pop"],
|
|
||||||
["-", "comment"],
|
|
||||||
],
|
|
||||||
string: [
|
|
||||||
[/[^\\"]+/, "string"],
|
|
||||||
// [/@escapes/, "string.escape"],
|
|
||||||
// [/\\./, "string.escape.invalid"],
|
|
||||||
[/"/, { token: "string.quote", bracket: "@close", next: "@pop" }],
|
|
||||||
],
|
|
||||||
whitespace: [
|
|
||||||
[/[ \t\r\n]+/, "white"],
|
|
||||||
["/-", "comment", "@comment"],
|
|
||||||
[/--.*$/, "comment"],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user