Skip to content

Commit

Permalink
feat(tags): dev.include template tag to include templates into each o…
Browse files Browse the repository at this point in the history
…ther

close #18
  • Loading branch information
stdword committed Feb 12, 2024
1 parent 555af89 commit 7a031d8
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 81 deletions.
36 changes: 19 additions & 17 deletions src/app.tsx
Expand Up @@ -40,7 +40,7 @@ async function onAppSettingsChanged() {
dayjs.updateLocale('en', {
weekStart: 1,
})
}
}

async function init() {
if (DEV) {
Expand Down Expand Up @@ -225,23 +225,26 @@ async function main() {
showInsertUI(e.uuid)
})

registerBlockContextCopyCommand('Copy as 🏛template', commandTemplateName)
registerPageContextCopyCommand('Copy as 🏛template', commandTemplateName)
registerBlockContextCopyCommand('Copy as 🏛template', false)
registerPageContextCopyCommand('Copy as 🏛template', false)
handleTemplateCommand(commandTemplateName)
}

const commandTemplateViewName = 'template-view'
{
registerBlockContextCopyCommand('Copy as 🏛view', commandTemplateViewName)
registerPageContextCopyCommand('Copy as 🏛view', commandTemplateViewName)
registerBlockContextCopyCommand('Copy as 🏛view', true)
registerPageContextCopyCommand('Copy as 🏛view', true)
handleTemplateViewCommand(commandTemplateViewName)
}

