Initial commit: Klammertext source distribution
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>
This commit is contained in:
126
sks/code/code.k
Normal file
126
sks/code/code.k
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
@@code.k :filename :pattern :caption :number | text.literal :
|
||||
A source file displayed verbatim
|
||||
@@
|
||||
|
||||
@@code :: @eval code_format.Code(K) @ @@
|
||||
|
||||
@@c.k code_text :
|
||||
A word or phrase displayed verbatim in a line
|
||||
@@
|
||||
|
||||
@@c :: @eval code_format.Code_fragment(K) eval@
|
||||
@@
|
||||
|
||||
@@source_file filename : @eval code_format.Source(K) @ @@
|
||||
|
||||
|
||||
#[
|
||||
@@code.k text :file :number.bool true :caption : Code listing with formatted comments @@
|
||||
|
||||
@@code :
|
||||
@eval code_format.Code(K) eval@
|
||||
@@
|
||||
|
||||
|
||||
@@lst spec.figure_id :
|
||||
@reference *spec* | Listing @
|
||||
@@
|
||||
|
||||
|
||||
|
||||
c <text>
|
||||
code <text> :caption :filename :pattern
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
@@codebox.k s :color 1,1,1 :size normalsize :escapechar ^^
|
||||
:space_break_only.bool false :linenumber.bool false :scale 1.0
|
||||
:indent :standalone :vcenter :
|
||||
Verbatim text for source code preserving whitepace, surrounded by a box
|
||||
that extends to the margins @@
|
||||
|
||||
@@codebox.html ::
|
||||
@code :text *s* @
|
||||
@@
|
||||
|
||||
@@codebox.tex ::
|
||||
\definecolor{codeboxbgcolor}{rgb}{*color*}
|
||||
\setlength{\codeboxlinelength}{\linewidth - 6pt}
|
||||
\vspace*{4pt}
|
||||
\begin{lstlisting}%
|
||||
[frame=single,
|
||||
framerule=1pt,
|
||||
basicstyle=\*size*\ttfamily,
|
||||
lineskip=0pt,
|
||||
linewidth=*scale*\codeboxlinelength,
|
||||
columns=fullflexible,
|
||||
keepspaces=true,
|
||||
framesep=6pt,
|
||||
xleftmargin=6pt,
|
||||
escapechar=*escapechar*,
|
||||
breaklines=true,
|
||||
prebreak=\hbox{\large$\mapsto$},
|
||||
%postbreak={\textbf{\hbox{$\rightarrow$}}},
|
||||
rulecolor=\color{codeboxcolor},
|
||||
backgroundcolor=\color{codeboxbgcolor},
|
||||
breakatwhitespace=*space_break_only*,
|
||||
numbers=none, # #- @if *linenumber* | left | none @ #- ,
|
||||
numbersep=12pt,
|
||||
numberstyle=\small\color{Darkred}]
|
||||
*s*
|
||||
\end{lstlisting}
|
||||
@@
|
||||
|
||||
@@codebox.txt ::
|
||||
@code :text *s* @
|
||||
@@
|
||||
|
||||
@@pathname.k s :small.bool false : Pathname @@
|
||||
@@pathname :: @eval code_format.Pathname(K) eval@ @@
|
||||
|
||||
@@annotate.k text :caption :
|
||||
Comments put in boxes to the right of the code
|
||||
@@
|
||||
|
||||
# @@annotate : @eval code_format.Annotate(K) eval@ @@
|
||||
# @@annotate : @codebox *text* @ @@
|
||||
@@annotate :: @code :text *text @ @@
|
||||
|
||||
@@listing s : @code :text *s* @ @@
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
@@sv.k s : Italic font for variable in @t syntax @ argument @@
|
||||
@@sv.tex :: ^^textrm"^^textit"*s*$$ @@
|
||||
@@sv.html :: @ri *s* @ @@
|
||||
|
||||
@@svs.k s : Sans-serif font for variable in @t syntax @ argument @@
|
||||
@@svs.tex :: "^^small^^textsf"^^textit"*s*$$$ @@
|
||||
@@svs.html :: @s @i *s* @ @ @@
|
||||
|
||||
@@svsub.k base | sub : Italic font for subscripted variable in @t syntax @ argument @@
|
||||
@@svsub.tex :: ^^textrm"^^textit"*base*$$^^textsubscript"*sub*$ @@
|
||||
@@svsub.html :: <span class="ritalic">*base*</span><sub>*sub*</sub> @@
|
||||
|
||||
@@syntax.k s :fontsize normalsize :indent.bool true :
|
||||
Verbatim text that includes italic font for syntax descriptions @@
|
||||
|
||||
@@syntax.html ::
|
||||
@code :text *s* @ # :indent *indent* @
|
||||
@@
|
||||
|
||||
@@syntax.tex ::
|
||||
\vspace*{8pt}\begin{LVerbatim}[xleftmargin=0pt, # @if true | 0pt | -24pt @ ,
|
||||
baselinestretch=1.05, fontsize=\*fontsize*, frame=single, framesep=8pt,
|
||||
commandchars=\\̈\$, fontfamily = @verbatimfont@, framesep=12pt]
|
||||
*s*
|
||||
\end{LVerbatim}
|
||||
@@
|
||||
|
||||
@@svspace.tex length :
|
||||
^^vspace*"*length*$
|
||||
@@
|
||||
|
||||
@@svspace.html length :
|
||||
@@
|
||||
]#
|
||||
318
sks/code/code_format.py
Normal file
318
sks/code/code_format.py
Normal file
@@ -0,0 +1,318 @@
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
sys.path.append("../kutil")
|
||||
sys.path.append("../target")
|
||||
|
||||
import re
|
||||
import klammer_base
|
||||
import kutil
|
||||
import html_util
|
||||
from html_util import E
|
||||
import latex_util as L
|
||||
import pprint
|
||||
import phases
|
||||
|
||||
def escape_newlines(s):
|
||||
return re.sub("\n", " ___NL___ ", s)
|
||||
|
||||
# def literal_newline(s):
|
||||
# def replace(match):
|
||||
# before, after = match.groups()
|
||||
# return f"{before}\\n{after}"
|
||||
# backslash_pat = re.compile(r'(".*?)\n(.*?")', re.S)
|
||||
# return backslash_pat.sub(replace, s)
|
||||
|
||||
def get_blocks(s):
|
||||
comment_pat = re.compile(r"(\s*)//(\d+)\s+(.*)", re.S)
|
||||
blocks = []
|
||||
lines = s.strip("\n").split("\n")
|
||||
i = 0
|
||||
uncommented = ""
|
||||
while i < len(lines):
|
||||
match = comment_pat.match(lines[i])
|
||||
if match:
|
||||
if uncommented:
|
||||
blocks.append([uncommented.rstrip(), None])
|
||||
uncommented = ""
|
||||
count = int(match.group(2))
|
||||
comment = match.group(3)
|
||||
code = ""
|
||||
j = 0
|
||||
i += 1
|
||||
while j < count:
|
||||
line = re.sub("\n", "\\n", lines[i])
|
||||
code += line + "\n"
|
||||
j += 1
|
||||
i += 1
|
||||
blocks.append([code.strip("\n"), comment])
|
||||
else:
|
||||
uncommented += lines[i] + "\n"
|
||||
i += 1
|
||||
if uncommented:
|
||||
blocks.append([uncommented.rstrip(), None])
|
||||
return blocks
|
||||
|
||||
def latex_spaces(s):
|
||||
def replace(match):
|
||||
s = match.group(0)
|
||||
if False and len(s) == 1:
|
||||
return "~"
|
||||
else:
|
||||
result = "~" * len(s)
|
||||
result = f"\\hphantom{{{result}}}"
|
||||
return result
|
||||
space_pat = re.compile(" +", re.S)
|
||||
return space_pat.sub(replace, s)
|
||||
|
||||
def latex_unquote(s):
|
||||
quoted = "asciicircum quotesingle asciigrave asciitilde asciitilde backslash".split()
|
||||
quoted = [f"{{}}\text{e}{{}}" for e in quoted]
|
||||
result = s
|
||||
for q in quoted:
|
||||
result = re.sub(q, "X", result)
|
||||
result = re.sub(" ", "Y", result)
|
||||
return result
|
||||
|
||||
def longest_line(s):
|
||||
result = ""
|
||||
for line in latex_unquote(s).split("\n"):
|
||||
if len(line) > len(result):
|
||||
result = line
|
||||
return result
|
||||
|
||||
def literal_newline(s):
|
||||
def replace(match):
|
||||
before, after = match.groups()
|
||||
return f"{before}\\n{after}"
|
||||
backslash_pat = re.compile(r'(".*?)\n(.*?")', re.S)
|
||||
return backslash_pat.sub(replace, s)
|
||||
|
||||
class Code(klammer_base.Klammer_base):
|
||||
id = 0
|
||||
def __init__(self, K):
|
||||
super().__init__(K)
|
||||
self.text = phases.expand_whitespace_markers(self.text)
|
||||
|
||||
def html(self):
|
||||
if self.K_target == "html":
|
||||
self.text = literal_newline(self.text)
|
||||
# Escape Klammertext special characters so they survive
|
||||
# re-insertion into the katom stream after @eval
|
||||
self.text = self.text.replace("^", "^^")
|
||||
self.text = self.text.replace("#", "^#")
|
||||
self.text = self.text.replace("@", "^@")
|
||||
self.text = self.text.replace("|", "^|")
|
||||
self.blocks = get_blocks(self.text)
|
||||
result = ''
|
||||
for text, comment in self.blocks:
|
||||
border = "code_border" if comment else "code_no_border"
|
||||
body = E("div").body(text).cls(f"code_text {border}").str(None)
|
||||
if comment:
|
||||
body += "\n" + E("div").body(comment).cls("code_comment").str()
|
||||
result += E("div").body(body).cls("code_block").str()
|
||||
if self.number or self.caption:
|
||||
#result = html_util.add_caption(
|
||||
# result, "Listing", self.number, self.caption, "i", "left", "top")
|
||||
caption = kutil.caption_marker("Listing", self.caption)
|
||||
result = f'<div class="plain_caption code_caption">{caption}</div>{result}\n'
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def escape_latex(s):
|
||||
"""Escape LaTeX special characters in code text."""
|
||||
# Backslash must be first (before adding more backslashes)
|
||||
s = s.replace("\\", "\\textbackslash{}")
|
||||
s = s.replace("{", "\\{")
|
||||
s = s.replace("}", "\\}")
|
||||
s = s.replace("%", "\\%")
|
||||
s = s.replace("$", "\\$")
|
||||
s = s.replace("&", "\\&")
|
||||
s = s.replace("_", "\\_")
|
||||
s = s.replace("^", "\\textasciicircum{}")
|
||||
s = s.replace("~", "\\textasciitilde{}")
|
||||
s = s.replace("<", "\\textless{}")
|
||||
s = s.replace(">", "\\textgreater{}")
|
||||
return s
|
||||
|
||||
def tex(self):
|
||||
# Escape Klammertext special characters
|
||||
self.text = self.text.replace("^", "^^")
|
||||
self.text = self.text.replace("#", "^#")
|
||||
self.text = self.text.replace("@", "^@")
|
||||
self.text = self.text.replace("|", "^|")
|
||||
# Escape LaTeX special characters in code text
|
||||
self.text = Code.escape_latex(self.text)
|
||||
self.blocks = get_blocks(self.text)
|
||||
strutvis = "0pt"
|
||||
start_strut = f"\\rule[0pt]{{{strutvis}}}{{12pt}}"
|
||||
end_strut = f"\\rule[-6pt]{{{strutvis}}}{{12pt}}"
|
||||
caption_strut = f"\\rule[-8pt]{{{strutvis}}}{{6pt}}"
|
||||
indent = "8pt"
|
||||
comment_sep = "10pt"
|
||||
i = 0
|
||||
result = ""
|
||||
count = len(self.blocks)
|
||||
for text, comment in self.blocks:
|
||||
text = " " + re.sub("\n", " \n ", text) + " "
|
||||
longest = longest_line(text)
|
||||
text = latex_spaces(text)
|
||||
text = re.sub("\n", r"\\\\", text)
|
||||
text = f"{start_strut}\\ttfamily {text}{end_strut}"
|
||||
width = f"\\widthof{{\\ttfamily {longest}}}"
|
||||
code = L.environment("minipage", text, width) + "\\\\\n"
|
||||
if comment:
|
||||
width = f"\\linewidth - {width} - {indent} - {comment_sep}"
|
||||
code = f"\\fcolorbox{{Gray}}{{LightGray}}{{{code}}}"
|
||||
code += f"\\rule{{{comment_sep}}}{{{strutvis}}}" \
|
||||
+ L.environment("minipage", "\\sffamily\\small\\raggedright " + comment, width)
|
||||
result += f"\\rule{{{indent}}}{{{strutvis}}}{code}"
|
||||
if comment:
|
||||
if i < count - 1 and self.blocks[i+1][1]:
|
||||
result += "\\\\[4pt]"
|
||||
i += 1
|
||||
if not self.blocks[count-1][1]:
|
||||
result = result[:-4]
|
||||
if self.number or self.caption:
|
||||
caption = kutil.caption_marker("Listing", self.caption)
|
||||
if self.blocks[0][1]:
|
||||
caption += caption_strut
|
||||
strut = f"\\rule{{{indent}}}{{{strutvis}}}"
|
||||
result = f"{strut}\\emph{{\\it {caption}}}\\newline\n" + result + "\n"
|
||||
result = f"\\hypertarget{{Reference-Listing-{Code.id}}}{{}}\n{result}"
|
||||
Code.id += 1
|
||||
return result
|
||||
|
||||
def undash(s):
|
||||
result = s
|
||||
result = re.sub("__MDASH__", "---", result)
|
||||
result = re.sub("__NDASH__", "--", result)
|
||||
return result
|
||||
|
||||
class Code_fragment(klammer_base.Klammer_base):
|
||||
def __init__(self, K):
|
||||
super().__init__(K)
|
||||
|
||||
def html(self):
|
||||
#print(f"code: |{self.code_text}|")
|
||||
result = self.code_text.strip()
|
||||
result = undash(result)
|
||||
#result = re.escape(result)
|
||||
result = re.sub("<", "<", result)
|
||||
result = re.sub(" ", " ", result)
|
||||
#print(f"code: |{self.code_text}| -> |{result}|")
|
||||
return f'<span class="code">{result}</span>'
|
||||
|
||||
def tex(self):
|
||||
return f"{{\\tt {self.code_text.strip()}}}"
|
||||
|
||||
def show(s):
|
||||
print("-"*80)
|
||||
print(s)
|
||||
print("-"*80)
|
||||
|
||||
class Source(klammer_base.Klammer_base):
|
||||
def __init__(self, K):
|
||||
super().__init__(K)
|
||||
with open(self.filename) as fp:
|
||||
self.src = fp.read()
|
||||
|
||||
def tex(self):
|
||||
result = self.src
|
||||
# result = re.sub("#", "^#", result)
|
||||
# result = re.sub("\\^", "\\^", result)
|
||||
result = f"\\begin{{verbatim}}\n{result}\n\\end{{verbatim}}\n"
|
||||
return result
|
||||
|
||||
def html(self):
|
||||
result = escape_newlines(self.src.strip()) + "\n"
|
||||
result = re.sub("@", "^@", result)
|
||||
result = E("div").body(result).cls("code_text").str()
|
||||
return result
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
if __name__ == "__main__":
|
||||
s = """
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
//1 One line commented
|
||||
int count = 12;
|
||||
//3 Two lines commented
|
||||
for (int i = 0; i < count; i++) {
|
||||
std::cout << "Counter: " << i << "\n";
|
||||
}
|
||||
//1 A really long comment for one line. A really long comment for one line. A really long comment for one line.
|
||||
std::cout << "End\n";
|
||||
}
|
||||
"""
|
||||
get_blocks(s);
|
||||
|
||||
|
||||
# class Code(klammer_base.Klammer_base):
|
||||
# def __init__(self, K):
|
||||
# super().__init__(K)
|
||||
# #self.show("Code")
|
||||
# if self.filename and self.text:
|
||||
# raise Exception("Both :text and :filename cannot be defined")
|
||||
# if K.filename:
|
||||
# with open(K.filename) as fp:
|
||||
# self.src = fp.read()
|
||||
# if K.pattern:
|
||||
# rgx = re.compile(f".*?({K.pattern}).*", re.S)
|
||||
# match = rgx.match(self.src)
|
||||
# if match is None:
|
||||
# raise Exception(f"Match fails for @source_code: {K.pattern}")
|
||||
# self.src = match.group(1)
|
||||
# self.src = kutil.protect_klammertext_special_characters(self.src)
|
||||
# else:
|
||||
# self.src = self.text
|
||||
|
||||
# def html(self):
|
||||
# #src = re.sub("\n", "<!-- -->", self.src) ?
|
||||
# #result = f'<pre class="code">\n{self.src}\n</pre>\n'
|
||||
# result = self.src
|
||||
# result = undash(result)
|
||||
# result = f'<pre>\n{result}\n</pre>\n'
|
||||
# return result
|
||||
|
||||
# def tex(self):
|
||||
# src = self.src
|
||||
# src = re.sub(r"\\{", "{", src)
|
||||
# src = re.sub(r"\\}", "}", src)
|
||||
# result = f"\\begin{{lstlisting}}\n{src}\n\\end{{lstlisting}}\n"
|
||||
# return result
|
||||
|
||||
# def txt(self):
|
||||
# return "x~ " + self.src
|
||||
|
||||
# class Pathname(klammer_base.Klammer_base):
|
||||
# def __init__(self, K):
|
||||
# super().__init__(K)
|
||||
|
||||
# def html(self):
|
||||
# return f'<span class="monospace">{self.s}</span>'
|
||||
|
||||
# def tex(self):
|
||||
# result = self.s
|
||||
# def replace(match):
|
||||
# return '\\{}'.format(match.group(1))
|
||||
# result = re.sub(r'\\', 'XXXBACKSLASHXXX', result)
|
||||
# result = re.compile('\s*__UNSPACE__\s*', re.S).sub('', result)
|
||||
# result = re.compile(r'([&${}%#_])').sub(replace, result)
|
||||
# result = re.sub('\^', r'\\^{}', result)
|
||||
# result = re.sub('~', r'\\~{}', result)
|
||||
# result = re.sub(r'XXXBACKSLASHXXX', r'{\\textbackslash}', result)
|
||||
# result = re.sub('\n', r'~\\\\\n', result.strip())
|
||||
# result = re.sub(' ', '$~$', result)
|
||||
# result = re.sub("'", r"{\\textquotesingle}", result)
|
||||
# result = re.sub('"', r'{\\textquotedbl}', result)
|
||||
# result = re.sub('--', '{-}{-}', result)
|
||||
# result = r'{{\normalfont\texttt{{{}}}}}'.format(result.strip())
|
||||
# result = re.sub(r'\{\\textbackslash\}\\#', '\\#', result)
|
||||
# if self.small:
|
||||
# result = '{{\\footnotesize{}}}'.format(result)
|
||||
# return result
|
||||
|
||||
# def txt(self):
|
||||
# return f"'{self.s}'"
|
||||
|
||||
57
sks/code/css/code.css
Normal file
57
sks/code/css/code.css
Normal file
@@ -0,0 +1,57 @@
|
||||
.code {
|
||||
font-family: var(--monospace);
|
||||
}
|
||||
|
||||
.code_block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: .125rem 0 0 1rem;
|
||||
padding: 0;
|
||||
/* flex-direction: column-reverse; */
|
||||
}
|
||||
|
||||
.code_caption {
|
||||
margin-left: 1rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.code_text {
|
||||
display: inline-block;
|
||||
/* vertical-align: top; */
|
||||
white-space: pre;
|
||||
font-family: var(--monospace);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.code_comment {
|
||||
display: inline-block;
|
||||
/* vertical-align: top; */
|
||||
padding: .25rem;
|
||||
border: solid white 1px;
|
||||
padding: .25rem .25rem .25rem .5rem;
|
||||
font-family: var(--sans-serif);
|
||||
font-style: italic;
|
||||
font-size: .8rem;
|
||||
min-width: 100px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.code_border {
|
||||
border: solid gray 1px;
|
||||
/* margin: .125rem; */
|
||||
margin: .125rem .125rem .125rem .25rem;
|
||||
padding: .125rem .5rem .25rem .5rem;
|
||||
background-color: rgb(95%,95%,95%);
|
||||
|
||||
}
|
||||
|
||||
.code_no_border {
|
||||
padding: .125rem 0 .125rem .5rem;
|
||||
margin: 0 0 0 .125rem;
|
||||
border: solid white 1px;
|
||||
/* Debugging:
|
||||
border: solid lightgray 1px;
|
||||
background-color: rgb(250,250,127);
|
||||
*/
|
||||
}
|
||||
|
||||
1
sks/code/css/list.txt
Normal file
1
sks/code/css/list.txt
Normal file
@@ -0,0 +1 @@
|
||||
code.css
|
||||
20
sks/code/js/code.js
Normal file
20
sks/code/js/code.js
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
function adjust_code_comment_width()
|
||||
{
|
||||
let max_width = K.get("#text").offsetWidth;
|
||||
K.getv(".code_comment").forEach(function (comment_box) {
|
||||
let block = comment_box.parentNode;
|
||||
let code_box = block.children[0];
|
||||
let comment_width = max_width - code_box.offsetWidth;
|
||||
K.width(comment_box, comment_width);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("load", function (event) {
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
function (event) {
|
||||
adjust_code_comment_width();
|
||||
});
|
||||
adjust_code_comment_width();
|
||||
});
|
||||
9
sks/code/sty/code.sty
Normal file
9
sks/code/sty/code.sty
Normal file
@@ -0,0 +1,9 @@
|
||||
\usepackage{etoolbox}
|
||||
\usepackage{fancyvrb}
|
||||
|
||||
\usepackage{listings}
|
||||
\lstset{basicstyle=\ttfamily,fontadjust=true,basewidth=0.5em,xleftmargin=26pt}
|
||||
|
||||
\usepackage[strings,nohyphen]{underscore}
|
||||
|
||||
\usepackage{mdframed}
|
||||
Reference in New Issue
Block a user