diff --git a/.prettierignore b/.prettierignore index 7b0ce354..a0473a26 100644 --- a/.prettierignore +++ b/.prettierignore @@ -28,3 +28,7 @@ pkg/*/node_modules/ !/pkg/**/*.test.ts !docs/.vitepress/config.mts + +# grammar +!/grammar/src/index.ts +!/grammar/utils/prepare_complete.mjs diff --git a/grammar/rollup.config.js b/grammar/rollup.config.js index 690b5bb6..2e3fbfd8 100644 --- a/grammar/rollup.config.js +++ b/grammar/rollup.config.js @@ -2,20 +2,20 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import typescript from "@rollup/plugin-typescript" -import {lezer} from "@lezer/generator/rollup" +import typescript from '@rollup/plugin-typescript'; +import { lezer } from '@lezer/generator/rollup'; export default { - input: "src/index.ts", - external: id => id != "tslib" && !/^(\.?\/|\w:)/.test(id), - output: [ - {file: "dist/index.cjs", format: "cjs"}, - {dir: "./dist", format: "es"} - ], - plugins: [ - typescript({ - exclude: ["./utils/*"] - }), - lezer(), - ] -} + input: 'src/index.ts', + external: (id) => id != 'tslib' && !/^(\.?\/|\w:)/.test(id), + output: [ + { file: 'dist/index.cjs', format: 'cjs' }, + { dir: './dist', format: 'es' }, + ], + plugins: [ + typescript({ + exclude: ['./utils/*'], + }), + lezer(), + ], +}; diff --git a/grammar/src/complete.ts b/grammar/src/complete.ts index a09fe23b..b473c074 100644 --- a/grammar/src/complete.ts +++ b/grammar/src/complete.ts @@ -2,54 +2,58 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import { CompletionContext, snippetCompletion } from "@codemirror/autocomplete"; -import { fullStatementTemplates } from "./complete_statement"; +import { CompletionContext, snippetCompletion } from '@codemirror/autocomplete'; +import { fullStatementTemplates } from './complete_statement'; // Helper function to strip quotes from matched strings function stripQuotes(s: string) { - return s.replace(/^'|'$/g, ""); + return s.replace(/^'|'$/g, ''); } const fullStatementSnippets = fullStatementTemplates.map((x) => { let n = 1; - return snippetCompletion(x.label.replace(/''/g, () => `'\${${n++}:}'`), x); + return snippetCompletion( + x.label.replace(/''/g, () => `'\${${n++}:}'`), + x, + ); }); export function completeStatement(context: CompletionContext) { - const line = context.state.doc.lineAt(context.pos); - let textBefore = context.state.sliceDoc(line.from, context.pos); - const triggerMatch = /^[GT].*$/i.exec(textBefore); - - if (triggerMatch) { - const strings = textBefore.match(/'([^']*)'/g); - textBefore = textBefore.toLowerCase() - if (!strings) { - return { - from: context.pos - triggerMatch[0].length, - options: fullStatementSnippets, - validFor: /^.*$/, - }; - } - - const strippedStrings = strings.map(stripQuotes); - - const templateOption = fullStatementTemplates.map((x) => { - let n = 1; - let m = 0; - return snippetCompletion( - x.label.replace(/''/g, () => `'\${${n}:${strippedStrings[n++-1] || ""}}'`), - { - label: x.label.replace(/''/g, () => `${strings[m++] || "''"}`), - type: x.type - }); - }) - - return { - from: context.pos - textBefore.length, - options: templateOption, - validFor: /^.*$/, - }; - } - - return null; + const line = context.state.doc.lineAt(context.pos); + let textBefore = context.state.sliceDoc(line.from, context.pos); + const triggerMatch = /^[GT].*$/i.exec(textBefore); + + if (triggerMatch) { + const strings = textBefore.match(/'([^']*)'/g); + textBefore = textBefore.toLowerCase(); + if (!strings) { + return { + from: context.pos - triggerMatch[0].length, + options: fullStatementSnippets, + validFor: /^.*$/, + }; + } + + const strippedStrings = strings.map(stripQuotes); + + const templateOption = fullStatementTemplates.map((x) => { + let n = 1; + let m = 0; + return snippetCompletion( + x.label.replace(/''/g, () => `'\${${n}:${strippedStrings[n++ - 1] || ''}}'`), + { + label: x.label.replace(/''/g, () => `${strings[m++] || "''"}`), + type: x.type, + }, + ); + }); + + return { + from: context.pos - textBefore.length, + options: templateOption, + validFor: /^.*$/, + }; + } + + return null; } diff --git a/grammar/src/index.ts b/grammar/src/index.ts index d0121f39..dc5f040a 100644 --- a/grammar/src/index.ts +++ b/grammar/src/index.ts @@ -2,48 +2,55 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import { parser } from "./syntax.grammar" -import { LRLanguage, LanguageSupport, HighlightStyle, syntaxHighlighting,} from "@codemirror/language" -import { styleTags, tags as t } from "@lezer/highlight" -import { completeStatement } from "./complete" +import { parser } from './syntax.grammar'; +import { + LRLanguage, + LanguageSupport, + HighlightStyle, + syntaxHighlighting, +} from '@codemirror/language'; +import { styleTags, tags as t } from '@lezer/highlight'; +import { completeStatement } from './complete'; const syntax_colors = syntaxHighlighting( HighlightStyle.define( - [ - { tag: t.heading, color: "purple" }, - { tag: t.heading1, color: "gray" }, - {tag: t.variableName, color: "red"}, - { tag: t.keyword, color: "green" }, - {tag: t.string, color: "blue"}, - {tag: t.lineComment, color: "gray"}, - {tag: t.heading2, color: "black"} - ], - { all: { color: "black" } } - ) - ); - export const SlangroomLanguage = LRLanguage.define({ + [ + { tag: t.heading, color: 'purple' }, + { tag: t.heading1, color: 'gray' }, + { tag: t.variableName, color: 'red' }, + { tag: t.keyword, color: 'green' }, + { tag: t.string, color: 'blue' }, + { tag: t.lineComment, color: 'gray' }, + { tag: t.heading2, color: 'black' }, + ], + { all: { color: 'black' } }, + ), +); + +export const SlangroomLanguage = LRLanguage.define({ parser: parser.configure({ props: [ styleTags({ - "given then when and in inside if endif foreach endforeach" : t.variableName, - "have send open connect print output" : t.keyword, - "rule VersionRule! GenericRule!": t.heading, - "scenario ScenarioType/... ScenarioComment!": t.heading1, - "DbAction! EthereumAction! FsAction! GitAction! HelpersAction! HttpAction! JsonSchemaAction! OAuthAction! PocketbaseAction! QrCodeAction! RedisAction! ShellAction! TimestampAction! WalletAction! ZencodeAction!": t.heading2, + 'given then when and in inside if endif foreach endforeach': t.variableName, + 'have send open connect print output': t.keyword, + 'rule VersionRule! GenericRule!': t.heading, + 'scenario ScenarioType/... ScenarioComment!': t.heading1, + 'DbAction! EthereumAction! FsAction! GitAction! HelpersAction! HttpAction! JsonSchemaAction! OAuthAction! PocketbaseAction! QrCodeAction! RedisAction! ShellAction! TimestampAction! WalletAction! ZencodeAction!': + t.heading2, StringLiteral: t.string, Comment: t.lineComment, - }) - ] + }), + ], }), languageData: { - commentTokens: { line: "#" } - } -}) + commentTokens: { line: '#' }, + }, +}); const ac = SlangroomLanguage.data.of({ - autocomplete: completeStatement -}) + autocomplete: completeStatement, +}); export function Slangroom() { - return new LanguageSupport(SlangroomLanguage, [syntax_colors, ac]) + return new LanguageSupport(SlangroomLanguage, [syntax_colors, ac]); } diff --git a/grammar/src/syntax.grammar.d.ts b/grammar/src/syntax.grammar.d.ts index a9ceb9c7..bbdabde3 100644 --- a/grammar/src/syntax.grammar.d.ts +++ b/grammar/src/syntax.grammar.d.ts @@ -2,6 +2,6 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import {LRParser} from "@lezer/lr" +import { LRParser } from '@lezer/lr'; -export declare const parser: LRParser +export declare const parser: LRParser; diff --git a/grammar/src/syntax.grammar.terms.d.ts b/grammar/src/syntax.grammar.terms.d.ts index 6b4aa902..fcd3978b 100644 --- a/grammar/src/syntax.grammar.terms.d.ts +++ b/grammar/src/syntax.grammar.terms.d.ts @@ -2,4 +2,4 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -export const eof: number +export const eof: number; diff --git a/grammar/src/tokens.js b/grammar/src/tokens.js index 0bb779b5..f418327c 100644 --- a/grammar/src/tokens.js +++ b/grammar/src/tokens.js @@ -2,25 +2,205 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import {ExternalTokenizer} from "@lezer/lr" -import { eof,version, unknown, ignore, have, a, my, that, valid, inside, named, am, known, as, connect, open, to, and, send, statement, parameters, record, table, variable, name, address, transaction_id, - addresses, transaction, sc, path, content, commit, paths, array, values, value, sources, properties, headers, object, json_data, json_schema, request, uri, server_data, client, expires_in, - token, request_uri, data, my_credentials, email, list_parameters, show_parameters, create_parameters, record_parameters, update_parameters, delete_parameters, send_parameters, url, text, command, jwk, - holder, fields, verifier_url, issued_vc, disclosed, nonce, sk, script, key, keys, extra, conf, execute, sql, _with, read, file, the, of, database, save, ethereum, bytes, balance, suggested, - gas, price, id, after, broadcast, erc20, decimals, symbol, total, supply, erc721, _in, owner, asset, download, extract, verbatim, store, list, directory, exists, does, not, exist, verify, git, - repository, clone, create, _new, manipulate, get, set, merge, omit, concat, compact, pick, _do, sequential, parallel, same, post, put, patch, validate, json, generate, access, authorization, code, - details, from, add, start, pb, capacitor, login, ask, password, reset, some, records, one, update, qr, write, into, redis, _delete, shell, fetch, local, timestamp, milliseconds, seconds, present, vc, sd, jwt, - p256, _public, pretty, print, zencode, output, is, given, then, when, rule, scenario, _if, endif, foreach, endforeach, I, } from "./syntax.grammar.terms" +import { ExternalTokenizer } from '@lezer/lr'; +import { + eof, + version, + unknown, + ignore, + have, + a, + my, + that, + valid, + inside, + named, + am, + known, + as, + connect, + open, + to, + and, + send, + statement, + parameters, + record, + table, + variable, + name, + address, + transaction_id, + addresses, + transaction, + sc, + path, + content, + commit, + paths, + array, + values, + value, + sources, + properties, + headers, + object, + json_data, + json_schema, + request, + uri, + server_data, + client, + expires_in, + token, + request_uri, + data, + my_credentials, + email, + list_parameters, + show_parameters, + create_parameters, + record_parameters, + update_parameters, + delete_parameters, + send_parameters, + url, + text, + command, + jwk, + holder, + fields, + verifier_url, + issued_vc, + disclosed, + nonce, + sk, + script, + key, + keys, + extra, + conf, + execute, + sql, + _with, + read, + file, + the, + of, + database, + save, + ethereum, + bytes, + balance, + suggested, + gas, + price, + id, + after, + broadcast, + erc20, + decimals, + symbol, + total, + supply, + erc721, + _in, + owner, + asset, + download, + extract, + verbatim, + store, + list, + directory, + exists, + does, + not, + exist, + verify, + git, + repository, + clone, + create, + _new, + manipulate, + get, + set, + merge, + omit, + concat, + compact, + pick, + _do, + sequential, + parallel, + same, + post, + put, + patch, + validate, + json, + generate, + access, + authorization, + code, + details, + from, + add, + start, + pb, + capacitor, + login, + ask, + password, + reset, + some, + records, + one, + update, + qr, + write, + into, + redis, + _delete, + shell, + fetch, + local, + timestamp, + milliseconds, + seconds, + present, + vc, + sd, + jwt, + p256, + _public, + pretty, + print, + zencode, + output, + is, + given, + then, + when, + rule, + scenario, + _if, + endif, + foreach, + endforeach, + I, +} from './syntax.grammar.terms'; export const Eoftoken = new ExternalTokenizer( (input) => { - if (input.next < 0) { - input.acceptToken(eof); - } + if (input.next < 0) { + input.acceptToken(eof); + } }, - { contextual: true, fallback: true } - ); - const keywordMap = { + { contextual: true, fallback: true }, +); +const keywordMap = { version, unknown, ignore, @@ -205,15 +385,14 @@ export const Eoftoken = new ExternalTokenizer( foreach, endforeach, I, - p256 - } - + p256, +}; - export function keywords(name) { - if (name == "I") { - return keywordMap[name] +export function keywords(name) { + if (name == 'I') { + return keywordMap[name]; } - let found = keywordMap[name.toLowerCase()] - return found == null ? -1 : found - } + let found = keywordMap[name.toLowerCase()]; + return found == null ? -1 : found; +} diff --git a/grammar/test/test.js b/grammar/test/test.js index 009f7231..b3ee96dc 100644 --- a/grammar/test/test.js +++ b/grammar/test/test.js @@ -2,20 +2,23 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import {SlangroomLanguage} from "../dist/index.js" -import {fileTests} from "@lezer/generator/dist/test" +import { SlangroomLanguage } from '../dist/index.js'; +import { fileTests } from '@lezer/generator/dist/test'; -import * as fs from "fs" -import * as path from "path" +import * as fs from 'fs'; +import * as path from 'path'; import { fileURLToPath } from 'url'; -let caseDir = path.dirname(fileURLToPath(import.meta.url)) +let caseDir = path.dirname(fileURLToPath(import.meta.url)); for (let file of fs.readdirSync(caseDir)) { - if (!/\.txt$/.test(file)) continue + if (!/\.txt$/.test(file)) continue; - let name = /^[^\.]*/.exec(file)[0] - describe(name, () => { - for (let {name, run} of fileTests(fs.readFileSync(path.join(caseDir, file), "utf8"), file)) - it(name, () => run(SlangroomLanguage.parser)) - }) + let name = /^[^\.]*/.exec(file)[0]; + describe(name, () => { + for (let { name, run } of fileTests( + fs.readFileSync(path.join(caseDir, file), 'utf8'), + file, + )) + it(name, () => run(SlangroomLanguage.parser)); + }); } diff --git a/grammar/tsconfig.json b/grammar/tsconfig.json index 50249c2c..cb62f1a4 100644 --- a/grammar/tsconfig.json +++ b/grammar/tsconfig.json @@ -1,10 +1,10 @@ { - "compilerOptions": { - "strict": true, - "target": "es6", - "module": "es2020", - "newLine": "lf", - "moduleResolution": "node" - }, - "include": ["src/*.ts"] + "compilerOptions": { + "strict": true, + "target": "es6", + "module": "es2020", + "newLine": "lf", + "moduleResolution": "node" + }, + "include": ["src/*.ts"] }