Skip to content

Commit

Permalink
Add syntax highlighting through Lezer
Browse files Browse the repository at this point in the history
  • Loading branch information
ekzhang committed Nov 29, 2021
1 parent 4b2f329 commit bc7ea2d
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 8 deletions.
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
"devDependencies": {
"@codemirror/basic-setup": "^0.19.0",
"@codemirror/commands": "^0.19.5",
"@codemirror/highlight": "^0.19.6",
"@codemirror/lang-markdown": "^0.19.2",
"@codemirror/language": "^0.19.6",
"@codemirror/state": "^0.19.6",
"@codemirror/view": "^0.19.21",
"@fontsource/source-serif-pro": "^4.5.0",
"@lezer/generator": "^0.15.2",
"@sveltejs/vite-plugin-svelte": "1.0.0-next.30",
"@tsconfig/svelte": "^2.0.1",
"@types/chai": "^4.2.22",
Expand Down
19 changes: 12 additions & 7 deletions src/components/cell/CellInput.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<script lang="ts">
import { EditorState, basicSetup } from "@codemirror/basic-setup";
import { basicSetup } from "@codemirror/basic-setup";
import { EditorState, Compartment } from "@codemirror/state";
import { EditorView, KeyBinding, keymap } from "@codemirror/view";
import { indentWithTab } from "@codemirror/commands";
import { markdown } from "@codemirror/lang-markdown";
import { createEventDispatcher, onDestroy, onMount } from "svelte";
import { percival } from "@/lib/codemirror/language";
import type { CellState } from "@/lib/notebook";
const dispatch = createEventDispatcher();
Expand All @@ -15,24 +17,27 @@
let editorParent: HTMLDivElement;
let view: EditorView;
function run() {
dispatch("change", { value: currentValue });
}
let languageConf: Compartment;
function makeRunKeyBinding(key: string): KeyBinding {
return {
key,
run: () => {
run();
dispatch("change", { value: currentValue });
return true;
},
preventDefault: true,
};
}
$: languageExtension = state.type === "markdown" ? markdown : percival;
$: if (view) {
languageConf.reconfigure(languageExtension());
}
onMount(() => {
currentValue = state.value;
languageConf = new Compartment();
view = new EditorView({
state: EditorState.create({
doc: state.value,
Expand All @@ -44,7 +49,7 @@
makeRunKeyBinding("Shift-Enter"),
makeRunKeyBinding("Ctrl-Enter"),
]),
markdown(),
languageConf.of(languageExtension()),
EditorView.updateListener.of((update) => {
currentValue = update.state.doc.toJSON().join("\n");
}),
Expand Down
53 changes: 53 additions & 0 deletions src/lib/codemirror/language.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { parser } from "./percival.grammar";
import {
foldNodeProp,
foldInside,
indentNodeProp,
LanguageSupport,
LRLanguage,
} from "@codemirror/language";
import { styleTags, tags as t } from "@codemirror/highlight";

let parserWithMetadata = parser.configure({
props: [
styleTags({
Identifier: t.local(t.variableName),
TableName: t.definition(t.variableName),
PropName: t.definition(t.propertyName),
String: t.string,
Number: t.number,
Expr: [t.regexp, t.emphasis],
LineComment: t.lineComment,
BlockComment: t.blockComment,
ImportKeyword: t.keyword,
FromKeyword: t.keyword,
Goal: t.string,
"( )": t.paren,
":-": t.punctuation,
".": t.punctuation,
":": t.punctuation,
",": t.punctuation,
}),
indentNodeProp.add({
Rule: (context) => context.column(context.node.from) + context.unit,
}),
foldNodeProp.add({
Rule: foldInside,
}),
],
});

export const percivalLanguage = LRLanguage.define({
parser: parserWithMetadata,
languageData: {
commentTokens: {
line: "//",
block: { open: "/*", close: "*/" },
},
},
});

/** CodeMirror extension for Percival language support. */
export function percival() {
return new LanguageSupport(percivalLanguage, []);
}
73 changes: 73 additions & 0 deletions src/lib/codemirror/percival.grammar
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Lezer LR(1) grammar for Percival, used for syntax highlighting in CodeMirror.

@top Program { entry* }

entry {
Rule |
Import
}

Rule {
Fact "." |
Fact ":-" (Clause ",")* Clause "."
}

Clause {
Fact |
Expr
}

Fact {
TableName "(" ((Prop ",")* Prop)? ")"
}

Prop {
PropName (":" Value)?
}

Value {
Identifier |
literal |
Expr
}

literal {
Number |
String
}

Import {
ImportKeyword TableName FromKeyword String
}

@tokens {
Identifier { $[a-zA-Z_] $[a-zA-Z_0-9]* }

TableName { $[a-zA-Z_] $[a-zA-Z_0-9]* }

PropName { $[a-zA-Z_] $[a-zA-Z_0-9]* }

Number { $[-+]? ($[0-9] "_"?)+ ("." ($[0-9] "_"?)+)? ($[eE] $[-+]? ($[0-9] "_"?)+)? }

String { '"' (!["\\] | "\\" _)* '"' }

Expr { "`" ![`]* "`" }

LineComment { "//" ![\n]* }

BlockComment { "/*" _*? "*/" }

space { $[ \t\n\r]+ }

"(" ")"

":" ":-" "." ","

ImportKeyword { "@import" }

FromKeyword { "from" }
}

@skip { space | LineComment | BlockComment }

@detectDelim
3 changes: 3 additions & 0 deletions src/lib/codemirror/percival.grammar.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Parser } from "lezer";

export declare const parser: Parser;
18 changes: 17 additions & 1 deletion vite.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { resolve } from "path";
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { buildParserFile } from "@lezer/generator";

/** A simple custom Rollup-compatible plugin to import Lezer grammars. */
function lezer() {
return {
name: "rollup-plugin-lezer",
transform(src, id) {
if (/\.grammar$/.test(id)) {
return {
code: buildParserFile(src).parser,
map: null,
};
}
},
};
}

// https://vitejs.dev/config/
export default defineConfig({
Expand All @@ -13,7 +29,7 @@ export default defineConfig({
"@": resolve("src"),
},
},
plugins: [svelte()],
plugins: [svelte(), lezer()],
server: {
proxy: {
"/api": {
Expand Down

0 comments on commit bc7ea2d

Please sign in to comment.