h-sphere_sql-seal/src/codeblockHandler/CodeblockProcessor.ts

124 lines
4.3 KiB
TypeScript
Raw Normal View History

2025-01-09 12:32:24 +00:00
import { OmnibusRegistrator } from "@hypersphere/omnibus";
2025-01-10 11:03:43 +00:00
import { App, MarkdownPostProcessorContext, MarkdownRenderChild, TFile } from "obsidian";
import { SqlSealDatabase } from "../database/database";
import { Sync } from "../datamodel/sync";
import { ParserTableDefinition } from "../datamodel/syncStrategy/types";
import { parseLanguage } from "../grammar/newParser";
import { RendererRegistry, RenderReturn } from "../renderer/rendererRegistry";
import { transformQuery } from "../sql/sqlTransformer";
import { displayError, displayNotice } from "../utils/ui";
import SqlSealPlugin from "../main";
import { registerObservers } from "../utils/registerObservers";
import { IntermediateContent, parseIntermediateContent } from "src/grammar/parseIntermediateContent";
2025-01-09 12:32:24 +00:00
export class CodeblockProcessor extends MarkdownRenderChild {
registrator: OmnibusRegistrator
renderer: RenderReturn
private flags: IntermediateContent['flags']
private extrasEl: HTMLElement
private explainEl: HTMLElement
constructor(
private el: HTMLElement,
private source: string,
private ctx: MarkdownPostProcessorContext,
private rendererRegistry: RendererRegistry,
private db: SqlSealDatabase,
private plugin: SqlSealPlugin,
private app: App,
private sync: Sync) {
super(el)
this.registrator = this.sync.getRegistrator()
}
query: string;
async onload() {
try {
const results = parseLanguage(this.source, this.ctx.sourcePath)
if (results.tables) {
await this.registerTables(results.tables)
if (!results.queryPart) {
displayNotice(this.el, `Creating tables: ${results.tables.map(t => t.tableAlias).join(', ')}`)
return
}
}
const { intermediateContent } = results
const config = parseIntermediateContent(intermediateContent, {
flags: {
refresh: this.plugin.settings.enableDynamicUpdates,
explain: false
}
})
this.flags = config.flags
let rendererEl = this.el
if (this.flags.explain) {
this.extrasEl = this.el.createDiv({ cls: 'sqlseal-extras-container' })
if (config.flags.explain) {
this.explainEl = this.extrasEl.createEl('pre', { cls: 'sqlseal-extras-explain-container' })
}
rendererEl = this.el.createDiv({ cls: 'sqlseal-renderer-container' })
}
this.renderer = this.rendererRegistry.prepareRender(config.renderer.toLowerCase(), config.rendererArguments)(rendererEl)
// FIXME: probably should save the one before transform and perform transform every time we execute it.
this.query = results.queryPart
await this.render()
} catch (e) {
displayError(this.el, e.toString())
}
}
onunload() {
this.registrator.offAll()
}
async render() {
try {
const registeredTablesForContext = await this.sync.getTablesMappingForContext(this.ctx.sourcePath)
const res = transformQuery(this.query, registeredTablesForContext)
const transformedQuery = res.sql
if (this.flags.refresh) {
registerObservers({
bus: this.registrator,
callback: () => this.render(),
fileName: this.ctx.sourcePath,
tables: res.mappedTables
})
}
const file = this.app.vault.getFileByPath(this.ctx.sourcePath)
if (!file) {
return
}
const fileCache = this.app.metadataCache.getFileCache(file)
if (this.flags.explain) {
// Rendering explain
const result = await this.db.explain(transformedQuery, fileCache?.frontmatter ?? { })
this.explainEl.textContent = result
}
const { data, columns } = await this.db.select(transformedQuery, fileCache?.frontmatter ?? {})
this.renderer.render({ data, columns })
} catch (e) {
this.renderer.error(e.toString())
}
}
async registerTables(tables: ParserTableDefinition[]) {
await Promise.all(tables.map((table) => this.sync.registerTable(table)))
}
}