Is there any code which can complete from grammar? I got to this example, but its not completing FROM and SELECT keywords. I didn’t find anything so I guess there is no solution and it has to be taken care of manually.
import { EditorView, basicSetup } from "codemirror";
import { EditorState } from "@codemirror/state";
import { Language, syntaxTree } from "@codemirror/language";
import { LRLanguage, LanguageSupport } from "@codemirror/language";
import { styleTags, tags as t } from "@lezer/highlight";
import { autocompletion } from "@codemirror/autocomplete";
import { buildParser } from '@lezer/generator';
/* sample grammar and completion for
FROM table select field1, field2
*/
const tableSpecs = [
{ tablename: "users", fields: ["id", "name", "email"] },
{ tablename: "orders", fields: ["order_id", "user_id", "date"] }
];
// Lezer grammar for the DSL
const grammar = `
@top Query { "FROM" TableName "SELECT" FieldList }
TableName { identifier }
FieldList { Field ("," Field)* }
Field { identifier }
@tokens {
identifier { std.asciiLetter+ }
whitespace { std.whitespace+ }
}
@skip { whitespace }
`;
const parser = buildParser(grammar);
// Completion source
function sqlCompletion(context: any) {
console.log(`context ${context}`);
let { state, pos } = context;
let tree = state.tree;
let node = tree.resolve(pos, -1); // Innermost node at cursor
// Check if we're in an Identifier node
if (node.type.name === "TableName" && node.parent) {
let word = context.matchBefore(/\w*/);
if (!word) return null;
let options = tableSpecs.map(spec => ({ label: spec.tablename }));
options = options.filter(opt => opt.label.startsWith(word.text));
return {
from: word.from,
options
};
}
if (node.type.name === "Field" && node.parent) {
console.log("=== Field");
// Find the Query node by traversing up
let current = node.parent;
while (current && current.type.name !== "Query") {
current = current.parent;
}
if (!current) return null;
// Get the TableName node under Query
let tableNameNode = current.getChildren("TableName")[0];
if (!tableNameNode) return null;
// Extract table name text
let tableName = state.doc.sliceString(tableNameNode.from, tableNameNode.to);
console.log(tableName);
let tableSpec = tableSpecs.find(spec => spec.tablename === tableName);
if (!tableSpec) return null;
let word = context.matchBefore(/\w*/);
if (!word) return null;
let options = tableSpec.fields.map(field => ({ label: field }));
options = options.filter(opt => opt.label.startsWith(word.text));
return {
from: word.from,
options
};
}
return null;
}
const sqlLang = LRLanguage.define({
parser: parser.configure({
props: [
styleTags({
TableName: t.variableName,
FieldName: t.propertyName,
"SELECT FROM": t.keyword,
}),
],
}),
});
export function example() {
return new LanguageSupport(sqlLang, [
sqlLang.data.of({autocomplete: sqlCompletion})
])
}
;(window as any).view = new EditorView({
doc: 'FROM u' ,
extensions: [
basicSetup,
example(),
autocompletion({ override: [sqlCompletion] })
],
parent: document.body
})