[feature] inject width = 2 for nerdfont code point

but still buggy... fish and vim won't work properly sometime
This commit is contained in:
Klesh Wong 2020-11-30 18:21:28 +08:00
parent 312e049862
commit a6aafd2805

View File

@ -5,6 +5,8 @@ import argparse
import re import re
import sys import sys
import io import io
import os
from abc import ABC, abstractclassmethod, abstractmethod, abstractstaticmethod
from typing import List, TextIO from typing import List, TextIO
@ -27,92 +29,122 @@ def extract_nerdfont_ranges(font_patcher: TextIO):
yield r[0], r[1], m[1] yield r[0], r[1], m[1]
def inject_nerdfont_ranges(ranges: List[tuple], textin: TextIO, textout: TextIO): class Scope(ABC):
scope = None comment_override = None
comment_char = None
escape_char = None
length = len(ranges)
keyword_re = re.compile(r'<(\w+)>\s+(\S+)\s*')
def charmap_line(start: int, end: int, comment: str): def __init__(self, ranges: List[tuple], output: TextIO):
if end < start: self.ranges = ranges
return self.length = len(ranges)
textout.write("<U%04X>" % start) self.output = output
if end > start: self.cursor = 0
textout.write("..<U%04X>" % end) self.escape_char = None
textout.write(" ") self.re = re.compile(self.pattern())
escaped = "".join(map(lambda x: "%sx%02x" % (escape_char, x), chr(start).encode('utf-8')))
textout.write(escaped)
textout.write(" ")
textout.write(comment)
textout.write("\n")
charmap = { @property
're': re.compile(r'<U([A-Z0-9]+)>(..<U([A-Z0-9]+)>)?\s+(\S+)\s+(.*?)$'), def empty(self) -> bool:
'cursor': 0, return self.cursor >= self.length
'writeline': charmap_line
}
def width_line(start: int, end: int, comment: str): @property
if end < start: def current(self) -> tuple:
return return self.ranges[self.cursor]
textout.write("<U%04X>" % start)
if end > start:
textout.write("...<U%04X>" % end)
textout.write("\t%s\n" % comment)
width = { def pop(self) -> bool:
're': re.compile(r'<U([A-Z0-9]+)>(...<U([A-Z0-9]+)>)?(\s+)(\d+)$'), if self.empty:
'cursor': 0, return False
'writeline': width_line, ss, se, comment = self.current
'comment': '2', self.write_line(ss, se, self.comment_override or comment)
} self.cursor += 1
def pop_scope(): def read_line(self, line: str) -> bool:
ss, se, comment = ranges[scope['cursor']] m = self.re.match(line)
scope['writeline'](ss, se, scope.get('comment', comment))
scope['cursor'] += 1
return scope['cursor'] < length
for line in textin:
if textout.closed:
return
line = line.strip()
if line.startswith("CHARMAP"):
scope = charmap
elif line.startswith("WIDTH"):
scope = width
elif line.startswith("END "):
while scope['cursor'] < length:
pop_scope()
scope = None
elif comment_char and line.startswith(comment_char):
pass
elif scope and scope['cursor'] < length:
m = scope['re'].match(line)
if not m: if not m:
raise Exception("unkown line: " + line) raise Exception("unkown line: " + line)
ts = int(m[1], base=16) ts = int(m[1], base=16)
te = int(m[3], base=16) if m[3] else ts te = int(m[3], base=16) if m[3] else ts
ss, se, comment = ranges[scope['cursor']] while not self.empty and self.current[1] < ts:
# if ts == 0x5e00: self.pop()
# import ipdb; ipdb.set_trace() if self.empty:
if te < ss: # no intersection return True
pass ss, se, _ = self.current
# elif ts > se: # output no intersection
# while ts > se and pop_scope(): if te < ss:
# ss, se, comment = ranges[scope['cursor']] return True
elif ts >= ss and te <= se: # subset # dont output subset
continue if ts >= ss and te <= se:
else: return False
if ss <= te: if ss <= te: # clip left intersection
scope['writeline'](ts, ss - 1, m[5]) self.write_line(ts, ss - 1, m[5])
# if se <= te: if se <= te:
# pop_scope() self.pop()
while se <= te and pop_scope():
if ts <= se: if ts <= se:
scope['writeline'](se + 1, te, m[5]) self.write_line(se + 1, te, m[5])
ss, se, comment = ranges[scope['cursor']] if se < ts:
return True
return False
def write_line(self, start: int = None, end: int = None, comment: str = None) -> None:
if end < start:
return
self.output.write(self.format(start, end, comment) + '\n')
@abstractmethod
def pattern(self) -> str:
pass
@abstractmethod
def format(self, start: int, end: int, arg: any) -> str:
pass
class CharmapScope(Scope):
def pattern(self) -> str:
return r'<U([A-Z0-9]+)>(..<U([A-Z0-9]+)>)?\s+(\S+)\s+(.*?)$'
def format(self, start: int, end: int, comment: str) -> str:
return "%s%s %s %s" % (
"<U%04X>" % start,
"..<U%04X>" % end if end > start else "",
"".join(map(lambda x: "%sx%02x" % (self.escape_char, x), chr(start).encode('utf-8'))),
comment,
)
class WidthScope(Scope):
comment_override = '2'
def pattern(self) -> str:
return r'<U([A-Z0-9]+)>(...<U([A-Z0-9]+)>)?(\s+)(\d+)$'
def format(self, start: int, end: int, comment: str) -> str:
return "%s%s\t%s" % (
"<U%04X>" % start,
"...<U%04X>" % end if end > start else "",
comment
)
def inject_nerdfont_ranges(ranges: List[tuple], textin: TextIO, textout: TextIO):
comment_char = None
keyword_re = re.compile(r'<(\w+)>\s+(\S+)\s*')
charmap_scope = CharmapScope(ranges, textout)
width_scope = WidthScope(ranges, textout)
scope = None
for line in textin:
line = line.strip()
if line.startswith("CHARMAP"):
scope = charmap_scope
elif line.startswith("WIDTH"):
scope = width_scope
elif line.startswith("END "):
while not scope.empty:
scope.pop()
scope = None
elif comment_char and line.startswith(comment_char):
pass
elif scope and not scope.empty:
if scope.read_line(line) is False:
continue continue
elif line: elif line:
m = keyword_re.match(line) m = keyword_re.match(line)
@ -120,7 +152,7 @@ def inject_nerdfont_ranges(ranges: List[tuple], textin: TextIO, textout: TextIO)
if m[1] == "comment_char": if m[1] == "comment_char":
comment_char = m[2] comment_char = m[2]
elif m[1] == 'escape_char': elif m[1] == 'escape_char':
escape_char = m[2] charmap_scope.escape_char = m[2]
textout.write(line + "\n") textout.write(line + "\n")
@ -188,12 +220,18 @@ def test():
print(EXPECT) print(EXPECT)
print() print()
print("\033[42m result \033[0m", len(result.getvalue())) print("\033[42m result \033[0m", len(result.getvalue()))
print(result.getvalue()) a = EXPECT.split('\n')
b = result.getvalue().split('\n')
for i in range(max(len(a), len(b))):
print("\033[%dm%s\033[0m" % (32 if a[i] == b[i] else 31, b[i]))
else: else:
print("pass") print("pass")
if __name__ == "__main__": if __name__ == "__main__":
if os.environ.get('DEBUGGING') == '1':
test()
else:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="patch charmap to make NerdFont icons double width" description="patch charmap to make NerdFont icons double width"
) )
@ -217,20 +255,11 @@ if __name__ == "__main__":
dest="plainout", dest="plainout",
action="store_true", action="store_true",
help="write to stdout in plain-text") help="write to stdout in plain-text")
parser.add_argument(
"--test",
dest="test",
action="store_true",
help="run test case")
args = parser.parse_args() args = parser.parse_args()
font_patcher = open(args.font_patcher, 'r',) font_patcher = open(args.font_patcher, 'r',)
double_width_ranges = extract_nerdfont_ranges(font_patcher) double_width_ranges = extract_nerdfont_ranges(font_patcher)
if args.test:
test()
exit()
in_charmap = gzip.open(args.in_charmap, 'rt', encoding="ascii") in_charmap = gzip.open(args.in_charmap, 'rt', encoding="ascii")
if args.plainout: if args.plainout:
out_charmap = sys.stdout out_charmap = sys.stdout