- $body
+
+
+
+$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'
')
- 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'
'
- 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'''
''')
@@ -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: