This commit is contained in:
Kacper Kula 2025-04-02 10:40:00 +01:00
parent 3a1fcaccce
commit 0447368f53
9 changed files with 181 additions and 42 deletions

View file

@ -4,4 +4,5 @@ export interface CellFunction<T = unknown> {
get name(): string;
get sqlFunctionArgumentsCount(): number;
prepare(content: T): CellParserResult;
renderAsString(content: T): string;
}

View file

@ -40,6 +40,8 @@ export class ModernCellParser {
}
}
// If it is link string, use "a" renderer.
return content
} catch (e) {
console.error('Error parsing cell with data:', content, e)
@ -64,6 +66,39 @@ export class ModernCellParser {
}
}
renderAsString(content: string) {
try {
if (!content) {
return content
}
if (typeof content !== 'string') {
return content
}
if (content.startsWith('SQLSEALCUSTOM')) {
const parsedData = parse(content.slice('SQLSEALCUSTOM('.length, -1))
const renderer = this.getRenderer(parsedData)
return renderer!.renderAsString(parsedData.values)
}
// FIXME: bring this one back
// If it's an array, decode it and render each individual elements
if (isStringifiedArray(content)) {
try {
const parsed: string[] = parse(content)
return parsed.map(p => this.renderer)
return renderStringifiedArray(content)
} catch (e) {
return content
}
}
return content
} catch (e) {
console.error('Error parsing cell with data:', content, e)
return 'Parsing Error'
}
}
register(fn: CellFunction) {
this.functions.set(fn.name, fn)
}
@ -77,8 +112,13 @@ export class ModernCellParser {
}
private renderCustomElement(el: any) {
const renderer = this.getRenderer(el)
return renderer?.prepare(el.values)
}
private getRenderer(el: any) {
if (this.functions.has(el.type)) {
return this.functions.get(el.type)!.prepare(el.values)
return this.functions.get(el.type)!
} else {
throw new Error(`Custom function processor ${el.type} is not registered`)
}

View file

@ -57,6 +57,16 @@ export class ParseResults {
})
}
renderAsString(data: Record<string, any>[], columns: string[]) {
return data.map(d => {
const res: Record<string, string> = {}
for (const col of columns) {
res[col] = this.cellParser.renderAsString(d[col])
}
return res
})
}
initialise(parentEl: HTMLElement) {
this.functions.forEach(fn => fn(parentEl))
}

View file

@ -90,4 +90,19 @@ export class CheckboxParser implements CellFunction<Args> {
}
}
}
renderAsString(values: Args): string {
if (!isCheckboxProp(values)) {
if (!!values[0]) {
return '[x]'
}
return '[ ]'
} else {
if (values.checked) {
return '[x]'
} else {
return '[ ] '
}
}
}
}

View file

