switch to codemirror
This commit is contained in:
196
playground/src/cmeditor.ts
Normal file
196
playground/src/cmeditor.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import { AbstractEditor, EditorDelegate, Marker } from "./types";
|
||||
import { basicSetup } from "codemirror";
|
||||
import { EditorView, hoverTooltip, Tooltip } from "@codemirror/view";
|
||||
import { Compartment } from "@codemirror/state";
|
||||
import { parser } from "./parser.js";
|
||||
import { oneDark, oneDark as themeOneDark } from "@codemirror/theme-one-dark";
|
||||
import { styleTags, tags as t } from "@lezer/highlight";
|
||||
import { linter, Diagnostic } from "@codemirror/lint";
|
||||
import {
|
||||
LanguageSupport,
|
||||
LRLanguage,
|
||||
StreamLanguage,
|
||||
StringStream,
|
||||
} from "@codemirror/language";
|
||||
|
||||
let parserWithMetadata = parser.configure({
|
||||
props: [
|
||||
styleTags({
|
||||
Identifier: t.variableName,
|
||||
LineComment: t.lineComment,
|
||||
"if then else data where": t.keyword,
|
||||
}),
|
||||
// indentNodeProp, foldNodeProp
|
||||
],
|
||||
});
|
||||
|
||||
const newtLanguage = LRLanguage.define({
|
||||
parser: parserWithMetadata,
|
||||
languageData: {
|
||||
commentTokens: {
|
||||
line: "--",
|
||||
},
|
||||
},
|
||||
});
|
||||
// prettier did this...
|
||||
const keywords = [
|
||||
"let",
|
||||
"in",
|
||||
"where",
|
||||
"case",
|
||||
"of",
|
||||
"data",
|
||||
"U",
|
||||
"do",
|
||||
"ptype",
|
||||
"pfunc",
|
||||
"module",
|
||||
"infixl",
|
||||
"infixr",
|
||||
"infix",
|
||||
"∀",
|
||||
"forall",
|
||||
"import",
|
||||
"uses",
|
||||
"class",
|
||||
"instance",
|
||||
"record",
|
||||
"constructor",
|
||||
"if",
|
||||
"then",
|
||||
"else",
|
||||
"$",
|
||||
"λ",
|
||||
"?",
|
||||
"@",
|
||||
".",
|
||||
"->",
|
||||
"→",
|
||||
":",
|
||||
"=>",
|
||||
":=",
|
||||
"$=",
|
||||
"=",
|
||||
"<-",
|
||||
"\\",
|
||||
"_",
|
||||
"|",
|
||||
];
|
||||
|
||||
interface State {
|
||||
tokenizer(stream: StringStream, state: State): string | null;
|
||||
}
|
||||
|
||||
function tokenizer(stream: StringStream, state: State): string | null {
|
||||
stream.eatSpace();
|
||||
if (stream.match("--")) {
|
||||
stream.skipToEnd();
|
||||
return "comment";
|
||||
}
|
||||
if (stream.match(/^[/]-/)) {
|
||||
state.tokenizer = commentTokenizer;
|
||||
return state.tokenizer(stream, state);
|
||||
}
|
||||
if (stream.match(/^[\w_][\w\d_']*/)) {
|
||||
let word = stream.current();
|
||||
if (keywords.includes(word)) return "keyword";
|
||||
if (word[0] >= "A" && word[0] <= "Z") return "typename";
|
||||
return "identifier";
|
||||
}
|
||||
// unhandled
|
||||
stream.next();
|
||||
return null;
|
||||
}
|
||||
|
||||
function commentTokenizer(stream: StringStream, state: State): string | null {
|
||||
console.log("ctok");
|
||||
let dash = false;
|
||||
let ch;
|
||||
while ((ch = stream.next())) {
|
||||
if (dash && ch === "/") {
|
||||
state.tokenizer = tokenizer;
|
||||
return "comment";
|
||||
}
|
||||
dash = ch === "-";
|
||||
}
|
||||
console.log("XX", stream.current());
|
||||
return "comment";
|
||||
}
|
||||
|
||||
const newtLanguage2 = StreamLanguage.define({
|
||||
startState: () => ({ tokenizer }),
|
||||
token(stream, st) {
|
||||
return st.tokenizer(stream, st);
|
||||
},
|
||||
});
|
||||
|
||||
function newt() {
|
||||
return new LanguageSupport(newtLanguage2);
|
||||
}
|
||||
|
||||
export class CMEditor implements AbstractEditor {
|
||||
view: EditorView;
|
||||
delegate: EditorDelegate;
|
||||
theme: Compartment;
|
||||
constructor(container: HTMLElement, doc: string, delegate: EditorDelegate) {
|
||||
this.delegate = delegate;
|
||||
this.theme = new Compartment();
|
||||
|
||||
this.view = new EditorView({
|
||||
doc,
|
||||
parent: container,
|
||||
extensions: [
|
||||
basicSetup,
|
||||
linter((view) => this.delegate.lint(view)),
|
||||
this.theme.of(EditorView.baseTheme({})),
|
||||
hoverTooltip((view, pos) => {
|
||||
let cursor = this.view.state.doc.lineAt(pos);
|
||||
let line = cursor.number;
|
||||
let range = this.view.state.wordAt(pos);
|
||||
console.log(range);
|
||||
if (range) {
|
||||
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) {
|
||||
let rval: Tooltip = {
|
||||
pos: range.head,
|
||||
above: true,
|
||||
create: () => {
|
||||
let dom = document.createElement("div");
|
||||
dom.className = "tooltip";
|
||||
dom.textContent = entry.type;
|
||||
return { dom };
|
||||
},
|
||||
};
|
||||
return rval;
|
||||
}
|
||||
}
|
||||
// we'll iterate the syntax tree for word.
|
||||
// let entry = delegate.getEntry(word, line, col)
|
||||
return null;
|
||||
}),
|
||||
newt(),
|
||||
],
|
||||
});
|
||||
}
|
||||
setDark(isDark: boolean) {
|
||||
this.view.dispatch({
|
||||
effects: this.theme.reconfigure(
|
||||
isDark ? oneDark : EditorView.baseTheme({})
|
||||
),
|
||||
});
|
||||
}
|
||||
setValue(_doc: string) {
|
||||
// Is this all we can do?
|
||||
this.view.dispatch({
|
||||
changes: { from: 0, to: this.view.state.doc.length, insert: _doc },
|
||||
});
|
||||
}
|
||||
getValue() {
|
||||
// maybe?
|
||||
return this.view.state.doc.toString();
|
||||
}
|
||||
setMarkers(_: Marker[]) {}
|
||||
}
|
||||
Reference in New Issue
Block a user