scripts: Added <^> to punescape

Mainly for ^ for centering:

  "%{hi!%}^8s"        -> "  hi!   "

Note we can right bias with the nested modifier!

  "%{%{hi!%}^7s%}>8s" -> "   hi!  "

Also note this does _not_ support fill characters like python's format.
I'm not sure it's tractable with %-modifiers, python's format must get
up to some funky parsing to make this work ("%s<s"?).

I think this also fixed the behavior of left-aligning numbers? Seems we
weren't handling that before.
This commit is contained in:
Christopher Haster
2026-02-19 11:44:13 -06:00
parent d23edafcfe
commit d4a2ce4a4e
10 changed files with 90 additions and 50 deletions
+10 -6
View File
@@ -480,10 +480,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -499,10 +499,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
@@ -569,10 +573,10 @@ def psplit(s, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
s_ = []
i = start or 0
+8 -4
View File
@@ -342,10 +342,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -361,10 +361,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
+8 -4
View File
@@ -1718,10 +1718,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -1737,10 +1737,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
+10 -6
View File
@@ -4213,10 +4213,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -4232,10 +4232,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
@@ -4302,10 +4306,10 @@ def psplit(s, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
s_ = []
i = start or 0
+8 -4
View File
@@ -4102,10 +4102,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -4121,10 +4121,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
+10 -6
View File
@@ -488,10 +488,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -507,10 +507,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
@@ -577,10 +581,10 @@ def psplit(s, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
s_ = []
i = start or 0
+10 -6
View File
@@ -646,10 +646,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -665,10 +665,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
@@ -735,10 +739,10 @@ def psplit(s, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
s_ = []
i = start or 0
+8 -4
View File
@@ -530,10 +530,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -549,10 +549,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
+10 -6
View File
@@ -567,10 +567,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -586,10 +586,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
@@ -656,10 +660,10 @@ def psplit(s, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
s_ = []
i = start or 0
+8 -4
View File
@@ -428,10 +428,10 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'(?P<format>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{'
'|' '\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
'(?P<subformat>[<^>+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
@@ -447,10 +447,14 @@ def punescape(s, attrs=None, start=None, end=None, submatch=None):
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
f = f.replace('+', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
if '-' in f:
f = '<' + f.replace('-', '')
elif not any(d in f for d in '<^>'):
f = '>' + f
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)