const commandViewName = 'view'
{
const commandLabel = 'Insert inline 🏛view'
const code = 'c.page.name'
const commandGuide = RendererMacro.command(commandViewName).arg(`"${code}"`).toString()
const commandGuide = RendererMacro
.command(commandViewName)
.arg(`"${code}"`, {raw: true})
.toString()

logseq.App.registerCommandPalette({
key: 'insert-inline-view',
Expand Down Expand Up @@ -270,10 +273,10 @@ async function main() {
await postInit()
}

function registerBlockContextCopyCommand(label: string, commandName: string) {
function registerBlockContextCopyCommand(label: string, isView: boolean) {
logseq.Editor.registerBlockContextMenuItem(
label, async (e) => {
const macro = await templateMacroStringForBlock(e.uuid)
const macro = await templateMacroStringForBlock(e.uuid, isView)
if (!macro) {
console.debug(p`Assertion error: block should exists`, e.uuid)
return
Expand All @@ -285,11 +288,10 @@ function registerBlockContextCopyCommand(label: string, commandName: string) {
await logseq.UI.showMsg('Copied to clipboard', 'success', {timeout: 5000})
})
}

function registerPageContextCopyCommand(label: string, commandName: string) {
function registerPageContextCopyCommand(label: string, isView: boolean) {
logseq.App.registerPageMenuItem(
label, async ({ page: pageName }) => {
const command = await templateMacroStringForPage(pageName)
const command = await templateMacroStringForPage(pageName, isView)
if (!command) {
console.debug(p`Assertion error: page should exists`, pageName)
return
Expand All @@ -310,7 +312,7 @@ async function handleRequiredRef(ref: string, refUserName: string) {
}

return parseReference(ref)!
}
}
async function handleLogicErrors(func: Function) {
try {
await func()
Expand All @@ -324,7 +326,7 @@ async function handleLogicErrors(func: Function) {
else
console.error(p`${(error as Error).stack}`)
}
}
}

function handleTemplateCommand(commandName: string) {
let unload = logseq.App.onMacroRendererSlotted(async ({ slot, payload }) => {
Expand All @@ -350,7 +352,7 @@ function handleTemplateCommand(commandName: string) {
})
})
logseq.beforeunload(unload as unknown as () => Promise<void>)
}
}
function handleTemplateViewCommand(commandName: string) {
logseq.provideModel({
async editBlock(e: any) {
Expand Down Expand Up @@ -423,7 +425,7 @@ function handleTemplateViewCommand(commandName: string) {
})
})
logseq.beforeunload(unload as unknown as () => Promise<void>)
}
}
function handleViewCommand(commandName: string) {
const unload = logseq.App.onMacroRendererSlotted(async ({ slot, payload }) => {
const uuid = payload.uuid
Expand All @@ -446,11 +448,11 @@ function handleViewCommand(commandName: string) {

console.debug(p`Rendering view`, {slot, uuid, viewBody, args})
await handleLogicErrors(async () => {
await renderView(slot, uuid, viewBody, raw, args)
await renderView(slot, uuid, viewBody, args)
})
})
logseq.beforeunload(unload as unknown as () => Promise<void>)
}
}


export const App = (logseq: any) => {
Expand Down
17 changes: 13 additions & 4 deletions src/context.ts
Expand Up @@ -54,17 +54,26 @@ dayjs.extend(updateLocale)
dayjs.extend(logseqPlugin)


export interface ILogseqContext {
export interface ILogseqCurrentContext {
mode: 'template' | 'view'
currentPage: PageContext
currentBlock: BlockContext
}

export interface ILogseqCallContext {
identity: Context | {
slot: string,
key: string,
}
config: ConfigContext
page: PageContext | null
block: BlockContext | null
args: ArgsContext
}

export interface ILogseqContext extends ILogseqCallContext, ILogseqCurrentContext {
page: PageContext
currentPage: PageContext
block: BlockContext
currentBlock: BlockContext
args: ArgsContext

tags?: Context
self?: BlockContext
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/customized_eta.ts
Expand Up @@ -194,7 +194,7 @@ function compileBody(buff) {
// @ts-expect-error
const config = this.config

let returnStr = ''
let returnStr = 'include = undefined; includeAsync = undefined; layout = undefined;\n'
for (const currentBlock of buff) {
if (typeof currentBlock === 'string') {
const str = currentBlock
Expand Down
132 changes: 89 additions & 43 deletions src/logic.ts
Expand Up @@ -5,7 +5,7 @@ import { LogseqMarkup } from './extensions/mldoc_ast'
import { InlineTemplate, ITemplate, Template } from './template'
import {
ILogseqContext, Context, PageContext, BlockContext,
ArgsContext, ConfigContext,
ArgsContext, ConfigContext, ILogseqCurrentContext, ILogseqCallContext,
} from './context'
import {
p, IBlockNode, lockOn, sleep, LogseqReference, getPage, getBlock,
Expand All @@ -17,22 +17,43 @@ import {
import { RenderError, StateError, StateMessage } from './errors'


/**
* @raises StateError: Arg `:page` doesn't exist or improperly specified
*/
async function getCurrentContext(
slot: string,
template: ITemplate,
blockUUID: string,
argsContext: ArgsContext,
): Promise<ILogseqContext | null> {
mode: ILogseqCurrentContext['mode'],
): Promise<ILogseqCurrentContext | null> {
if (!blockUUID) {
// Where is uuid? It definitely should be here, but this is a bug:
// https://github.com/logseq/logseq/issues/8904
console.debug(p`Assertion error: this case should be filtered out in "isInsideMacro"`)
return null
}

const currentBlock = await logseq.Editor.getBlock(blockUUID)
if (!currentBlock) {
console.debug(p`logseq issue → rendering non-existed block / slot`)
return null
}

const currentPage = await logseq.Editor.getPage(currentBlock.page.id) as PageEntity
const currentPageContext = PageContext.createFromEntity(currentPage)
const currentBlockContext = BlockContext.createFromEntity(currentBlock, { page: currentPageContext })

return {
mode,
currentPage: currentPageContext,
currentBlock: currentBlockContext,
}
}

/**
* @raises StateError: Arg `:page` doesn't exist or improperly specified
* @raises StateError: Arg `:block` doesn't exist or improperly specified
*/
async function getCallContext(
slot: string,
template: ITemplate,
argsContext: ArgsContext,
): Promise<ILogseqCallContext> {
// fulfill args with template arg-props
const argsProps = template.getArgProperties()
for (const [ key, value ] of Object.entries(argsProps))
Expand Down Expand Up @@ -80,35 +101,41 @@ async function getCurrentContext(
contextBlock = blockExists
}

const currentBlock = await logseq.Editor.getBlock(blockUUID)
if (!currentBlock) {
console.debug(p`logseq issue → rendering non-existed block / slot`)
return null
}

const currentPage = await logseq.Editor.getPage(currentBlock.page.id) as PageEntity
const currentPageContext = PageContext.createFromEntity(currentPage)
const currentBlockContext = BlockContext.createFromEntity(currentBlock, { page: currentPageContext })

argsContext._hideUndefinedMode = true
return {
identity: new Context({ slot, key: slot.split('__', 2)[1].trim() }),
config: await ConfigContext.get(),

page: contextPage ? PageContext.createFromEntity(contextPage) : currentPageContext,
currentPage: currentPageContext,
page: contextPage ? PageContext.createFromEntity(contextPage) : null,
block: contextBlock ? BlockContext.createFromEntity(contextBlock) : null,
args: argsContext,
}
}

block: contextBlock ? BlockContext.createFromEntity(contextBlock) : currentBlockContext,
currentBlock: currentBlockContext,
async function getContext(
callContext: ILogseqCallContext,
currentContext: ILogseqCurrentContext,
): Promise<ILogseqContext> {
return {
mode: currentContext.mode,
identity: callContext.identity,
config: callContext.config,

args: argsContext,
page: callContext.page || currentContext.currentPage,
currentPage: currentContext.currentPage,

block: callContext.block || currentContext.currentBlock,
currentBlock: currentContext.currentBlock,

args: callContext.args,
}
}
}


/**
* @raises StateError: template doesn't exist
*/
async function getTemplateBlock(
export async function getTemplateBlock(
templateRef: LogseqReference
): Promise<[BlockEntity, string | undefined, LogseqReferenceAccessType]> {
let templateBlock: BlockEntity | null
Expand Down Expand Up @@ -144,7 +171,7 @@ async function getTemplateBlock(
return [ templateBlock, name, accessedVia ]
}

async function getTemplate(ref: LogseqReference): Promise<Template> {
export async function getTemplate(ref: LogseqReference): Promise<Template> {
const [ templateBlock, name, accessedVia ] = await getTemplateBlock(ref)

let includingParent: boolean | undefined
Expand Down Expand Up @@ -270,10 +297,15 @@ async (
if (handled)
return

const context = await getCurrentContext(slot, template, uuid, argsContext)
if (!context)
const currentContext = await getCurrentContext(uuid, 'template')
if (!currentContext)
return

const context = await getContext(
await getCallContext(slot, template, argsContext),
currentContext,
)

let rendered: IBlockNode
try {
rendered = await template.render(context)
Expand Down Expand Up @@ -335,20 +367,18 @@ async (
})

/**
* @raises StateError: template doesn't exist
* @raises StateMessage: template doesn't have any content (empty)
* @raises RenderError: template rendering error
*/
async function _renderTemplateView(
export async function compileTemplateView(
slot: string,
blockUUID: string,
template: ITemplate,
rawCode: RendererMacro,
argsContext: ArgsContext,
) {
const context = await getCurrentContext(slot, template, blockUUID, argsContext)
if (!context)
return
currentContext: ILogseqCurrentContext,
): Promise<string> {
const context = await getContext(
await getCallContext(slot, template, argsContext),
currentContext,
)

let rendered: IBlockNode
try {
Expand All @@ -366,7 +396,7 @@ async function _renderTemplateView(
)
}

const compiled = await walkBlockTree(rendered, async (b, lvl) => {
let compiled = await walkBlockTree(rendered, async (b, lvl) => {
const content = (b.content || '').toString()
if (!content.trim())
return ''
Expand All @@ -375,6 +405,9 @@ async function _renderTemplateView(
})
console.debug(p`Markup compiled:`, {data: compiled})

if (compiled.content === '' && compiled.children.length === 1)
compiled = compiled.children[0]

const htmlFold = (node: IBlockNode, level = 0): string => {
const children = () => node.children.map(
(n) => htmlFold(n as IBlockNode, level + 1)
Expand Down Expand Up @@ -413,6 +446,20 @@ async function _renderTemplateView(
const view = htmlFold(compiled)
console.debug(p`View folded:`, {view})

return view
}

async function _renderTemplateView(
slot: string,
blockUUID: string,
template: ITemplate,
argsContext: ArgsContext,
) {
const currentContext = await getCurrentContext(blockUUID, 'view')
if (!currentContext)
return

const view = await compileTemplateView(slot, template, argsContext, currentContext)
provideHTML(blockUUID, view, slot)
}

Expand All @@ -430,20 +477,19 @@ export async function renderTemplateView(
if (handled)
return

await _renderTemplateView(slot, blockUUID, template, rawCode, argsContext)
}
await _renderTemplateView(slot, blockUUID, template, argsContext)
}

export async function renderView(
slot: string,
blockUUID: string,
viewBody: string,
rawCode: RendererMacro,
args: string[] = [],
) {
const template = getView(viewBody)
const argsContext = ArgsContext.create(template.name, args)
await _renderTemplateView(slot, blockUUID, template, rawCode, argsContext)
}
await _renderTemplateView(slot, blockUUID, template, argsContext)
}

export async function templateMacroStringForBlock(uuid: string, isView: boolean = false): Promise<string> {
const block = await logseq.Editor.getBlock(uuid)
Expand Down

0 comments on commit 7a031d8

Please sign in to comment.