diff --git a/example.html b/example.html deleted file mode 100644 index 3c62f4b..0000000 --- a/example.html +++ /dev/null @@ -1,890 +0,0 @@ - - - - - - diff: example_old.py / example.py - - - - - - - - -
-
- Split view - -
-
-
-
-
-
-
- - -
-
-
‭example.py
-
- 1#!/usr/bin/env python3 -1#!/usr/bin/env python3 -2 -2 -3import math -3import math -4import itertools -4import itertools -5import textwrap -5import textwrap -
-6 -6 -7import click -7import click -8from reedmuller import reedmuller -8from reedmuller import reedmuller -9 -9 -10 -10 -11class Tag: -11class Tag: -12 """ Helper class to ease creation of SVG. All API functions that create SVG allow you to substitute this with your -12 """ Helper class to ease creation of SVG. All API functions that create SVG allow you to substitute this with your -13 own implementation by passing a ``tag`` parameter. """ -13 own implementation by passing a ``tag`` parameter. """ -14 -14 -15 def __init__(self, name, children=None, root=False, **attrs): -15 def __init__(self, name, children=None, root=False, **attrs): -16 if (fill := attrs.get('fill')) and isinstance(fill, tuple): -16 if (fill := attrs.get('fill')) and isinstance(fill, tuple): -17 attrs['fill'], attrs['fill-opacity'] = fill -17 attrs['fill'], attrs['fill-opacity'] = fill -18 if (stroke := attrs.get('stroke')) and isinstance(stroke, tuple): -18 if (stroke := attrs.get('stroke')) and isinstance(stroke, tuple): -19 attrs['stroke'], attrs['stroke-opacity'] = stroke -19 attrs['stroke'], attrs['stroke-opacity'] = stroke -20 self.name, self.attrs = name, attrs -20 self.name, self.attrs = name, attrs -21 self.children = children or [] -21 self.children = children or [] -22 self.root = root -22 self.root = root -23 -23 -24 def __str__(self): -24 def __str__(self): -25 prefix = '<?xml version="1.0" encoding="utf-8"?>\n' if self.root else '' -25 prefix = '<?xml version="1.0" encoding="utf-8"?>\n' if self.root else '' -26 opening = ' '.join([self.name] + [f'{key.replace("__", ":").replace("_", "-")}="{value}"' for key, value in self.attrs.items()]) -26 opening = ' '.join([self.name] + [f'{key.replace("__", ":").replace("_", "-")}="{value}"' for key, value in self.attrs.items()]) -27 if self.children: -27 if self.children: -28 children = '\n'.join(textwrap.indent(str(c), ' ') for c in self.children) -28 children = '\n'.join(textwrap.indent(str(c), ' ') for c in self.children) -29 return f'{prefix}<{opening}>\n{children}\n</{self.name}>' -29 return f'{prefix}<{opening}>\n{children}\n</{self.name}>' -
-30 else: -30 else: -31 return f'{prefix}<{opening}/>' -31 return f'{prefix}<{opening}/>' -32 -32 -33 -33 -34 @classmethod -34 @classmethod -35 def setup_svg(kls, tags, bounds, margin=0, unit='mm', pagecolor='white'): -35 def setup_svg(kls, tags, bounds, unit='mm', pagecolor='white', inkscape=False): -36 (min_x, min_y), (max_x, max_y) = bounds -36 (min_x, min_y), (max_x, max_y) = bounds -37 - -38 if margin: - -39 min_x -= margin - -40 min_y -= margin - -41 max_x += margin - -42 max_y += margin - -43 -37 -44 w, h = max_x - min_x, max_y - min_y -38 w, h = max_x - min_x, max_y - min_y -45 w = 1.0 if math.isclose(w, 0.0) else w -39 w = 1.0 if math.isclose(w, 0.0) else w -46 h = 1.0 if math.isclose(h, 0.0) else h -40 h = 1.0 if math.isclose(h, 0.0) else h -47 -41 - -42 if inkscape: - -43 tags.insert(0, kls('sodipodi:namedview', [], id='namedview1', pagecolor=pagecolor, - -44 inkscape__document_units=unit)) -48 namespaces = dict( -45 namespaces = dict( -49 xmlns="http://www.w3.org/2000/svg", -46 xmlns="http://www.w3.org/2000/svg", - -47 xmlns__xlink="http://www.w3.org/1999/xlink", - -48 xmlns__sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', - -49 xmlns__inkscape='http://www.inkscape.org/namespaces/inkscape') - -50 - -51 else: - -52 namespaces = dict( - -53 xmlns="http://www.w3.org/2000/svg", -50 xmlns__xlink="http://www.w3.org/1999/xlink") -54 xmlns__xlink="http://www.w3.org/1999/xlink") -51 -55 -52 return kls('svg', tags, -56 return kls('svg', tags, -53 width=f'{w}{unit}', height=f'{h}{unit}', -57 width=f'{w}{unit}', height=f'{h}{unit}', -54 viewBox=f'{min_x} {min_y} {w} {h}', -58 viewBox=f'{min_x} {min_y} {w} {h}', -55 style=f'background-color:{pagecolor}', -59 style=f'background-color:{pagecolor}', -
-56 **namespaces, -60 **namespaces, -57 root=True) -61 root=True) -58 -62 -59 -63 -60@click.command() -64@click.command() -61@click.option('-h', '--height', type=float, default=20, help='Bar height in mm') -65@click.option('-h', '--height', type=float, default=20, help='Bar height in mm') -62@click.option('-t/-n', '--text/--no-text', default=True, help='Whether to add text containing the data under the bar code') -66@click.option('-t/-n', '--text/--no-text', default=True, help='Whether to add text containing the data under the bar code') -63@click.option('-f', '--font', default='sans-serif', help='Font for the text underneath the bar code') -67@click.option('-f', '--font', default='sans-serif', help='Font for the text underneath the bar code') -64@click.option('-s', '--font-size', type=float, default=12, help='Font size for the text underneath the bar code in points (pt)') -68@click.option('-s', '--font-size', type=float, default=12, help='Font size for the text underneath the bar code in points (pt)') -65@click.option('-b', '--bar-width', type=float, default=1.0, help='Bar width in mm') -69@click.option('-b', '--bar-width', type=float, default=1.0, help='Bar width in mm') -66@click.option('-m', '--margin', type=float, default=3.0, help='Margin around bar code in mm') -70@click.option('-m', '--margin', type=float, default=3.0, help='Margin around bar code in mm') -67@click.option('-c', '--color', default='black', help='SVG color for the bar code') -71@click.option('-c', '--color', default='black', help='SVG color for the bar code') -68@click.option('--text-color', default=None, help='SVG color for the text (defaults to the bar code\'s color)') -72@click.option('--text-color', default=None, help='SVG color for the text (defaults to the bar code\'s color)') -69@click.option('--dpi', type=float, default=96, help='DPI value to assume for internal SVG unit conversions') -73@click.option('--dpi', type=float, default=96, help='DPI value to assume for internal SVG unit conversions') -70@click.argument('data') -74@click.argument('data') -71@click.argument('outfile', type=click.File('w'), default='-') -75@click.argument('outfile', type=click.File('w'), default='-') -72def cli(data, outfile, height, text, font, font_size, bar_width, margin, color, text_color, dpi): -76def cli(data, outfile, height, text, font, font_size, bar_width, margin, color, text_color, dpi): -73 data = int(data, 16) -77 data = int(data, 16) -74 text_color = text_color or color -78 text_color = text_color or color -75 -79 -76 NUM_BITS = 26 -80 NUM_BITS = 26 -77 -81 -78 data_bits = [bool(data & (1<<i)) for i in range(NUM_BITS)] -82 data_bits = [bool(data & (1<<i)) for i in range(NUM_BITS)] -79 data_encoded = itertools.chain(*[ -83 data_encoded = itertools.chain(*[ -80 (a, not a) for a in data_bits -84 (a, not a) for a in data_bits -81 ]) -85 ]) -82 data_encoded = [True, False, True, False, *data_encoded, False, True, True, False, True] -86 data_encoded = [True, False, True, False, *data_encoded, False, True, True, False, True] -83 -87 -84 width = len(data_encoded) * bar_width -88 width = len(data_encoded) * bar_width -85 # 1 px = 0.75 pt -89 # 1 px = 0.75 pt -86 pt_to_mm = lambda pt: pt / 0.75 /dpi * 25.4 -90 pt_to_mm = lambda pt: pt / 0.75 /dpi * 25.4 -87 font_size = pt_to_mm(font_size) -91 font_size = pt_to_mm(font_size) -88 total_height = height + font_size*2 -92 total_height = height + font_size*2 -89 -93 -90 tags = [] -94 tags = [] -91 for key, group in itertools.groupby(enumerate(data_encoded), key=lambda x: x[1]): -95 for key, group in itertools.groupby(enumerate(data_encoded), key=lambda x: x[1]): -92 if key: -96 if key: -93 group = list(group) -97 group = list(group) -94 x0, _key = group[0] -98 x0, _key = group[0] -95 w = len(group) -99 w = len(group) -96 tags.append(Tag('path', stroke=color, stroke_width=w, d=f'M {(x0 + w/2)*bar_width} 0 l 0 {height}')) -100 tags.append(Tag('path', stroke=color, stroke_width=w, d=f'M {(x0 + w/2)*bar_width} 0 l 0 {height}')) -97 -101 -98 if text: -102 if text: -99 tags.append(Tag('text', children=[f'{data:07x}'], -103 tags.append(Tag('text', children=[f'{data:07x}'], -100 x=width/2, y=height + 0.5*font_size, -104 x=width/2, y=height + 0.5*font_size, -101 font_family=font, font_size=f'{font_size:.3f}px', -105 font_family=font, font_size=f'{font_size:.3f}px', -102 text_anchor='middle', dominant_baseline='hanging', -106 text_anchor='middle', dominant_baseline='hanging', -103 fill=text_color)) -107 fill=text_color)) -104 -108 -
-105 outfile.write(str(Tag.setup_svg(tags, bounds=((0, 0), (width, total_height)), margin=margin))) -109 outfile.write(str(Tag.setup_svg(tags, bounds=((0, 0), (width, total_height)), margin=margin))) -106 -110 -107 -111 -108if __name__ == '__main__': -112if __name__ == '__main__': -109 cli() -113 cli() -
-
-
- - - diff --git a/example.py b/example.py deleted file mode 100644 index 2e22e20..0000000 --- a/example.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python3 - -import math -import itertools -import textwrap - -import click -from reedmuller import reedmuller - - -class Tag: - """ Helper class to ease creation of SVG. All API functions that create SVG allow you to substitute this with your - own implementation by passing a ``tag`` parameter. """ - - def __init__(self, name, children=None, root=False, **attrs): - if (fill := attrs.get('fill')) and isinstance(fill, tuple): - attrs['fill'], attrs['fill-opacity'] = fill - if (stroke := attrs.get('stroke')) and isinstance(stroke, tuple): - attrs['stroke'], attrs['stroke-opacity'] = stroke - self.name, self.attrs = name, attrs - self.children = children or [] - self.root = root - - def __str__(self): - prefix = '\n' if self.root else '' - opening = ' '.join([self.name] + [f'{key.replace("__", ":").replace("_", "-")}="{value}"' for key, value in self.attrs.items()]) - if self.children: - children = '\n'.join(textwrap.indent(str(c), ' ') for c in self.children) - return f'{prefix}<{opening}>\n{children}\n' - else: - return f'{prefix}<{opening}/>' - - - @classmethod - def setup_svg(kls, tags, bounds, unit='mm', pagecolor='white', inkscape=False): - (min_x, min_y), (max_x, max_y) = bounds - - w, h = max_x - min_x, max_y - min_y - w = 1.0 if math.isclose(w, 0.0) else w - h = 1.0 if math.isclose(h, 0.0) else h - - if inkscape: - tags.insert(0, kls('sodipodi:namedview', [], id='namedview1', pagecolor=pagecolor, - inkscape__document_units=unit)) - namespaces = dict( - xmlns="http://www.w3.org/2000/svg", - xmlns__xlink="http://www.w3.org/1999/xlink", - xmlns__sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', - xmlns__inkscape='http://www.inkscape.org/namespaces/inkscape') - - else: - namespaces = dict( - xmlns="http://www.w3.org/2000/svg", - xmlns__xlink="http://www.w3.org/1999/xlink") - - return kls('svg', tags, - width=f'{w}{unit}', height=f'{h}{unit}', - viewBox=f'{min_x} {min_y} {w} {h}', - style=f'background-color:{pagecolor}', - **namespaces, - root=True) - - -@click.command() -@click.option('-h', '--height', type=float, default=20, help='Bar height in mm') -@click.option('-t/-n', '--text/--no-text', default=True, help='Whether to add text containing the data under the bar code') -@click.option('-f', '--font', default='sans-serif', help='Font for the text underneath the bar code') -@click.option('-s', '--font-size', type=float, default=12, help='Font size for the text underneath the bar code in points (pt)') -@click.option('-b', '--bar-width', type=float, default=1.0, help='Bar width in mm') -@click.option('-m', '--margin', type=float, default=3.0, help='Margin around bar code in mm') -@click.option('-c', '--color', default='black', help='SVG color for the bar code') -@click.option('--text-color', default=None, help='SVG color for the text (defaults to the bar code\'s color)') -@click.option('--dpi', type=float, default=96, help='DPI value to assume for internal SVG unit conversions') -@click.argument('data') -@click.argument('outfile', type=click.File('w'), default='-') -def cli(data, outfile, height, text, font, font_size, bar_width, margin, color, text_color, dpi): - data = int(data, 16) - text_color = text_color or color - - NUM_BITS = 26 - - data_bits = [bool(data & (1<\n{children}\n' - else: - return f'{prefix}<{opening}/>' - - - @classmethod - def setup_svg(kls, tags, bounds, margin=0, unit='mm', pagecolor='white'): - (min_x, min_y), (max_x, max_y) = bounds - - if margin: - min_x -= margin - min_y -= margin - max_x += margin - max_y += margin - - w, h = max_x - min_x, max_y - min_y - w = 1.0 if math.isclose(w, 0.0) else w - h = 1.0 if math.isclose(h, 0.0) else h - - namespaces = dict( - xmlns="http://www.w3.org/2000/svg", - xmlns__xlink="http://www.w3.org/1999/xlink") - - return kls('svg', tags, - width=f'{w}{unit}', height=f'{h}{unit}', - viewBox=f'{min_x} {min_y} {w} {h}', - style=f'background-color:{pagecolor}', - **namespaces, - root=True) - - -@click.command() -@click.option('-h', '--height', type=float, default=20, help='Bar height in mm') -@click.option('-t/-n', '--text/--no-text', default=True, help='Whether to add text containing the data under the bar code') -@click.option('-f', '--font', default='sans-serif', help='Font for the text underneath the bar code') -@click.option('-s', '--font-size', type=float, default=12, help='Font size for the text underneath the bar code in points (pt)') -@click.option('-b', '--bar-width', type=float, default=1.0, help='Bar width in mm') -@click.option('-m', '--margin', type=float, default=3.0, help='Margin around bar code in mm') -@click.option('-c', '--color', default='black', help='SVG color for the bar code') -@click.option('--text-color', default=None, help='SVG color for the text (defaults to the bar code\'s color)') -@click.option('--dpi', type=float, default=96, help='DPI value to assume for internal SVG unit conversions') -@click.argument('data') -@click.argument('outfile', type=click.File('w'), default='-') -def cli(data, outfile, height, text, font, font_size, bar_width, margin, color, text_color, dpi): - data = int(data, 16) - text_color = text_color or color - - NUM_BITS = 26 - - data_bits = [bool(data & (1< -
- Split view - -
-
-
-
-
-
- -''' -MAIN_CSS = r''' - -@media (prefers-color-scheme: light) { - html { - --c-bg-primary: #ffffff; - --c-fg-primary: #000000; - --c-bg-auxiliary: #f8f8f8; - --c-fg-auxiliary: #a0a0a0; - --c-border-line: #e0e0e0; - --c-bg-insert: #ecfdf0; - --c-bg-delete: #fbe9eb; - --c-bg-delete-lineno: #f9d7dc; - --c-fg-delete-lineno: #ae969a; - --c-bg-delete-word: #fac5cd; - --c-fg-delete-word: #400000; - --c-fg-insert-word: #004000; - --c-bg-insert-word: #c7f0d2; - --c-fg-insert-lineno: #9bb0a1; - --c-bg-insert-lineno: #ddfbe6; - --c-bg-empty: #f0f0f0; - --c-fg-foldline: #bbbbbb; - --c-border-delete: #e0c8c8; /* pick a darker border color inside the light red gutter */ - } -} - -@media (prefers-color-scheme: dark) { - html { - --c-bg-primary: #010409; - --c-fg-primary: #a0a0a0; - --c-bg-auxiliary: #0d1117; - --c-fg-auxiliary: #f0f6fc; - --c-fg-foldline: #bbbbbb; - --c-border-line: #3d444d; - --c-bg-insert: #223738; - --c-bg-delete: #280d1f; - --c-bg-delete-lineno: #421632; - --c-fg-delete-lineno: #ae969a; - --c-bg-delete-word: #421632; - --c-fg-delete-word: #fac5cd; - --c-fg-insert-word: #c7f0d2; - --c-bg-insert-word: #325148; - --c-fg-insert-lineno: #9bb0a1; - --c-bg-insert-lineno: #325148; - --c-bg-empty: #080b0f; - --c-border-delete: #e0c8c8; - } -} - -@media print { - html { - /* Copy of the light theme, but we clip all light gray backgrounds to white. */ - --c-bg-primary: #ffffff; - --c-fg-primary: #000000; - --c-bg-auxiliary: #ffffff; - --c-fg-auxiliary: #a0a0a0; - --c-border-line: #e0e0e0; - --c-bg-insert: #ecfdf0; - --c-bg-delete: #fbe9eb; - --c-bg-delete-lineno: #f9d7dc; - --c-fg-delete-lineno: #ae969a; - --c-bg-delete-word: #fac5cd; - --c-fg-delete-word: #400000; - --c-fg-insert-word: #004000; - --c-bg-insert-word: #c7f0d2; - --c-fg-insert-lineno: #9bb0a1; - --c-bg-insert-lineno: #ddfbe6; - --c-bg-empty: #ffffff; - --c-fg-foldline: #bbbbbb; - --c-border-delete: #e0c8c8; - } -} - -@layer wsd-base-style { - html { - background-color: var(--c-bg-primary); - height: 100%; - width: 100%; +HTML_TEMPLATE = r''' + + + + + $title + + + + - + + - $diff_style_toggle - -
- $body +
+
+ Split view + +
+
+
+
+
+
+ +
+$body +
''' +PYGMENTS_CSS = ''' +body .hll { background-color: #ffffcc } +body { background: #ffffff; } +body .c { color: #177500 } /* Comment */ +body .err { color: #000000 } /* Error */ +body .k { color: #A90D91 } /* Keyword */ +body .l { color: #1C01CE } /* Literal */ +body .n { color: #000000 } /* Name */ +body .o { color: #000000 } /* Operator */ +body .cm { color: #177500 } /* Comment.Multiline */ +body .cp { color: #633820 } /* Comment.Preproc */ +body .c1 { color: #177500 } /* Comment.Single */ +body .cs { color: #177500 } /* Comment.Special */ +body .kc { color: #A90D91 } /* Keyword.Constant */ +body .kd { color: #A90D91 } /* Keyword.Declaration */ +body .kn { color: #A90D91 } /* Keyword.Namespace */ +body .kp { color: #A90D91 } /* Keyword.Pseudo */ +body .kr { color: #A90D91 } /* Keyword.Reserved */ +body .kt { color: #A90D91 } /* Keyword.Type */ +body .ld { color: #1C01CE } /* Literal.Date */ +body .m { color: #1C01CE } /* Literal.Number */ +body .s { color: #C41A16 } /* Literal.String */ +body .na { color: #836C28 } /* Name.Attribute */ +body .nb { color: #A90D91 } /* Name.Builtin */ +body .nc { color: #3F6E75 } /* Name.Class */ +body .no { color: #000000 } /* Name.Constant */ +body .nd { color: #000000 } /* Name.Decorator */ +body .ni { color: #000000 } /* Name.Entity */ +body .ne { color: #000000 } /* Name.Exception */ +body .nf { color: #000000 } /* Name.Function */ +body .nl { color: #000000 } /* Name.Label */ +body .nn { color: #000000 } /* Name.Namespace */ +body .nx { color: #000000 } /* Name.Other */ +body .py { color: #000000 } /* Name.Property */ +body .nt { color: #000000 } /* Name.Tag */ +body .nv { color: #000000 } /* Name.Variable */ +body .ow { color: #000000 } /* Operator.Word */ +body .mb { color: #1C01CE } /* Literal.Number.Bin */ +body .mf { color: #1C01CE } /* Literal.Number.Float */ +body .mh { color: #1C01CE } /* Literal.Number.Hex */ +body .mi { color: #1C01CE } /* Literal.Number.Integer */ +body .mo { color: #1C01CE } /* Literal.Number.Oct */ +body .sb { color: #C41A16 } /* Literal.String.Backtick */ +body .sc { color: #2300CE } /* Literal.String.Char */ +body .sd { color: #C41A16 } /* Literal.String.Doc */ +body .s2 { color: #C41A16 } /* Literal.String.Double */ +body .se { color: #C41A16 } /* Literal.String.Escape */ +body .sh { color: #C41A16 } /* Literal.String.Heredoc */ +body .si { color: #C41A16 } /* Literal.String.Interpol */ +body .sx { color: #C41A16 } /* Literal.String.Other */ +body .sr { color: #C41A16 } /* Literal.String.Regex */ +body .s1 { color: #C41A16 } /* Literal.String.Single */ +body .ss { color: #C41A16 } /* Literal.String.Symbol */ +body .bp { color: #5B269A } /* Name.Builtin.Pseudo */ +body .vc { color: #000000 } /* Name.Variable.Class */ +body .vg { color: #000000 } /* Name.Variable.Global */ +body .vi { color: #000000 } /* Name.Variable.Instance */ +body .il { color: #1C01CE } /* Literal.Number.Integer.Long */ + +/* +These styles are used to highlight each diff line. +Note: for partial like highlight change to "display:block-inline" +*/ +span.left_diff_change { + background-color: #FFE5B5; + display: block +} +span.left_diff_add { + background-color: #eeeeee; + display: block +} +span.left_diff_del { + background-color: #ffdddd; + display: block +} +span.lineno_q { + display: block; +} +span.right_diff_change { + background-color: #FFE5B5; + display: block +} +span.right_diff_add { + background-color: #ddffdd; + display: block +} +span.right_diff_del { + background-color: #eeeeee; + display: block +} +span.clearbg { + background-color: transparent; +} +''' + +class SexprLexer(RegexLexer): + name = 'KiCad S-Expression' + aliases = ['sexp'] + filenames = ['*.kicad_mod', '*.kicad_sym'] + + tokens = { + 'root': [ + (r'\s+', token.Whitespace), + (r'[()]', token.Punctuation), + (r'([+-]?\d+\.\d+)(?=[)\s])', token.Number), + (r'(-?\d+)(?=[)\s])', token.Number), + (r'"((?:[^"]|\\")*)"(?=[)\s])', token.String), + (r'([^()"\s]+)(?=[)\s])', token.Name), + ] + } + +from pygments.formatter import Formatter +from pygments.token import STANDARD_TYPES + +from functools import lru_cache + @lru_cache(maxsize=256) def get_token_class(ttype): while not (name := STANDARD_TYPES.get(ttype)): if ttype is token.Token: return 'n' ttype = ttype.parent - return f'wsd-{name}' + return name def iter_token_lines(tokensource): lineno = 1 @@ -583,16 +569,17 @@ class RecordFormatter(Formatter): if lineno_ours == lineno: break else: - self.lines.append((True, f'')) + self.lines.append(f'') + assert lineno_ours == lineno if not change: change_class = '' elif not lineno_ours or not lineno_theirs: - change_class = ' wsd-insert' + change_class = ' insert' else: - change_class = ' wsd-change' + change_class = ' change' - line = f'{lineno}' + line = f'{lineno}' parts = re.split(r'(\00.|\01|$)', diff_ours) source_pos = 0 @@ -612,9 +599,9 @@ class RecordFormatter(Formatter): if source_pos <= next_marker_pos < source_pos + len(value): split_pos = next_marker_pos - source_pos left, value = value[:split_pos], value[split_pos:] - line += f'{html.escape(left)}' + line += f'{html.escape(left)}' source_pos += len(left) - diff_class = ' wsd-word-change' if next_marker_type.startswith('\0') else '' + diff_class = ' word_change' if next_marker_type.startswith('\0') else '' diff_markers = diff_markers[1:] else: break @@ -625,12 +612,13 @@ class RecordFormatter(Formatter): line += '' line += '' - self.lines.append((change, line)) + self.lines.append(line) for _ours_empty, (lineno_theirs, _diff_theirs), change in diff: - self.lines.append((True, f'')) + self.lines.append(f'') + assert change and lineno_theirs -def html_diff_content(old, new, lexer, context_len=5, fold_min=5): +def html_diff_content(old, new, lexer): diff = list(difflib._mdiff(old.splitlines(), new.splitlines())) fmt_l = RecordFormatter('left', diff) @@ -639,28 +627,13 @@ def html_diff_content(old, new, lexer, context_len=5, fold_min=5): fmt_r = RecordFormatter('right', diff) pygments.highlight(new, lexer, fmt_r) - out = [] - for change, group in groupby(zip(fmt_l.lines, fmt_r.lines), lambda pair: pair[0][0]): - group = list(group) - do_collapse = not change and len(group) > 2*context_len + fold_min - for i, ((_change_left, line_left), (_change_right, line_right)) in enumerate(group): - if do_collapse and i == context_len: - out.append(f'
') - out.append(line_left) - out.append(line_right) - if do_collapse and i == len(group) - context_len - 1: - out.append('
') - return '\n'.join(out) + return '\n'.join(chain.from_iterable(zip(fmt_l.lines, fmt_r.lines))) -def html_diff_block(old, new, filename, lexer, hide_filename=True, context_len=5, fold_min=5): - lexer.stripnl = False # Make pygments preserve leading and trailing empty lines. - code = html_diff_content(old, new, lexer, context_len=context_len, fold_min=fold_min) - filename = f'
‭{filename}
' - if hide_filename: - filename = '' - return textwrap.dedent(f'''
- {filename} -
+def html_diff_block(old, new, filename, lexer): + code = html_diff_content(old, new, lexer) + return textwrap.dedent(f'''
+
‭{filename}
+
{code}
''') @@ -674,11 +647,8 @@ def cli(): parser.add_argument('-L', '--list-lexers', action='store_true', help='List available lexers for -l/--lexer') parser.add_argument('-t', '--pagetitle', help='Override page title of output HTML file') parser.add_argument('-o', '--output', default=sys.stdout, type=argparse.FileType('w'), help='Name of output file (default: stdout)') - parser.add_argument('--context-len', type=int, default=5, help='Number of lines to always print around changes without folding') - parser.add_argument('--fold-min', type=int, default=5, help='Minimum number of unchanged lines beyond which to fold') parser.add_argument('--header', action='store_true', help='Only output HTML header with stylesheets and stuff, and no diff') parser.add_argument('--content', action='store_true', help='Only output HTML content, without header') - parser.add_argument('--nofilename', action='store_true', help='Do not output file name headers') parser.add_argument('old', nargs='?', help='source file or directory to compare ("before" file)') parser.add_argument('new', nargs='?', help='source file or directory to compare ("after" file)') args = parser.parse_args() @@ -696,24 +666,12 @@ def cli(): if args.syntax_css: syntax_css = Path(args.syntax_css).read_text() else: - light_css = HtmlFormatter(classprefix='wsd-', style='xcode').get_style_defs() - dark_css = HtmlFormatter(classprefix='wsd-', style=witchhazel.WitchHazelStyle).get_style_defs() - - syntax_css = textwrap.dedent(f'''@media print, (prefers-color-scheme: light) {{ - {light_css} - }} - - @media (prefers-color-scheme: dark) {{ - {dark_css} - }}''') + syntax_css = PYGMENTS_CSS if args.header: print(string.Template(HTML_TEMPLATE).substitute( title=pagetitle, pygments_css=syntax_css, - main_css=MAIN_CSS, - diff_style_toggle=DIFF_STYLE_TOGGLE, - diff_style_script=DIFF_STYLE_SCRIPT, body='$body'), file=args.output) sys.exit(0) @@ -757,13 +715,16 @@ def cli(): if args.lexer: lexer = get_lexer_by_name(lexer) else: - try: - lexer = guess_lexer_for_filename(new, new_text) - except: - lexer = get_lexer_by_name('text') + if new.suffix.lower() in ('.kicad_mod', '.kicad_mod', '.kicad_pcb', '.kicad_sch')\ + or new.name == 'sym_lib_table': + lexer = SexprLexer() + else: + try: + lexer = guess_lexer_for_filename(new, new_text) + except: + lexer = get_lexer_by_name('text') - diff_blocks.append(html_diff_block(old_text, new_text, suffix, lexer, hide_filename=args.nofilename, - context_len=args.context_len, fold_min=args.fold_min)) + diff_blocks.append(html_diff_block(old_text, new_text, suffix, lexer)) body = '\n'.join(diff_blocks) if args.content: @@ -772,9 +733,6 @@ def cli(): print(string.Template(HTML_TEMPLATE).substitute( title=pagetitle, pygments_css=syntax_css, - main_css=MAIN_CSS, - diff_style_toggle=DIFF_STYLE_TOGGLE, - diff_style_script=DIFF_STYLE_SCRIPT, body=body), file=args.output) if args.open: