Skip to content

Commit d73aefe

Browse files
committed
feat: Quality of life improvements for Changelog, History and Diff views
- History: now displayed via GET and /../history/rev_a/rev_b instead of POST submitted reuqest. To not mess up the history and be able to link the diff. - Diff: The displayed chunks/the lines of the chunks were not correctly ordered. Now what is displayed matches what a unidiff looks like. - Changelog/Show commit: Now the /commit/ can handle renamed, deleted or created files much better. In case of an edit the diff is shown.
1 parent 0136a83 commit d73aefe

File tree

8 files changed

+152
-111
lines changed

8 files changed

+152
-111
lines changed

otterwiki/helper.py

+25
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
1010
"""
1111

12+
import re
13+
from collections import namedtuple
1214
from otterwiki.server import app, mail, storage, Preferences
1315
from otterwiki.gitstorage import StorageError
1416
from flask import flash, url_for, session
@@ -190,3 +192,26 @@ def get_pagename_prefixes(filter=[]):
190192
pagename_prefixes.append(crumb)
191193
if len(pagename_prefixes)>3: break
192194
return pagename_prefixes
195+
196+
197+
198+
def patchset2urlmap(patchset, rev_b, rev_a=None):
199+
url_map = {}
200+
for file in patchset:
201+
source_file = re.sub('^(a|b)/','',file.source_file)
202+
target_file = re.sub('^(a|b)/','',file.target_file)
203+
if rev_a is None:
204+
#import ipdb; ipdb.set_trace()
205+
try:
206+
rev_a = storage.get_parent_revision(filename=source_file,revision=rev_b)
207+
except StorageError:
208+
rev_a = rev_b
209+
d = {
210+
'source_file' : source_file,
211+
'target_file' : target_file,
212+
'source_url' : auto_url(source_file,revision=rev_a)[1] if source_file != "/dev/null" else None,
213+
'target_url' : auto_url(target_file,revision=rev_b)[1] if target_file != "/dev/null" else None,
214+
}
215+
url_map[file.path] = namedtuple('UrlData', d.keys())(*d.values())
216+
return url_map
217+

otterwiki/static/css/partials/history.css

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ table.diff td {
99
table.diff td.filename {
1010
padding-top: 1rem;
1111
padding-left: 0;
12+
border-bottom: 1px solid rgba(128,128,128,0.25);
1213
}
1314
table.diff tr.added,
1415
table.diff tr.removed,
16+
table.diff td.value,
1517
table.diff td.hunk {
1618
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
1719
}
@@ -89,4 +91,4 @@ a:hover span.page-match {
8991
color: rgba(0,0,0,.85);
9092
text-decoration: underline;
9193
padding: 0.2em;
92-
}
94+
}

otterwiki/templates/diff.html

+54-36
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,79 @@
11
{# vim: set et ts=8 sts=4 sw=4 ai: #}
2+
{% if pagepath %}
23
{% extends "page.html" %}
4+
{% else %}
5+
{% extends "wiki.html" %}
6+
{% endif %}
7+
8+
9+
{% block menu %}
10+
{{ super() }}
11+
{% if not pagepath and menutree %}
12+
{% include 'snippets/menutree.html' %}
13+
{% endif %}
14+
{% endblock %}
15+
316
{% block content %}
417
<div class="w-full mw-full p-0 clearfix">
518
{% if rev_a and rev_b %}
619
<h3>Comparing <tt>{{rev_a}}</tt> to <tt>{{rev_b}}</tt></h3>
7-
{% endif %}
8-
{% if revision %}
20+
{% elif revision %}
921
<h3>Commit <tt>{{revision}}</tt></h3>
1022
{% endif %}
23+
{% if metadata %}
24+
<span class="datetime" title="{{metadata.datetime|format_datetime("deltanow")}} ago">{{metadata.datetime|format_datetime}}</span>
25+
{%if not metadata.author_email%}{{metadata.author_name}}{%else%}<a href="mailto:{{metadata.author_email}}">{{metadata.author_name}}</a>{%endif%}:
26+
{{metadata.message or '-/-'|safe}}
27+
{%endif%}
1128
<table class="diff">
12-
{% if patchset %}
1329
{% for file in patchset %}
14-
{# file.path #} {# file.added #} {# file.removed #}
30+
{% with filename=file.path,lines=file_diffs[file.path],urlobj=url_map[file.path] %}
31+
{# {{filename}} {{lines}} {{file}} #}
32+
{%- if page_filename|length == 0 or filename == page_filename %}
1533
<tr>
1634
<td class="filename" colspan="{%if withlinenumbers %}4{%else%}2{%endif%}">
17-
<a href="{{url_map[file.path][1]}}">{{url_map[file.path][0]}}</a>
35+
{%- if urlobj.source_file == urlobj.target_file %}
36+
<tt>{{urlobj.source_file}}</tt> <a href="{{urlobj.source_url}}" class="revision-small">{{rev_a}}</a> ..
37+
<a href="{{urlobj.target_url}}" class="revision-small">{{rev_b}}</a>
38+
{%- else %}
39+
{%- if urlobj.source_url %}
40+
<a href="{{urlobj.source_url}}"><tt>{{urlobj.source_file}}</tt></a>
41+
{%- else %}
42+
<tt>{{urlobj.source_file}}</tt>
43+
{%- endif %}
44+
..
45+
{%- if urlobj.target_url %}
46+
<a href="{{urlobj.target_url}}"><tt>{{urlobj.target_file}}</tt></a>
47+
{%- else %}
48+
<tt>{{urlobj.target_file}}</tt>
49+
{%- endif %}
50+
{%- endif %}
51+
{#
52+
{%- elif rev_a and rev_b %}
53+
<tt>{{url_map[filename][0][0]}}</tt> <a href="{{url_map[filename][0][1]}}" class="revision-small">{{rev_a}}</a> .. <a href="{{url_map[filename][1][1]}}" class="revision-small">{{rev_b}}</a>
54+
{%- endif %}
55+
#}
1856
</td>
1957
</tr>
20-
{% for hunk in file %}
21-
<tr>
22-
<td class="hunk" colspan="{%if withlinenumbers %}4{%else%}2{%endif%}">@@ {{hunk.source_start}},{{hunk.source_length}} {{hunk.target_start}},{{hunk.target_length}}@@ </td>
23-
</tr>
24-
{% for i,lines in hunk_helper[(file.source_file, file.target_file, hunk.source_start, hunk.source_length)].items() -%}
2558
{% for l in lines %}
2659
<tr class="{{l.style}}">
60+
{%- if l.style == "hunk" %}
61+
<td class="hunk" colspan="{%if withlinenumbers %}4{%else%}2{%endif%}">{{l.value}}</td>
62+
{%- else %}
2763
{%-if withlinenumbers -%}
2864
<td class="diff-decoration">{{l.source}}</td>
2965
<td class="diff-decoration">{{l.target}}</td>
3066
{%- endif -%}
3167
<td class="diff-decoration">{{l.type}}</td>
32-
<td>{{l.value | replace('\n', '')}}</td>
68+
<td class="value">{{l.value | replace('\n', '')}}</td>
69+
{%- endif -%}
3370
</tr>
3471
{%- endfor -%}{# l in lines #}
35-
{%- endfor %}{# lines in hunk_helper #}
36-
37-
{#
38-
{% for l in hunk.source_lines()-%}
39-
<tr style="background-color:#a00;">
40-
<td style="width:1%;">{{l.source_line_no}}</td>
41-
<td style="width:1%;">{{l.target_line_no}}</td>
42-
<td style="width:1%;">{{l.line_type}}</td>
43-
<td>{{l.value}}</td>
44-
</tr>
45-
{% endfor %}
46-
{% for l in hunk.target_lines()-%}
47-
<tr style="background-color:#0a0;">
48-
<td style="width:1%;">{{l.source_line_no}}</td>
49-
<td style="width:1%;">{{l.target_line_no}}</td>
50-
<td style="width:1%;">{{l.line_type}}</td>
51-
<td>{{l.value}}</td>
52-
</tr>
53-
{% endfor %}
54-
#}
55-
56-
{% endfor %}{# hunk in file #}
57-
{% endfor %}{# file in patchset #}
72+
{%- else -%}
73+
<!-- skipped page_filename={{page_filename}} filename={{filename}} -->
74+
{%- endif %}
75+
{% endwith %}{# filenname,lines in file_diffs #}
76+
{% endfor %}{# filenname,lines in file_diffs #}
5877
</table>
59-
{% endif %}
78+
{######}
6079
{% endblock %}
61-

otterwiki/util.py

+22-35
Original file line numberDiff line numberDiff line change
@@ -154,51 +154,38 @@ def mkdir(path):
154154
pathlib.Path(path).mkdir(parents=True, exist_ok=True)
155155

156156

157-
def patchset2hunkdict(patchset):
158-
hunk_helper = {}
157+
def patchset2filedict(patchset):
159158
_line_type_style = {
160159
" ": "",
161160
"+": "added",
162161
"-": "removed",
163162
}
163+
files = {}
164164
for file in patchset:
165+
line_data = []
165166
for hunk in file:
166-
lines = {}
167-
for l in hunk.source_lines():
168-
if not l.source_line_no in lines:
169-
lines[l.source_line_no] = []
170-
lines[l.source_line_no].append(
167+
line_data.append(
168+
{
169+
"source": "",
170+
"target": "",
171+
"value": f"@@ {hunk.source_start},{hunk.source_length} {hunk.target_start},{hunk.target_length} @@",
172+
"style": "hunk",
173+
}
174+
)
175+
for line in hunk:
176+
line_data.append(
171177
{
172-
"source": l.source_line_no,
173-
"target": l.target_line_no or "",
174-
"type": l.line_type,
175-
"style": _line_type_style[l.line_type],
176-
"value": l.value,
178+
"source": line.source_line_no or "",
179+
"target": line.target_line_no or "",
180+
"type": line.line_type,
181+
"style": _line_type_style[line.line_type],
182+
"value": line.value,
183+
"hunk": False,
177184
}
178185
)
179-
for l in hunk.target_lines():
180-
if l.source_line_no is not None:
181-
continue
182-
if not l.target_line_no in lines:
183-
lines[l.target_line_no] = []
184-
lines[l.target_line_no].append(
185-
{
186-
"source": l.source_line_no or "",
187-
"target": l.target_line_no,
188-
"type": l.line_type,
189-
"style": _line_type_style[l.line_type],
190-
"value": l.value,
191-
}
192-
)
193-
hunk_helper[
194-
(
195-
file.source_file,
196-
file.target_file,
197-
hunk.source_start,
198-
hunk.source_length,
199-
)
200-
] = lines
201-
return hunk_helper
186+
files[file.path] = line_data
187+
188+
return files
202189

203190

204191
def get_local_timezone():

otterwiki/views.py

+9
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,15 @@ def history(path):
259259
rev_b=request.form.get("rev_b"),
260260
)
261261

262+
@app.route("/<path:path>/diff/<string:rev_a>/<string:rev_b>", methods=["POST", "GET"])
263+
def diff(path, rev_a, rev_b):
264+
# return "path={}".format(path)
265+
p = Page(path)
266+
return p.diff(
267+
rev_a=rev_a,
268+
rev_b=rev_b,
269+
)
270+
262271

263272
@app.route("/<path:path>/rename/", methods=["POST", "GET"])
264273
@app.route("/<path:path>/rename", methods=["POST", "GET"])

otterwiki/wiki.py

+32-20
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
get_pagepath,
3030
get_page_directoryname,
3131
sanitize_pagename,
32-
patchset2hunkdict,
32+
patchset2filedict,
3333
get_header,
3434
)
3535
from otterwiki.helper import (
@@ -39,6 +39,7 @@
3939
get_attachment_directoryname,
4040
get_pagename,
4141
get_pagename_prefixes,
42+
patchset2urlmap,
4243
)
4344
from otterwiki.auth import has_permission, current_user
4445
from otterwiki.plugins import chain_hooks
@@ -55,6 +56,8 @@
5556

5657

5758
def get_breadcrumbs(pagepath):
59+
if not pagepath or len(pagepath)<1:
60+
return []
5861
# strip trailing slashes
5962
pagepath = pagepath.rstrip("/")
6063
parents = []
@@ -386,25 +389,33 @@ def show_commit(self, revision):
386389
if not has_permission("READ"):
387390
abort(403)
388391
try:
389-
_, diff = storage.show_commit(revision)
392+
metadata, diff = storage.show_commit(revision)
390393
except StorageError as e:
391394
app.logger.error(e)
392395
abort(404)
396+
393397
patchset = PatchSet(diff)
394-
url_map = {}
395-
for file in patchset:
396-
url_map[file.path] = auto_url(
397-
file.path,
398-
revision=revision,
399-
)
400-
hunk_helper = patchset2hunkdict(patchset)
398+
pagepath = None
399+
400+
url_map=patchset2urlmap(patchset, revision)
401+
if len(url_map) == 1:
402+
pagepath = get_pagepath(list(url_map.keys())[0])
403+
404+
menutree = SidebarNavigation(get_page_directoryname(pagepath or "/"))
405+
406+
file_diffs = patchset2filedict(patchset)
401407
return render_template(
402408
"diff.html",
403409
title="commit {}".format(revision),
410+
metadata=metadata,
404411
url_map=url_map,
412+
file_diffs=file_diffs,
405413
patchset=patchset,
406-
hunk_helper=hunk_helper,
407414
revision=revision,
415+
withlinenumbers=False,
416+
pagepath=pagepath,
417+
breadcrumbs=get_breadcrumbs(pagepath),
418+
menutree=menutree.query(),
408419
)
409420

410421

@@ -743,25 +754,26 @@ def diff(self, rev_a=None, rev_b=None):
743754
# handle case that the page doesn't exists
744755
self.exists_or_404()
745756

746-
diff = storage.diff(self.filename, rev_b, rev_a)
757+
diff = storage.diff(rev_a, rev_b)
747758
patchset = PatchSet(diff)
748-
url_map = {}
749-
for file in patchset:
750-
url_map[file.path] = auto_url(
751-
file.path,
752-
revision=rev_a,
753-
)
754-
hunk_helper = patchset2hunkdict(patchset)
759+
url_map=patchset2urlmap(patchset, rev_b, rev_a)
760+
file_diffs = patchset2filedict(patchset)
761+
762+
menutree = SidebarNavigation(get_page_directoryname(self.pagepath))
755763
return render_template(
756764
"diff.html",
757765
title="{} - diff {} {}".format(self.pagename, rev_a, rev_b),
758766
pagepath=self.pagepath,
759767
pagename=self.pagename,
768+
page_filename=self.filename,
769+
file_diffs=file_diffs,
760770
patchset=patchset,
761771
url_map=url_map,
762-
hunk_helper=hunk_helper,
763772
rev_a=rev_a,
764773
rev_b=rev_b,
774+
withlinenumbers=False,
775+
menutree=menutree.query(),
776+
breadcrumbs=self.breadcrumbs(),
765777
)
766778

767779
def history(self, rev_a=None, rev_b=None):
@@ -779,7 +791,7 @@ def history(self, rev_a=None, rev_b=None):
779791
404,
780792
)
781793
if rev_a is not None and rev_b is not None and rev_a != rev_b:
782-
return self.diff(rev_a=rev_a, rev_b=rev_b)
794+
return redirect(url_for("diff", path=self.pagepath, rev_a=rev_a, rev_b=rev_b))
783795

784796
log = []
785797
for i,orig_entry in enumerate(orig_log):

tests/test_otterwiki.py

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ def test_blame_and_history_and_diff(test_client):
280280
rv = test_client.post(
281281
"/{}/history".format(pagename),
282282
data={"rev_a": revision[1], "rev_b": revision[0]},
283+
follow_redirects=True,
283284
)
284285
assert rv.status_code == 200
285286
html = rv.data.decode()

0 commit comments

Comments
 (0)