Merge pull request #99 from h-sphere/feat/external-plugin-flags-register

chore: adding ability to register flags
This commit is contained in:
Kacper Kula 2025-03-14 10:24:52 +00:00 committed by GitHub
commit 4e3e30949f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 108 additions and 48 deletions

View file

@ -5,6 +5,9 @@
Fixes:
- Fixed parsing arguments to the `file` function. Now parameters with symbols like `[]*` should work properly (i.e. JSONPath arguments)
Technical:
- Other plugins can now register flags to allow for extra configuration
# 0.27.0 (2025-02-17)
- Better syntax highlighting! Now it highlights SQL query parts
- Support for comments. You can now add comments like `--` and `/* */` to you queries

View file

@ -47,7 +47,7 @@ export class CodeblockProcessor extends MarkdownRenderChild {
}
const results = parseWithDefaults(this.source, this.rendererRegistry.getViewDefinitions(), defaults)
const results = parseWithDefaults(this.source, this.rendererRegistry.getViewDefinitions(), defaults, this.rendererRegistry.flags)
// const results = parseLanguage(this.source, this.ctx.sourcePath)
if (results.tables) {
@ -59,7 +59,6 @@ export class CodeblockProcessor extends MarkdownRenderChild {
}
this.flags = results.flags
let rendererEl = this.el
if (this.flags.explain) {
@ -116,7 +115,7 @@ export class CodeblockProcessor extends MarkdownRenderChild {
}
const { data, columns } = await this.db.select(transformedQuery, fileCache?.frontmatter ?? {})
this.renderer.render({ data, columns })
this.renderer.render({ data, columns, flags: this.flags })
} catch (e) {
this.renderer.error(e.toString())
}

View file

@ -154,7 +154,6 @@ export class Sync {
// TODO: THIS PART SHOULD BE REWRITTEN SOONish
const existingTableLog = await this.tableMapLog.getByAlias(reg.sourceFile, reg.tableAlias)
if (!existingTableLog) {
// console.log(`Registering new mapping ${reg.sourceFile} :: ${reg.tableAlias} -> ${tableName}`)
// Create new one
await this.tableMapLog.insert({
alias_name: reg.tableAlias,
@ -164,7 +163,6 @@ export class Sync {
} else {
// Check if it is the same mapping
if (existingTableLog.table_name !== tableName) {
// console.log(`Alias ${reg.sourceFile} :: ${reg.tableAlias} changed table, now it should refer to ${tableName})`)
await this.tableMapLog.deleteMapping(existingTableLog.id)
await this.tableMapLog.insert({
alias_name: reg.tableAlias,

View file

@ -51,7 +51,7 @@ export class SQLSealViewPlugin implements PluginValue {
destroy(): void { }
private parseWithGrammar(sql: string): Decorator[] {
const grammar = ohm.grammar(SQLSealLangDefinition(this.renderers.getViewDefinitions(), true))
const grammar = ohm.grammar(SQLSealLangDefinition(this.renderers.getViewDefinitions(), this.renderers.flags, true));
// FIXME: extend grammar with error line.

View file

@ -1,4 +1,5 @@
import * as ohm from 'ohm-js';
import { Flag } from 'src/renderer/rendererRegistry';
export interface ViewDefinition {
name: string,
@ -10,13 +11,20 @@ const viewName = (view: ViewDefinition) => `caseInsensitive<"${view.name}">`
export const SQLSealLangDefinition = (views: ViewDefinition[], enableErrors: boolean = false) => {
export const SQLSealLangDefinition = (views: ViewDefinition[], flags: readonly Flag[] = [], enableErrors: boolean = false) => {
const viewsDefinitions = views
.map(view => view.singleLine ?
`#(${viewName(view)} ${view.argument})`
: `${viewName(view)} ${view.argument}`)
: `${viewName(view)} ${view.argument}`)
.join(' | ')
const flagsDefinitions = flags.map(flag => {
return `caseInsensitive<"${flag.name}"> -- ${flag.key}`
}).join(' \n| ')
return String.raw`
SQLSealLang {
Grammar = (TableExpression | ViewExpression | FlagExpression | blank ${enableErrors ? '| errorLine' : ''})* SelectStmt*
@ -24,6 +32,7 @@ export const SQLSealLangDefinition = (views: ViewDefinition[], enableErrors: boo
FlagExpression = caseInsensitive<"REFRESH"> -- refresh
| caseInsensitive<"NO REFRESH"> -- norefresh
| caseInsensitive<"EXPLAIN"> -- explain
${flags.length ? '| ExtraFlags -- extraFlags' : ''}
TableExpression = tableKeyword identifier "=" TableDefinition
TableDefinition = fileOpening NonemptyListOf<listElement, ","> tableDefinitionClosing -- file
| tableOpening alnum+ tableDefinitionClosing -- mdtable
@ -38,6 +47,7 @@ export const SQLSealLangDefinition = (views: ViewDefinition[], enableErrors: boo
| (~ ("," | ")") any)+ -- unquoted
ViewExpression = ${viewsDefinitions}
ExtraFlags = ${flagsDefinitions}
anyObject = "{" (~selectKeyword any)*
handlebarsTemplate = (~selectKeyword any)*
selectKeyword = caseInsensitive<"WITH"> | caseInsensitive<"SELECT">
@ -57,36 +67,36 @@ export const SQLSealLangDefinition = (views: ViewDefinition[], enableErrors: boo
const generateSemantic = (grammar: ohm.Grammar) => {
const s = grammar.createSemantics()
s.addOperation<any>('toObject', {
Grammar: (entries, selectStatement) => {
const res = {
flags: {
},
renderer: {
name: 'GRID',
options: ''
},
tables: [] as any[],
query: ''
}
if (entries.children.length) {
entries.children.forEach(c => {
switch (c.ctorName) {
case 'TableExpression':
res.tables.push(c.toObject())
break;
case 'ViewExpression':
res.renderer = c.toObject()
break
case 'FlagExpression':
res.flags = {...res.flags, ...c.toObject()}
break
}
})
}
if (selectStatement) {
res.query = selectStatement.sourceString
}
const operations: ohm.ActionDict<any> = {
Grammar: (entries, selectStatement) => {
const res = {
flags: {
},
renderer: {
name: 'GRID',
options: ''
},
tables: [] as any[],
query: ''
}
if (entries.children.length) {
entries.children.forEach(c => {
switch (c.ctorName) {
case 'TableExpression':
res.tables.push(c.toObject())
break;
case 'ViewExpression':
res.renderer = c.toObject()
break
case 'FlagExpression':
res.flags = { ...res.flags, ...c.toObject() }
break
}
})
}
if (selectStatement) {
res.query = selectStatement.sourceString
}
return res
},
@ -128,8 +138,16 @@ const generateSemantic = (grammar: ohm.Grammar) => {
listElement_unquoted: (v) => v.sourceString,
_terminal() {
return this.sourceString
}
})
}
}
if ((grammar.rules['ExtraFlags'].body as any).ruleName) {
operations.ExtraFlags = (flag) => {
const key = flag.ctorName.substring('ExtraFlags_'.length)
return { [key]: true }
}
}
s.addOperation<any>('toObject', operations)
return s
}
@ -153,8 +171,8 @@ export interface ParserResult {
tables: Array<TableDefinition>
}
export const parse = (query: string, views: ViewDefinition[]) => {
const grammar = ohm.grammar(SQLSealLangDefinition(views))
export const parse = (query: string, views: ViewDefinition[], flags: readonly Flag[] = []) => {
const grammar = ohm.grammar(SQLSealLangDefinition(views, flags))
const match = grammar.match(query)
if (match.succeeded()) {
// Converting
@ -166,12 +184,12 @@ export const parse = (query: string, views: ViewDefinition[]) => {
}
export const parseWithDefaults = (query: string, views: ViewDefinition[], defaultvalues: ParserResult): ParserResult => {
const parsed = parse(query, views)
export const parseWithDefaults = (query: string, views: ViewDefinition[], defaultvalues: ParserResult, flags: readonly Flag[] = []): ParserResult => {
const parsed = parse(query, views, flags)
return {
flags: {...defaultvalues.flags, ...parsed.flags},
flags: { ...defaultvalues.flags, ...parsed.flags },
query: parsed.query || defaultvalues.query,
renderer: {...defaultvalues.renderer, ...parsed.renderer},
renderer: { ...defaultvalues.renderer, ...parsed.renderer },
tables: parsed.tables ?? []
} satisfies ParserResult
}

View file

@ -2,7 +2,7 @@ import { Menu, Plugin, TAbstractFile, TFile } from 'obsidian';
import { GridRenderer } from './renderer/GridRenderer';
import { MarkdownRenderer } from './renderer/MarkdownRenderer';
import { TableRenderer } from './renderer/TableRenderer';
import { RendererConfig, RendererRegistry } from './renderer/rendererRegistry';
import { Flag, RendererConfig, RendererRegistry } from './renderer/rendererRegistry';
import { DEFAULT_SETTINGS, SQLSealSettings, SQLSealSettingsTab } from './settings/SQLSealSettingsTab';
import { SqlSeal } from './sqlSeal';
import { SealFileSync } from './vaultSync/SealFileSync';
@ -208,6 +208,14 @@ export default class SqlSealPlugin extends Plugin {
this.cellParserRegistar.unregister(name)
}
registerSQLSealFlag(flag: Flag) {
this.rendererRegistry.registerFlag(flag)
}
unregisterSQLSealFlag(name: string) {
this.rendererRegistry.unregisterFlag(name)
}
async registerTable<const columns extends string[]>(plugin: Plugin, name: string, columns: columns) {
const hash = await FilepathHasher.sha256(`${plugin.manifest.name}`)
const tableName = `external_table_${hash}_name`

View file

@ -3,7 +3,7 @@ import SqlSealPlugin from "../main"
import { version } from '../../package.json'
import { RendererConfig } from "../renderer/rendererRegistry";
const API_VERSION = 1;
const API_VERSION = 2;
export class SQLSealRegisterApi {
registeredApis: Array<SQLSealApi> = []
@ -41,9 +41,16 @@ interface RegisteredFunction {
fn: CallableFunction
}
// TODO: use the type from registrator
export interface RegisteredFlag {
name: string,
key: string
}
export class SQLSealApi {
private views: Array<RegisteredView> = []
private functions: Array<RegisteredFunction> = []
private flags: Array<RegisteredFlag> = []
constructor(private readonly plugin: Plugin, private sqlSealPlugin: SqlSealPlugin) {
plugin.register(() => {
@ -71,6 +78,11 @@ export class SQLSealApi {
return this.sqlSealPlugin.registerTable(this.plugin, tableName, columns)
}
registerFlag(flag: RegisteredFlag) {
this.flags.push(flag)
this.sqlSealPlugin.registerSQLSealFlag(flag)
}
unregister() {
for(const view of this.views) {
this.sqlSealPlugin.unregisterSQLSealView(view.name)
@ -80,5 +92,9 @@ export class SQLSealApi {
for(const fn of this.functions) {
this.sqlSealPlugin.unregisterSQLSealFunction(fn.name)
}
for(const flag of this.flags) {
this.sqlSealPlugin.unregisterSQLSealFlag(flag.name)
}
}
}

View file

@ -16,9 +16,15 @@ export interface RendererConfig<T extends Record<string, any> = Record<string, a
viewDefinition: ViewDefinition
}
export interface Flag {
name: string;
key: string;
}
export class RendererRegistry {
renderers: Map<string, RendererConfig> = new Map()
renderersByKey: Map<string, RendererConfig> = new Map()
_extraFlags: Array<Flag> = []
constructor() { }
private default = 'grid'
@ -35,6 +41,18 @@ export class RendererRegistry {
this.renderersByKey.set(config.rendererKey, config)
}
registerFlag(flag: Flag) {
this._extraFlags.push(flag)
}
unregisterFlag(name: string) {
this._extraFlags = this._extraFlags.filter(f => f.name !== name)
}
get flags(): Readonly<typeof this._extraFlags> {
return this._extraFlags
}
unregister(uniqueName: string) {
if (!this.renderers.has(uniqueName)) {
throw new Error(`Renderer not registered: ${uniqueName}`)