diff --git a/src/cellParser/CellFunction.ts b/src/cellParser/CellFunction.ts index 753159c..d0b5983 100644 --- a/src/cellParser/CellFunction.ts +++ b/src/cellParser/CellFunction.ts @@ -4,4 +4,5 @@ export interface CellFunction { get name(): string; get sqlFunctionArgumentsCount(): number; prepare(content: T): CellParserResult; + renderAsString(content: T): string; } \ No newline at end of file diff --git a/src/cellParser/ModernCellParser.ts b/src/cellParser/ModernCellParser.ts index 2751c89..43e45d4 100644 --- a/src/cellParser/ModernCellParser.ts +++ b/src/cellParser/ModernCellParser.ts @@ -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`) } diff --git a/src/cellParser/parseResults.ts b/src/cellParser/parseResults.ts index 39fc058..d26811e 100644 --- a/src/cellParser/parseResults.ts +++ b/src/cellParser/parseResults.ts @@ -57,6 +57,16 @@ export class ParseResults { }) } + renderAsString(data: Record[], columns: string[]) { + return data.map(d => { + const res: Record = {} + for (const col of columns) { + res[col] = this.cellParser.renderAsString(d[col]) + } + return res + }) + } + initialise(parentEl: HTMLElement) { this.functions.forEach(fn => fn(parentEl)) } diff --git a/src/cellParser/parser/checkbox.ts b/src/cellParser/parser/checkbox.ts index 1fafd6c..61fcfd9 100644 --- a/src/cellParser/parser/checkbox.ts +++ b/src/cellParser/parser/checkbox.ts @@ -90,4 +90,19 @@ export class CheckboxParser implements CellFunction { } } } + + renderAsString(values: Args): string { + if (!isCheckboxProp(values)) { + if (!!values[0]) { + return '[x]' + } + return '[ ]' + } else { + if (values.checked) { + return '[x]' + } else { + return '[ ] ' + } + } + } } \ No newline at end of file diff --git a/src/cellParser/parser/image.ts b/src/cellParser/parser/image.ts index 376264e..38abd17 100644 --- a/src/cellParser/parser/image.ts +++ b/src/cellParser/parser/image.ts @@ -17,10 +17,7 @@ export class ImageParser implements CellFunction { 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 { 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() + } + } } \ No newline at end of file diff --git a/src/cellParser/parser/link.ts b/src/cellParser/parser/link.ts index 6ae1141..541cca1 100644 --- a/src/cellParser/parser/link.ts +++ b/src/cellParser/parser/link.ts @@ -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 { 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 { 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})` + } } } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 6f1b3f0..ba31960 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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)) diff --git a/src/renderer/MarkdownRenderer.ts b/src/renderer/MarkdownRenderer.ts index 99386cc..3a93ee8 100644 --- a/src/renderer/MarkdownRenderer.ts +++ b/src/renderer/MarkdownRenderer.ts @@ -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[]) => { return data.map(d => columns.map(c => String(d[c]))) @@ -11,7 +13,12 @@ const mapDataFromHeaders = (columns: string[], data: Record[]) => { 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)) } }) diff --git a/src/renderer/TemplateRenderer.ts b/src/renderer/TemplateRenderer.ts index 2309cd3..a0e9209 100644 --- a/src/renderer/TemplateRenderer.ts +++ b/src/renderer/TemplateRenderer.ts @@ -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