@ -17,10 +17,7 @@ export class ImageParser implements CellFunction<Args> {
return 2 // FIXME: should it be 1 or 2?
}
prepare([href, path]: Args): CellParserResult {
if (!isLinkLocal(href)) {
return createEl('img', { attr: { src: href } });
}
getResourcePath(href: string, path?: string) {
href = (href ?? '').trim()
if (href.startsWith('![[')) {
href = href.slice(3, -2)
@ -35,8 +32,35 @@ export class ImageParser implements CellFunction<Args> {
path = (path ? parentPath + '/' : '') + href
const file = this.app.vault.getFileByPath(path);
if (!file) {
return 'File does not exist'
throw new Error('File does not exist')
}
return this.create('img', { attr: { src: this.app.vault.getResourcePath(file) } });
return this.app.vault.getResourcePath(file)
}
prepare([href, path]: Args): CellParserResult {
if (!isLinkLocal(href)) {
return createEl('img', { attr: { src: href } });
}
try {
let resourcePath = this.getResourcePath(href, path)
return this.create('img', { attr: { src: resourcePath } });
} catch (e) {
return e
}
}
renderAsString([href, path]: Args): string {
if (!isLinkLocal(href)) {
return `![](${href})`
}
try {
const resourcePath = this.getResourcePath(href, path)
return `![[${resourcePath}]]`
} catch (e) {
return e.toString()
}
}
}

View file

@ -3,6 +3,7 @@ import { CellFunction } from "../CellFunction";
import { CellParserResult, Result } from "../ModernCellParser";
import { App } from "obsidian";
import { isLinkLocal, isLinktext, removeExtension } from "src/utils/ui/helperFunctions";
import { parse } from 'json5'
type Args = [string] | [string, string]
@ -18,6 +19,44 @@ export class LinkParser implements CellFunction<Args> {
return 2 // FIXME: should it be 1 or 2?
}
parseLink(href: string, name?: string) {
if (!name) {
name = href
}
href = href.trim()
let cls = ''
if (isLinktext(href)) {
const [path, linkName] = href.slice(2, -2).split('|')
const link = this.app.metadataCache.getFirstLinkpathDest(path, '')
if (name.trim() === href) {
name = linkName ?? path
}
if (!link) {
href = path
} else {
href = link.path
}
cls = 'internal-link'
} else if (isLinkLocal(href)) {
let fileHref = href
if (fileHref.endsWith('.md')) {
fileHref = fileHref.slice(0, -3)
}
if (name === href) {
const components = href.split('/')
name = removeExtension(components[components.length - 1])
}
cls = 'internal-link'
}
return {
name, href, cls
}
}
prepare([href, name]: Args) {
if (!href) {
return ''
@ -25,44 +64,47 @@ export class LinkParser implements CellFunction<Args> {
if (isStringifiedArray(href)) {
try {
// FIXME: this return should work differently
renderStringifiedArray(href, href => this.prepare([href]))
} catch (e) {
return href
}
}
const res = this.parseLink(href, name)
const el = this.create('a', { text: res.name, href: res.href, cls: res.cls })
return el
}
if (!name) {
name = href
renderAsString([href, name]: Args): string {
if (!href) {
return ''
}
href = href.trim()
let cls = ''
if (isLinktext(href)) {
const [path, linkName] = href.slice(2, -2).split('|')
const link = this.app.metadataCache.getFirstLinkpathDest(path, '')
if (name.trim() === href) {
name = linkName ?? path
}
if (!link) {
href = path
} else {
href = link.path
}
cls = 'internal-link'
} else if (isLinkLocal(href)) {
let fileHref = href
if (fileHref.endsWith('.md')) {
fileHref = fileHref.slice(0, -3)
}
if (name === href) {
const components = href.split('/')
name = removeExtension(components[components.length - 1])
}
cls = 'internal-link'
// if (isStringifiedArray(href)) {
// try {
// // FIXME: parse and run render as strings
// const arr: string[] = parse(href)
// return arr.map(e => this.renderAsString(e))
// const res = renderStringifiedArray(href, href => this.prepare([href]))
// if (res instanceof HTMLElement) {
// return res.outerHTML
// }
// return res
// } catch (e) {
// return href
// }
// }
const res = this.parseLink(href, name)
if (res.cls == 'internal-link') {
if (!name) {
return `[[${res.href}]]`
}
const el = this.create('a', { text: name, href, cls })
return el
return `[[${res.href}|${res.name}]]`
} else {
return `[${res.name}](${res.href})`
}
}
}

View file

@ -60,7 +60,7 @@ export default class SqlSealPlugin extends Plugin {
this.rendererRegistry.register('sql-seal-internal-table', new TableRenderer(this.app, this.cellParser))
this.rendererRegistry.register('sql-seal-internal-grid', new GridRenderer(this.app, this, this.cellParser))
this.rendererRegistry.register('sql-seal-internal-markdown', new MarkdownRenderer(this.app))
this.rendererRegistry.register('sql-seal-internal-markdown', new MarkdownRenderer(this.app, this.cellParser))
this.rendererRegistry.register('sql-seal-internal-list', new ListRenderer(this.app, this.cellParser))
this.rendererRegistry.register('sql-seal-internal-template', new TemplateRenderer(this.app, this.cellParser))

View file

@ -4,6 +4,8 @@ import { App } from "obsidian";
import { RendererConfig } from "../renderer/rendererRegistry";
import { displayError } from "../utils/ui";
import { ViewDefinition } from "../grammar/parser";
import { ParseResults } from "../cellParser/parseResults";
import { ModernCellParser } from "../cellParser/ModernCellParser";
const mapDataFromHeaders = (columns: string[], data: Record<string, any>[]) => {
return data.map(d => columns.map(c => String(d[c])))
@ -11,7 +13,12 @@ const mapDataFromHeaders = (columns: string[], data: Record<string, any>[]) => {
export class MarkdownRenderer implements RendererConfig {
constructor(private readonly app: App) { }
parseResult: ParseResults;
constructor(private readonly app: App, private readonly cellParser: ModernCellParser) {
this.parseResult = new ParseResults(cellParser)
}
get rendererKey() {
return 'markdown'
@ -34,7 +41,7 @@ export class MarkdownRenderer implements RendererConfig {
const tab = getMarkdownTable({
table: {
head: columns,
body: mapDataFromHeaders(columns, data)
body: mapDataFromHeaders(columns, this.parseResult.renderAsString(data, columns))
}
})

View file

@ -4,8 +4,8 @@ import { RendererConfig } from "./rendererRegistry";
import { displayError } from "../utils/ui";
import { ViewDefinition } from "../grammar/parser";
import Handlebars from "handlebars";
import { ParseResults } from "src/cellParser/parseResults";
import { ModernCellParser } from "src/cellParser/ModernCellParser";
import { ParseResults } from "../cellParser/parseResults";
import { ModernCellParser } from "../cellParser/ModernCellParser";
interface TemplateRendererConfig {
template: HandlebarsTemplateDelegate