from panflute import Doc , Div
from typing import Dict
import os
# This class is used to keep state while transforming the document using
# transform.py. For the context to be available to the html and TeX generators,
# individual keys must be manually assigned to the individual elements. This is
# done in transform.py.
#
# The context is also aware of its parent contexts and relevant data (such as
# metadata and commands) can be read from the closest parent context. Writing
# only happens to the current one.
#
# This class is basically an extension to panflute's doc, this is why metadata
# is read directly from it.
class Context :
def __init__ ( self , doc : Doc , path : str , parent : ' Context ' = None , trusted : bool = True ) :
self . parent = parent
self . _commands = { }
self . doc = doc
self . trusted = trusted
self . path = path
self . dir = os . path . dirname ( path ) if os . path . dirname ( path ) != " " else " . "
self . filename = os . path . basename ( path )
if self . get_metadata ( " flags " , immediate = True ) is None :
self . set_metadata ( " flags " , { } )
def get_command ( self , command : str ) :
if command in self . _commands :
return self . _commands [ command ]
elif self . parent :
return self . parent . get_command ( command )
else :
return None
def set_command ( self , command : str , val ) :
self . _commands [ command ] = val
def unset_command ( self , command : str ) :
del self . _commands [ command ]
def is_flag_set ( self , flag : str ) :
if self . get_metadata ( " flags. " + flag ) :
if self . get_metadata ( " flags. " + flag ) :
return True
else :
return False
elif self . parent :
return self . parent . is_flag_set ( flag )
else :
return False
def set_flag ( self , flag : str , val : bool ) :
self . set_metadata ( " flags. " + flag , val )
def unset_flag ( self , flag : str ) :
self . unset_metadata ( " flags. " + flag )
def get_metadata ( self , key : str , simple : bool = True , immediate : bool = False ) :
value = self . doc . get_metadata ( key , None , simple )
if value is not None :
return value
elif self . parent and not immediate :
return self . parent . get_metadata ( key )
else :
return None
def set_metadata ( self , key : str , value ) :
if key == " language " :
print ( " WARN: Setting language this way doesn ' t propagate to TeX. Either use the Front Matter or specify it additionally using the \\ languagexx macro. " )
meta = self . doc . metadata
key = key . split ( " . " )
for k in key [ : - 1 ] :
meta = meta [ k ]
meta [ key [ - 1 ] ] = value
def unset_metadata ( self , key : str ) :
meta = self . doc . metadata
key = key . split ( " . " )
for k in key [ : - 1 ] :
meta = meta [ k ]
del meta . content [ key [ - 1 ] ] # A hack because MetaMap doesn't have a __delitem__
# This is a custom element which creates \begingroup \endgroup groups in TeX
# and also causes KaTeX math blocks to be isolated in a similar way.
#
# Whenever a new context is created, its content should be eclosed in a group and vice-versa.
class Group ( Div ) :
def __init__ ( self , * args , metadata = { } , * * kwargs ) :
self . metadata = metadata
super ( ) . __init__ ( * args , * * kwargs )