mirror of
https://github.com/h-sphere/sql-seal.git
synced 2026-07-22 10:10:28 +00:00
101 lines
No EOL
3.9 KiB
TypeScript
101 lines
No EOL
3.9 KiB
TypeScript
import { App, MarkdownPostProcessorContext } from "obsidian"
|
|
import { displayError, displayNotice } from "./ui"
|
|
import { resolveFrontmatter } from "./frontmatter"
|
|
import { hashString } from "./hash"
|
|
import { extractCtes, prefixedIfNotGlobal, updateTables } from "./sqlReparseTables"
|
|
import { SqlSealDatabase } from "./database"
|
|
import { Logger } from "./logger"
|
|
import { TablesManager } from "./dataLoader/collections/tablesManager"
|
|
import { QueryManager } from "./dataLoader/collections/queryManager"
|
|
import { parseLanguage, Table, TableWithParentPath } from "./grammar/newParser"
|
|
import { RendererRegistry, RenderReturn } from "./rendererRegistry"
|
|
|
|
export class SqlSealCodeblockHandler {
|
|
get globalTables() {
|
|
return ['files', 'tags', 'tasks', 'xyz'] // Make this come from SealFileSync and plugins.
|
|
}
|
|
constructor(
|
|
private readonly app: App,
|
|
private readonly db: SqlSealDatabase,
|
|
private logger: Logger,
|
|
private tableManager: TablesManager,
|
|
private queryManager: QueryManager,
|
|
private rendererRegistry: RendererRegistry
|
|
) {
|
|
}
|
|
|
|
setupTableSignals(tables: Array<TableWithParentPath>) {
|
|
tables.forEach(t => {
|
|
this.tableManager.registerTable(t.tableName, t.fileName, t.parentPath)
|
|
})
|
|
}
|
|
|
|
setupQuerySignals({ statement, tables }: ReturnType<typeof updateTables>, renderer: RenderReturn, ctx: MarkdownPostProcessorContext, el: Element) {
|
|
const frontmatter = resolveFrontmatter(ctx, this.app)
|
|
const renderSelect = async () => {
|
|
try {
|
|
const { data, columns } = await this.db.select(statement, frontmatter ?? {})
|
|
renderer.render({ data, columns })
|
|
} catch (e) {
|
|
renderer.error(e.toString())
|
|
}
|
|
}
|
|
|
|
|
|
const sig = this.queryManager.registerQuery(ctx.docId, tables)
|
|
const unsubscribe = sig(() => {
|
|
if (!el.isConnected) {
|
|
unsubscribe()
|
|
return
|
|
}
|
|
renderSelect()
|
|
})
|
|
}
|
|
|
|
getHandler() {
|
|
return async (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
|
const prefix = hashString(ctx.sourcePath)
|
|
|
|
let results;
|
|
let renderer: RenderReturn;
|
|
|
|
try {
|
|
await this.db.connect()
|
|
// Before we display data, we need to parse query to see if there is query part.
|
|
results = parseLanguage(source)
|
|
if (results.queryPart) {
|
|
// FIXME: this one probably needs both renderer and error api.
|
|
renderer = this.rendererRegistry.prepareRender(results.intermediateContent)(el)
|
|
} else {
|
|
displayNotice(el, `Creating tables: ${results.tables.map(t => t.tableName).join(', ')}`)
|
|
}
|
|
|
|
const prefixedTables = results.tables.map(t => {
|
|
return {
|
|
...t,
|
|
tableName: prefixedIfNotGlobal(t.tableName, this.globalTables, prefix),
|
|
parentPath: ctx.sourcePath
|
|
}
|
|
}) satisfies TableWithParentPath[]
|
|
|
|
this.setupTableSignals(prefixedTables)
|
|
|
|
} catch (e) {
|
|
displayError(el, e.toString())
|
|
return
|
|
}
|
|
|
|
try {
|
|
if (results.queryPart) {
|
|
const { statement, tables } = updateTables(results.queryPart!, [...this.globalTables], prefix)
|
|
const ctes = extractCtes(results.queryPart)
|
|
const tablesWithoutCtes = tables.filter(t => !ctes.includes(t))
|
|
this.setupQuerySignals({ statement, tables: tablesWithoutCtes }, renderer!, ctx, el)
|
|
}
|
|
} catch (e) {
|
|
renderer!.error(e.toString())
|
|
}
|
|
|
|
}
|
|
}
|
|
} |