Curated source subset assembled by klammertext-dev's doc/make_dist.sh: the Klammermachine (mac), the Standard Klammer Set (sks), the commands (com), editor plugins and install guides (doc), a test subset (tst), and lib/bin placeholders. Builds with 'make -C com'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
468 lines
15 KiB
Python
468 lines
15 KiB
Python
import pprint
|
|
import re
|
|
import textwrap
|
|
import kutil
|
|
import klammer_base
|
|
import itertools
|
|
import math
|
|
from html_util import E
|
|
|
|
class List(klammer_base.Klammer_base):
|
|
def __init__(self, K, list_type):
|
|
super().__init__(K)
|
|
self.list_type = list_type
|
|
self.items = kutil.rest_args(self.list_items)
|
|
if self.items[-1].strip() == "":
|
|
self.items = self.items[:-1]
|
|
word_count = max([len(e.strip().split()) for e in self.items])
|
|
self.compressed = False #and word_count < int(self.List_compressed_word_max)
|
|
|
|
def html(self):
|
|
body = ""
|
|
word_count = 0
|
|
for e in self.items:
|
|
body += f"<li>\n{kutil.format_for_paragraphs(e.strip())}\n</li>\n"
|
|
cls = "compressed" if self.compressed else ""
|
|
result = E(self.list_type).cls(cls).body(body.strip()).str().strip() + "\n"
|
|
return result
|
|
|
|
def tex(self):
|
|
body = "\n\n".join([f"\\item {e}" for e in self.items])
|
|
command = {'ol' : 'enumerate', 'ul' : 'itemize'}[self.list_type]
|
|
init = ""
|
|
if self.__dict__.get("initial") and self.initial != 1:
|
|
init = "\\addtocounter{enumi}{" + str(int(self.initial) - 1) + "}"
|
|
#topsep = '[noitemsep,topsep=0pt]'
|
|
topsep = "[noitemsep,topsep=0pt," if self.compressed else "[topsep=0pt,itemsep=2pt,"
|
|
topsep += "leftmargin=16pt]"
|
|
listsep = '\\setlist{nolistsep}' if self.cmp == 'true' else ''
|
|
result = f'{listsep}\n\\begin{{{command}}}{topsep}{init}\n{body}\n\\end{{{command}}}\n'
|
|
return result
|
|
|
|
def txt(self):
|
|
n = 1
|
|
result = ""
|
|
for item in self.items:
|
|
prefix = f"{n:3}. " if self.list_type == "ol" else " • " # U+2022
|
|
print("prefix", prefix)
|
|
formatted_item = "\n".join(
|
|
textwrap.wrap(item.strip(), width=80, subsequent_indent=" "*(len(prefix))))
|
|
result += f"{prefix}{formatted_item}\n"
|
|
if not self.compressed:
|
|
result += "\n"
|
|
n += 1
|
|
print(result)
|
|
return result
|
|
|
|
|
|
class Define(klammer_base.Klammer_base):
|
|
def __init__(self, K):
|
|
super().__init__(K)
|
|
self.items = kutil.rest_args(self.descriptions, 2)
|
|
#self.show()
|
|
#pprint.pprint(self.items)
|
|
|
|
def html(self):
|
|
def fontify(s):
|
|
return {"r" : s,
|
|
"i" : f"<i>{s}</i>",
|
|
"b" : f"<b>{s}</b>",
|
|
"t" : f'<span class="monospace">{s}</span>',
|
|
"c" : f'<span class="monospace">{s}</span>',
|
|
"s" : f'<span class="sansserif">{s}</span>'}[self.font]
|
|
result = "\n\n".join([f"<dt>{fontify(term)}</dt>\n<dd>{definition}</dd>"
|
|
for term,definition in self.items])
|
|
result = f"<dl>\n{result}\n</dl>"
|
|
return result
|
|
|
|
def tex(self):
|
|
def fontify(s):
|
|
font = {'r' : r'\normalfont',
|
|
'i' : r'\normalfont\itshape',
|
|
'b' : r'\normalfont\bfseries',
|
|
't' : r'\normalfont\ttfamily',
|
|
'c' : r'\normalfont\ttfamily',
|
|
's' : r'\normalfont\sffamily'}[self.font]
|
|
return f"{font} {s}"
|
|
result = "\n\n".join([f"\\item[{fontify(term)}]\\hfill\n\\newline {definition}"
|
|
for term,definition in self.items])
|
|
result = f"\\begin{{description}}[labelindent=12pt,nosep,itemsep=6pt]\n{result}\n\\end{{description}}"
|
|
return result
|
|
|
|
def txt(self):
|
|
return "TXT DEFINITIONS"
|
|
|
|
|
|
class Lines(klammer_base.Klammer_base):
|
|
def __init__(self, K):
|
|
super().__init__(K)
|
|
|
|
def html(self):
|
|
#return " <br>".join(self.s.strip().split("\n"))
|
|
#return f'<pre style="font-family: var(--serif)">{self.s}</pre>'
|
|
#return f'<pre>{self.s}</pre>'
|
|
result = self.s
|
|
result = re.sub("\n", "<br>\n", result)
|
|
result = re.sub(" ", "&^#160;", result)
|
|
return result
|
|
|
|
def tex(self):
|
|
return r"\\\newline\n".join(self.s.strip().split("\n"))
|
|
|
|
def txt(self):
|
|
return self.s.strip()
|
|
|
|
|
|
"""
|
|
def lines(K):
|
|
return {'html' : html_lines,
|
|
'tex' : tex_lines,
|
|
'pdf' : tex_lines}[K._target](K)
|
|
|
|
def tex_lines(K):
|
|
return " \\\\\n".join(K.s.strip().split("\n"))
|
|
|
|
def html_lines(K):
|
|
return " <br>".join(K.s.strip().split("\n"))
|
|
"""
|
|
|
|
|
|
|
|
|
|
"""
|
|
def definition_item(K):
|
|
if K._format == 'html':
|
|
return '<dt>{}</dt>\n<dd>{}</dd>'.format(
|
|
format_for_paragraphs(K.term),
|
|
format_for_paragraphs(K.desc))
|
|
else:
|
|
leading = '' if K.inlist else '\\vspace*{-16pt}'
|
|
return '\\item[{}]\\leavevmode{}\n\n{}'.format(
|
|
K.term, leading, K.desc)
|
|
"""
|
|
|
|
|
|
|
|
|
|
"""
|
|
def list(K, list_type):
|
|
items = kutil.rest_args(K.list_items)
|
|
return {'html' : html_list,
|
|
'tex' : tex_list,
|
|
'pdf' : tex_list}[K._target](K, list_type, items)
|
|
|
|
def html_list(K, list_type, items):
|
|
body = "\n" + "".join([E("li").body(e).str()+"\n" for e in items])
|
|
result = E(list_type).body(body).str().rstrip()
|
|
return result
|
|
|
|
def tex_list(K, list_type, items):
|
|
body = "\n\n".join([f"\\item {e}" for e in items])
|
|
command = {'ol' : 'enumerate', 'ul' : 'itemize'}[list_type]
|
|
topsep = '[topsep=0pt]'
|
|
listsep = '\\setlist{nolistsep}' if K.cmp != 'false' else ''
|
|
result = f'{listsep}\n\\begin{{{command}}}{topsep}\n{body}\n\\end{{{command}}}\n'
|
|
return result
|
|
|
|
def describe(K):
|
|
items = "\n\n".join(kutil.rest_args(K.descriptions))
|
|
return {'html' : html_describe,
|
|
'tex' : tex_describe,
|
|
'pdf' : tex_describe}[K._target](K, items)
|
|
|
|
def html_describe(K, items):
|
|
return E("dl").body(items).str()
|
|
|
|
def tex_describe(K, items):
|
|
return f'\\begin{{description}}\n{items}\n\\end{{description}}'
|
|
|
|
|
|
def entry(K):
|
|
return {'html' : html_entry,
|
|
'tex' : tex_entry,
|
|
'pdf' : tex_entry}[K._target](K)
|
|
|
|
def html_entry(K):
|
|
#return f'<dt>{K.item}</dt>\n<dd>{K.description}</dd>'
|
|
return E("dt").body(K.item).str(0) + E("dd").body(K.description).str(0)
|
|
|
|
def tex_entry(K):
|
|
return f'\\item[{K.item}]\n{K.description}\n'
|
|
|
|
|
|
def lines(K):
|
|
return {'html' : html_lines,
|
|
'tex' : tex_lines,
|
|
'pdf' : tex_lines}[K._target](K)
|
|
|
|
def tex_lines(K):
|
|
return " \\\\\n".join(K.s.strip().split("\n"))
|
|
|
|
def html_lines(K):
|
|
return " <br>".join(K.s.strip().split("\n"))
|
|
|
|
"""
|
|
|
|
|
|
r"""
|
|
# List items
|
|
|
|
paragraph_separator_re = re.compile('\n *\n', re.S)
|
|
|
|
def format_for_paragraphs(s):
|
|
result = s
|
|
if len(paragraph_separator_re.findall(result)) > 0:
|
|
result = '\n\n{}\n\n'.format(result)
|
|
return result
|
|
|
|
def li(K):
|
|
if K._format == 'html':
|
|
result = K.s.strip()
|
|
result = format_for_paragraphs(result)
|
|
result = '<li>{}</li>'.format(result)
|
|
else:
|
|
#result = '\\item{{\\Kinlisttrue {}}} '.format(K.s.strip())
|
|
#result = '\\item\\begin\\Kinlisttrue {}\\end '.format(K.s.strip())
|
|
#result = '\\item\\parbox{{\\linewidth}}{{\\Kinlisttrue {}}} '.format(K.s.strip())
|
|
#result = '\\item\\Kinlisttrue {} '.format(K.s.strip())
|
|
result = '\\item {} '.format(K.s.strip())
|
|
|
|
if K.keep:
|
|
result = '\\parbox{{\\linewidth}}{{{}}}'.format(result)
|
|
return result
|
|
|
|
def mli(K):
|
|
result = '@0item {} | @0ulc\n'.format(K.args[0])
|
|
result += "\n".join(['@0li {} li@'.format(e) for e in K.args[1:]]) + '\n'
|
|
result += ' ulc@ item@\n'
|
|
return result
|
|
|
|
|
|
# Ordered and unordered lists:
|
|
|
|
def list_html(K, list_type):
|
|
result = ''
|
|
if K.list_items[0][0:4] != '<li>':
|
|
result = "\n".join(['<li>{}</li>'.format(e) for e in K.list_items])
|
|
else:
|
|
result = K.list_items[0]
|
|
tag = 'ol'
|
|
if list_type == 'ul':
|
|
tag = 'ul style="list-style-type:{};"'.format(K.bullet)
|
|
cls = 'compressed' if K.cmp else ''
|
|
if list_type == 'ul' and K.bullet == 'none':
|
|
cls += ' nobullet'
|
|
cls = ' class="{}"'.format(cls.strip())
|
|
|
|
result = '<{}{}>\n{}\n</{}>\n'.format(tag, cls, result, list_type)
|
|
return result
|
|
|
|
def list_latex(K, list_type):
|
|
result = ''
|
|
first_word = K.list_items[0][:5]
|
|
if first_word not in {'\\item', '\\parb'}:
|
|
result = " " + "\n".join(['@0li {} li@'.format(e.strip()) for e in K.list_items])
|
|
else:
|
|
result = K.list_items[0]
|
|
#options = 'nosep,itemsep=1pt,topsep=0pt,partopsep=8pt,parsep=4pt'
|
|
options = 'nosep,itemsep={}pt'.format(2 if K.cmp else 6)
|
|
if K.cmp:
|
|
itemsep=',nosep'
|
|
else:
|
|
itemsep=''
|
|
|
|
'''
|
|
if list_type == 'ul' and K.bullet == 'none' and K.indent:
|
|
left_margin = ',leftmargin=18pt'
|
|
else:
|
|
left_margin = ''
|
|
'''
|
|
|
|
left_margin = ''
|
|
if K.in_define:
|
|
if list_type == 'ul':
|
|
left_margin = ',leftmargin=4pt'
|
|
else:
|
|
left_margin = ',leftmargin=16pt'
|
|
|
|
if K.parsep:
|
|
parsep = K.parsep
|
|
else:
|
|
parsep = 2 if K.cmp else 6
|
|
|
|
#options = '[nosep,parsep={}pt{},{}]'.format(parsep, left_margin,itemsep)
|
|
options = '[parsep={}{}{}]'.format(parsep, left_margin, itemsep)
|
|
|
|
'''
|
|
if K.cmp:
|
|
options = '[nosep]'
|
|
else:
|
|
options = ''
|
|
'''
|
|
|
|
#options = '[itemsep={}]'.format(K.itemsep) if K.itemsep else ''
|
|
|
|
#options = '[topsep=0pt,parsep={}pt]'.format(2 if K.cmp else 6)
|
|
#options = '\\setlength{{\\topsep}{0pt}}\n\\setlength{{\parsep}}{{{}}}\n'.format(
|
|
bullet = ''
|
|
if list_type == 'ul' and K.bullet == 'none':
|
|
bullet = '\\renewcommand\\labelitemi{\\hspace*{-8pt}}'
|
|
itemsep = '\n\\setlength{{\\itemsep}}{{{}}}\n'.format(K.itemsep) if K.itemsep else ''
|
|
#result = '''\\listvmargin%
|
|
result = '''\\begin{{{0}}}{1}{2}
|
|
{3}
|
|
{4}
|
|
\\end{{{0}}}
|
|
'''.format('itemize' if list_type == 'ul' else 'enumerate',
|
|
options, itemsep, bullet, result.strip())
|
|
result = re.compile('\n\n', re.S).sub('\n', result)
|
|
result = re.compile(r' +\\begin', re.S).sub(r'\\begin', result)
|
|
if K.uncover:
|
|
N = 1
|
|
def replace(match):
|
|
nonlocal N
|
|
result = '{}<{}-> '.format(match.group(1), N)
|
|
N += 1
|
|
return result
|
|
result = re.compile(r'(\\item(\[|\s))').sub(replace, result)
|
|
return result
|
|
|
|
#\\listvmargin
|
|
|
|
def ul(K):
|
|
if K._format == 'html':
|
|
return list_html(K, 'ul')
|
|
elif K._format == 'latex':
|
|
return list_latex(K, 'ul')
|
|
else:
|
|
util.format_not_defined(K._format, 'ul')
|
|
|
|
def ol(K):
|
|
if K._format == 'html':
|
|
return list_html(K, 'ol')
|
|
elif K._format == 'latex':
|
|
return list_latex(K, 'ol')
|
|
else:
|
|
util.format_not_defined(K._format, 'ol')
|
|
|
|
# Definitions
|
|
|
|
def definition_item(K):
|
|
if K._format == 'html':
|
|
return '<dt>{}</dt>\n<dd>{}</dd>'.format(
|
|
format_for_paragraphs(K.term),
|
|
format_for_paragraphs(K.desc))
|
|
else:
|
|
leading = '' if K.inlist else '\\vspace*{-16pt}'
|
|
return '\\item[{}]\\leavevmode{}\n\n{}'.format(
|
|
K.term, leading, K.desc)
|
|
|
|
def dfont(K):
|
|
if K._format == 'latex':
|
|
result = {'r' : '\\normalfont',
|
|
'i' : '\\normalfont\\itshape',
|
|
'b' : '\\normalfont\\bfseries',
|
|
't' : '\\normalfont\\ttfamily',
|
|
's' : '\\normalfont\\sffamily\\large'
|
|
}[K.font]
|
|
elif K._format == 'html':
|
|
s = K.term
|
|
result = {'r' : s,
|
|
'i' : '<i>{}</i>'.format(s),
|
|
'b' : '<b>{}</b>'.format(s),
|
|
't' : '<span class="monospace">{}</span>'.format(s),
|
|
's' : '<span class="sansserif">{}</span>'.format(s)
|
|
}[K.font]
|
|
else:
|
|
error.Klammertext_error(
|
|
'There is not defined format named "{}".'.format(K._format))
|
|
return result
|
|
|
|
def escape_code_term(s):
|
|
def replace(match):
|
|
left, term, right = match.groups()
|
|
term = re.sub('\[', r'$\\lbrack$\\,', term)
|
|
term = re.sub('\]', r'\\,$\\rbrack$', term)
|
|
#term = re.sub('\[', '\\[', term)
|
|
#term = re.sub('\]', '\\]', term)
|
|
term = re.sub('--', '-{}-', term)
|
|
result = '{}{}{}'.format(left, term, right)
|
|
return result
|
|
pat = re.compile(r'(\\item\[)(.*?)(\]\\)', re.S)
|
|
return pat.sub(replace, s)
|
|
|
|
def define(K):
|
|
if K._format == 'latex':
|
|
#itemsep=1pt,topsep=2pt,partopsep=8pt,parsep=4pt,%
|
|
options = 'nosep,parsep=4pt,leftmargin=32pt'
|
|
fontname = {'r' : '\\normalfont',
|
|
'i' : '\\normalfont\\itshape',
|
|
'b' : '\\normalfont\\bfseries',
|
|
't' : '\\normalfont\\ttfamily',
|
|
's' : '\\normalfont\\sffamily',
|
|
'si' : '\\normalfont\\sffamily\\itshape',
|
|
'sb' : '\\normalfont\\sffamily\\bfseries',
|
|
'c' : '\\normalfont',
|
|
}[K.font]
|
|
term = K.s
|
|
if K.font == 'c':
|
|
term = escape_code_term(term)
|
|
#term = re.compile('item\[(.*?)\]', re.S).sub(r'item[\\verb+\1+]', term)
|
|
#term = re.compile('
|
|
return '''
|
|
\\listvmargin \\begin{{description}}[{},
|
|
labelindent=16pt,%
|
|
style=nextline,font={}]
|
|
%\\raggedright
|
|
{}
|
|
\\end{{description}} \\listvmargin\n'''.format(options,fontname, term)
|
|
|
|
elif K._format == 'html':
|
|
term_class = {'r' : 'normal',
|
|
'i' : 'italic',
|
|
'b' : 'bold',
|
|
't' : 'monospace',
|
|
's' : 'sansserifbody',
|
|
'si' : 'sansserifitalic',
|
|
'sb' : 'sansserifbold',
|
|
'c' : 'normal',
|
|
}[K.font]
|
|
term = K.s
|
|
if K.font == 'c':
|
|
#term = re.sub('–', '‐'*2, term)
|
|
term = re.sub('–', '- __UNSPACE__ -', term)
|
|
body = re.sub('<dt>', '<dt class="{}">'.format(term_class), term)
|
|
return '''
|
|
<dl>
|
|
{}
|
|
</dl>'''.format(body)
|
|
"""
|
|
|
|
|
|
def make_column_lists(lst, count):
|
|
cols = math.ceil(len(lst) / count)
|
|
result = []
|
|
for i in range(count):
|
|
#print(i * cols, (i + 1) * cols)
|
|
result.append(lst[i * cols : (i+1) * cols])
|
|
return list(itertools.zip_longest(*result, fillvalue=""))
|
|
|
|
def xmake_column_lists(lst, column_count):
|
|
count = len(lst)
|
|
row_count = math.ceil(count / column_count)
|
|
print(count, column_count, row_count)
|
|
i = 0
|
|
while i < count:
|
|
i += 1
|
|
|
|
def columns(K):
|
|
rows = make_column_lists(K.items.split(), K.n)
|
|
result = "@table\n" # :lines false :header_line false :leading 1.1 :vmargin false |\n"
|
|
for row in rows:
|
|
#print("row:", row)
|
|
result += " | ".join(row) + " ||\n"
|
|
result += "table@\n"
|
|
if K.K_target == "latex":
|
|
result = f"\\vspace*{{-12pt}}\n{result}"
|
|
return result
|