remove monaco, add input method to codemirror
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
StreamLanguage,
|
||||
StringStream,
|
||||
} from "@codemirror/language";
|
||||
import { ABBREV } from "./abbrev.js";
|
||||
|
||||
let parserWithMetadata = parser.configure({
|
||||
props: [
|
||||
@@ -32,7 +33,7 @@ const newtLanguage = LRLanguage.define({
|
||||
},
|
||||
},
|
||||
});
|
||||
// prettier did this...
|
||||
// prettier flattened this...
|
||||
const keywords = [
|
||||
"let",
|
||||
"in",
|
||||
@@ -113,7 +114,6 @@ function commentTokenizer(stream: StringStream, state: State): string | null {
|
||||
}
|
||||
dash = ch === "-";
|
||||
}
|
||||
console.log("XX", stream.current());
|
||||
return "comment";
|
||||
}
|
||||
|
||||
@@ -143,6 +143,27 @@ export class CMEditor implements AbstractEditor {
|
||||
basicSetup,
|
||||
linter((view) => this.delegate.lint(view)),
|
||||
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) => {
|
||||
let cursor = this.view.state.doc.lineAt(pos);
|
||||
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