mirror of
https://github.com/h-sphere/sql-seal.git
synced 2026-07-22 10:10:28 +00:00
Merge pull request #99 from h-sphere/feat/external-plugin-flags-register
chore: adding ability to register flags
This commit is contained in:
commit
4e3e30949f
8 changed files with 108 additions and 48 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
10
src/main.ts
10
src/main.ts
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}`)
|
||||
|
|
|
|||
Loading…
Reference in a new issue