|
|
<%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
|
|
|
|
|
|
<%def name="diff_line_anchor(commit, filename, line, type)"><%
|
|
|
return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
|
|
|
%></%def>
|
|
|
|
|
|
<%def name="action_class(action)">
|
|
|
<%
|
|
|
return {
|
|
|
'-': 'cb-deletion',
|
|
|
'+': 'cb-addition',
|
|
|
' ': 'cb-context',
|
|
|
}.get(action, 'cb-empty')
|
|
|
%>
|
|
|
</%def>
|
|
|
|
|
|
<%def name="op_class(op_id)">
|
|
|
<%
|
|
|
return {
|
|
|
DEL_FILENODE: 'deletion', # file deleted
|
|
|
BIN_FILENODE: 'warning' # binary diff hidden
|
|
|
}.get(op_id, 'addition')
|
|
|
%>
|
|
|
</%def>
|
|
|
|
|
|
|
|
|
|
|
|
<%def name="render_diffset(diffset, commit=None,
|
|
|
|
|
|
# collapse all file diff entries when there are more than this amount of files in the diff
|
|
|
collapse_when_files_over=20,
|
|
|
|
|
|
# collapse lines in the diff when more than this amount of lines changed in the file diff
|
|
|
lines_changed_limit=500,
|
|
|
|
|
|
# add a ruler at to the output
|
|
|
ruler_at_chars=0,
|
|
|
|
|
|
# show inline comments
|
|
|
use_comments=False,
|
|
|
|
|
|
# disable new comments
|
|
|
disable_new_comments=False,
|
|
|
|
|
|
# special file-comments that were deleted in previous versions
|
|
|
# it's used for showing outdated comments for deleted files in a PR
|
|
|
deleted_files_comments=None,
|
|
|
|
|
|
# for cache purpose
|
|
|
inline_comments=None,
|
|
|
|
|
|
)">
|
|
|
%if use_comments:
|
|
|
<div id="cb-comments-inline-container-template" class="js-template">
|
|
|
${inline_comments_container([], inline_comments)}
|
|
|
</div>
|
|
|
<div class="js-template" id="cb-comment-inline-form-template">
|
|
|
<div class="comment-inline-form ac">
|
|
|
|
|
|
%if c.rhodecode_user.username != h.DEFAULT_USER:
|
|
|
## render template for inline comments
|
|
|
${commentblock.comment_form(form_type='inline')}
|
|
|
%else:
|
|
|
${h.form('', class_='inline-form comment-form-login', method='get')}
|
|
|
<div class="pull-left">
|
|
|
<div class="comment-help pull-right">
|
|
|
${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="comment-button pull-right">
|
|
|
<button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
|
|
|
${_('Cancel')}
|
|
|
</button>
|
|
|
</div>
|
|
|
<div class="clearfix"></div>
|
|
|
${h.end_form()}
|
|
|
%endif
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
%endif
|
|
|
<%
|
|
|
collapse_all = len(diffset.files) > collapse_when_files_over
|
|
|
%>
|
|
|
|
|
|
%if c.user_session_attrs["diffmode"] == 'sideside':
|
|
|
<style>
|
|
|
.wrapper {
|
|
|
max-width: 1600px !important;
|
|
|
}
|
|
|
</style>
|
|
|
%endif
|
|
|
|
|
|
%if ruler_at_chars:
|
|
|
<style>
|
|
|
.diff table.cb .cb-content:after {
|
|
|
content: "";
|
|
|
border-left: 1px solid blue;
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
height: 18px;
|
|
|
opacity: .2;
|
|
|
z-index: 10;
|
|
|
//## +5 to account for diff action (+/-)
|
|
|
left: ${ruler_at_chars + 5}ch;
|
|
|
</style>
|
|
|
%endif
|
|
|
|
|
|
<div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
|
|
|
<div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
|
|
|
%if commit:
|
|
|
<div class="pull-right">
|
|
|
<a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
|
|
|
${_('Browse Files')}
|
|
|
</a>
|
|
|
</div>
|
|
|
%endif
|
|
|
<h2 class="clearinner">
|
|
|
## invidual commit
|
|
|
% if commit:
|
|
|
<a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=diffset.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a> -
|
|
|
${h.age_component(commit.date)}
|
|
|
% if diffset.limited_diff:
|
|
|
- ${_('The requested commit is too big and content was truncated.')}
|
|
|
${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
|
|
|
<a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
|
|
|
% elif hasattr(c, 'commit_ranges') and len(c.commit_ranges) > 1:
|
|
|
## compare diff, has no file-selector and we want to show stats anyway
|
|
|
${_ungettext('{num} file changed: {linesadd} inserted, ''{linesdel} deleted',
|
|
|
'{num} files changed: {linesadd} inserted, {linesdel} deleted', diffset.changed_files) \
|
|
|
.format(num=diffset.changed_files, linesadd=diffset.lines_added, linesdel=diffset.lines_deleted)}
|
|
|
% endif
|
|
|
% else:
|
|
|
## pull requests/compare
|
|
|
${_('File Changes')}
|
|
|
% endif
|
|
|
|
|
|
</h2>
|
|
|
</div>
|
|
|
|
|
|
%if diffset.has_hidden_changes:
|
|
|
<p class="empty_data">${_('Some changes may be hidden')}</p>
|
|
|
%elif not diffset.files:
|
|
|
<p class="empty_data">${_('No files')}</p>
|
|
|
%endif
|
|
|
|
|
|
<div class="filediffs">
|
|
|
|
|
|
## initial value could be marked as False later on
|
|
|
<% over_lines_changed_limit = False %>
|
|
|
%for i, filediff in enumerate(diffset.files):
|
|
|
|
|
|
<%
|
|
|
lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
|
|
|
over_lines_changed_limit = lines_changed > lines_changed_limit
|
|
|
%>
|
|
|
## anchor with support of sticky header
|
|
|
<div class="anchor" id="a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></div>
|
|
|
|
|
|
<input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox" onchange="updateSticky();">
|
|
|
<div
|
|
|
class="filediff"
|
|
|
data-f-path="${filediff.patch['filename']}"
|
|
|
data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}"
|
|
|
>
|
|
|
<label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
|
|
|
<div class="filediff-collapse-indicator"></div>
|
|
|
${diff_ops(filediff)}
|
|
|
</label>
|
|
|
|
|
|
${diff_menu(filediff, use_comments=use_comments)}
|
|
|
<table data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
|
|
|
|
|
|
## new/deleted/empty content case
|
|
|
% if not filediff.hunks:
|
|
|
## Comment container, on "fakes" hunk that contains all data to render comments
|
|
|
${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
|
|
|
% endif
|
|
|
|
|
|
%if filediff.limited_diff:
|
|
|
<tr class="cb-warning cb-collapser">
|
|
|
<td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
|
|
|
${_('The requested commit is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
|
|
|
</td>
|
|
|
</tr>
|
|
|
%else:
|
|
|
%if over_lines_changed_limit:
|
|
|
<tr class="cb-warning cb-collapser">
|
|
|
<td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
|
|
|
${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
|
|
|
<a href="#" class="cb-expand"
|
|
|
onclick="$(this).closest('table').removeClass('cb-collapsed'); updateSticky(); return false;">${_('Show them')}
|
|
|
</a>
|
|
|
<a href="#" class="cb-collapse"
|
|
|
onclick="$(this).closest('table').addClass('cb-collapsed'); updateSticky(); return false;">${_('Hide them')}
|
|
|
</a>
|
|
|
</td>
|
|
|
</tr>
|
|
|
%endif
|
|
|
%endif
|
|
|
|
|
|
% for hunk in filediff.hunks:
|
|
|
<tr class="cb-hunk">
|
|
|
<td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
|
|
|
## TODO: dan: add ajax loading of more context here
|
|
|
## <a href="#">
|
|
|
<i class="icon-more"></i>
|
|
|
## </a>
|
|
|
</td>
|
|
|
<td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
|
|
|
@@
|
|
|
-${hunk.source_start},${hunk.source_length}
|
|
|
+${hunk.target_start},${hunk.target_length}
|
|
|
${hunk.section_header}
|
|
|
</td>
|
|
|
</tr>
|
|
|
${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments)}
|
|
|
% endfor
|
|
|
|
|
|
<% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
|
|
|
|
|
|
## outdated comments that do not fit into currently displayed lines
|
|
|
% for lineno, comments in unmatched_comments.items():
|
|
|
|
|
|
%if c.user_session_attrs["diffmode"] == 'unified':
|
|
|
% if loop.index == 0:
|
|
|
<tr class="cb-hunk">
|
|
|
<td colspan="3"></td>
|
|
|
<td>
|
|
|
<div>
|
|
|
${_('Unmatched inline comments below')}
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
|
% endif
|
|
|
<tr class="cb-line">
|
|
|
<td class="cb-data cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-content cb-context">
|
|
|
${inline_comments_container(comments, inline_comments)}
|
|
|
</td>
|
|
|
</tr>
|
|
|
%elif c.user_session_attrs["diffmode"] == 'sideside':
|
|
|
% if loop.index == 0:
|
|
|
<tr class="cb-comment-info">
|
|
|
<td colspan="2"></td>
|
|
|
<td class="cb-line">
|
|
|
<div>
|
|
|
${_('Unmatched inline comments below')}
|
|
|
</div>
|
|
|
</td>
|
|
|
<td colspan="2"></td>
|
|
|
<td class="cb-line">
|
|
|
<div>
|
|
|
${_('Unmatched comments below')}
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
|
% endif
|
|
|
<tr class="cb-line">
|
|
|
<td class="cb-data cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-content cb-context">
|
|
|
% if lineno.startswith('o'):
|
|
|
${inline_comments_container(comments, inline_comments)}
|
|
|
% endif
|
|
|
</td>
|
|
|
|
|
|
<td class="cb-data cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-content cb-context">
|
|
|
% if lineno.startswith('n'):
|
|
|
${inline_comments_container(comments, inline_comments)}
|
|
|
% endif
|
|
|
</td>
|
|
|
</tr>
|
|
|
%endif
|
|
|
|
|
|
% endfor
|
|
|
|
|
|
</table>
|
|
|
</div>
|
|
|
%endfor
|
|
|
|
|
|
## outdated comments that are made for a file that has been deleted
|
|
|
% for filename, comments_dict in (deleted_files_comments or {}).items():
|
|
|
<%
|
|
|
display_state = 'display: none'
|
|
|
open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
|
|
|
if open_comments_in_file:
|
|
|
display_state = ''
|
|
|
%>
|
|
|
<div class="filediffs filediff-outdated" style="${display_state}">
|
|
|
<input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox" onchange="updateSticky();">
|
|
|
<div class="filediff" data-f-path="${filename}" id="a_${h.FID(filediff.raw_id, filename)}">
|
|
|
<label for="filediff-collapse-${id(filename)}" class="filediff-heading">
|
|
|
<div class="filediff-collapse-indicator"></div>
|
|
|
<span class="pill">
|
|
|
## file was deleted
|
|
|
<strong>${filename}</strong>
|
|
|
</span>
|
|
|
<span class="pill-group" style="float: left">
|
|
|
## file op, doesn't need translation
|
|
|
<span class="pill" op="removed">removed in this version</span>
|
|
|
</span>
|
|
|
<a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filename)}">¶</a>
|
|
|
<span class="pill-group" style="float: right">
|
|
|
<span class="pill" op="deleted">-${comments_dict['stats']}</span>
|
|
|
</span>
|
|
|
</label>
|
|
|
|
|
|
<table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
|
|
|
<tr>
|
|
|
% if c.user_session_attrs["diffmode"] == 'unified':
|
|
|
<td></td>
|
|
|
%endif
|
|
|
|
|
|
<td></td>
|
|
|
<td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
|
|
|
${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
|
|
|
</td>
|
|
|
</tr>
|
|
|
%if c.user_session_attrs["diffmode"] == 'unified':
|
|
|
<tr class="cb-line">
|
|
|
<td class="cb-data cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-content cb-context">
|
|
|
${inline_comments_container(comments_dict['comments'], inline_comments)}
|
|
|
</td>
|
|
|
</tr>
|
|
|
%elif c.user_session_attrs["diffmode"] == 'sideside':
|
|
|
<tr class="cb-line">
|
|
|
<td class="cb-data cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-content cb-context"></td>
|
|
|
|
|
|
<td class="cb-data cb-context"></td>
|
|
|
<td class="cb-lineno cb-context"></td>
|
|
|
<td class="cb-content cb-context">
|
|
|
${inline_comments_container(comments_dict['comments'], inline_comments)}
|
|
|
</td>
|
|
|
</tr>
|
|
|
%endif
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
% endfor
|
|
|
|
|
|
</div>
|
|
|
</div>
|
|
|
</%def>
|
|
|
|
|
|
<%def name="diff_ops(filediff)">
|
|
|
<%
|
|
|
from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|
|
MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
|
|
|
%>
|
|
|
<span class="pill">
|
|
|
%if filediff.source_file_path and filediff.target_file_path:
|
|
|
%if filediff.source_file_path != filediff.target_file_path:
|
|
|
## file was renamed, or copied
|
|
|
%if RENAMED_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<strong>${filediff.target_file_path}</strong> ⬅ <del>${filediff.source_file_path}</del>
|
|
|
<% final_path = filediff.target_file_path %>
|
|
|
%elif COPIED_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<strong>${filediff.target_file_path}</strong> ⬅ ${filediff.source_file_path}
|
|
|
<% final_path = filediff.target_file_path %>
|
|
|
%endif
|
|
|
%else:
|
|
|
## file was modified
|
|
|
<strong>${filediff.source_file_path}</strong>
|
|
|
<% final_path = filediff.source_file_path %>
|
|
|
%endif
|
|
|
%else:
|
|
|
%if filediff.source_file_path:
|
|
|
## file was deleted
|
|
|
<strong>${filediff.source_file_path}</strong>
|
|
|
<% final_path = filediff.source_file_path %>
|
|
|
%else:
|
|
|
## file was added
|
|
|
<strong>${filediff.target_file_path}</strong>
|
|
|
<% final_path = filediff.target_file_path %>
|
|
|
%endif
|
|
|
%endif
|
|
|
<i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
|
|
|
</span>
|
|
|
## anchor link
|
|
|
<a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}">¶</a>
|
|
|
|
|
|
<span class="pill-group" style="float: right">
|
|
|
|
|
|
## ops pills
|
|
|
%if filediff.limited_diff:
|
|
|
<span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
|
|
|
%endif
|
|
|
|
|
|
%if NEW_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="created">created</span>
|
|
|
%if filediff['target_mode'].startswith('120'):
|
|
|
<span class="pill" op="symlink">symlink</span>
|
|
|
%else:
|
|
|
<span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
|
|
|
%endif
|
|
|
%endif
|
|
|
|
|
|
%if RENAMED_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="renamed">renamed</span>
|
|
|
%endif
|
|
|
|
|
|
%if COPIED_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="copied">copied</span>
|
|
|
%endif
|
|
|
|
|
|
%if DEL_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="removed">removed</span>
|
|
|
%endif
|
|
|
|
|
|
%if CHMOD_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="mode">
|
|
|
${nice_mode(filediff['source_mode'])} âž¡ ${nice_mode(filediff['target_mode'])}
|
|
|
</span>
|
|
|
%endif
|
|
|
|
|
|
%if BIN_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="binary">binary</span>
|
|
|
%if MOD_FILENODE in filediff.patch['stats']['ops']:
|
|
|
<span class="pill" op="modified">modified</span>
|
|
|
%endif
|
|
|
%endif
|
|
|
|
|
|
<span class="pill" op="added">${('+' if filediff.patch['stats']['added'] else '')}${filediff.patch['stats']['added']}</span>
|
|
|
<span class="pill" op="deleted">${((h.safe_int(filediff.patch['stats']['deleted']) or 0) * -1)}</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</%def>
|
|
|
|
|
|
<%def name="nice_mode(filemode)">
|
|
|
${(filemode.startswith('100') and filemode[3:] or filemode)}
|
|
|
</%def>
|
|
|
|
|
|
<%def name="diff_menu(filediff, use_comments=False)">
|
|
|
<div class="filediff-menu">
|
|
|
|
|
|
%if filediff.diffset.source_ref:
|
|
|
|
|
|
## FILE BEFORE CHANGES
|
|
|
%if filediff.operation in ['D', 'M']:
|
|
|
<a
|
|
|
class="tooltip"
|
|
|
href="${h.route_path('repo_files',repo_name=filediff.diffset.target_repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
|
|
|
title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
|
|
|
>
|
|
|
${_('Show file before')}
|
|
|
</a> |
|
|
|
%else:
|
|
|
<span
|
|
|
class="tooltip"
|
|
|
title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
|
|
|
>
|
|
|
${_('Show file before')}
|
|
|
</span> |
|
|
|
%endif
|
|
|
|
|
|
## FILE AFTER CHANGES
|
|
|
%if filediff.operation in ['A', 'M']:
|
|
|
<a
|
|
|
class="tooltip"
|
|
|
href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
|
|
|
title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
|
|
|
>
|
|
|
${_('Show file after')}
|
|
|
</a>
|
|
|
%else:
|
|
|
<span
|
|
|
class="tooltip"
|
|
|
title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
|
|
|
>
|
|
|
${_('Show file after')}
|
|
|
</span>
|
|
|
%endif
|
|
|
|
|
|
% if use_comments:
|
|
|
|
|
|
|
<a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
|
|
|
<span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
|
|
|
</a>
|
|
|
% endif
|
|
|
|
|
|
%endif
|
|
|
|
|
|
</div>
|
|
|
</%def>
|
|
|
|
|
|
|
|
|
<%def name="inline_comments_container(comments, inline_comments)">
|
|
|
<div class="inline-comments">
|
|
|
%for comment in comments:
|
|
|
${commentblock.comment_block(comment, inline=True)}
|
|
|
%endfor
|
|
|
% if comments and comments[-1].outdated:
|
|
|
<span class="btn btn-secondary cb-comment-add-button comment-outdated}"
|
|
|
style="display: none;}">
|
|
|
${_('Add another comment')}
|
|
|
</span>
|
|
|
% else:
|
|
|
<span onclick="return Rhodecode.comments.createComment(this)"
|
|
|
class="btn btn-secondary cb-comment-add-button">
|
|
|
${_('Add another comment')}
|
|
|
</span>
|
|
|
% endif
|
|
|
|
|
|
</div>
|
|
|
</%def>
|
|
|
|
|
|
<%!
|
|
|
def get_comments_for(diff_type, comments, filename, line_version, line_number):
|
|
|
if hasattr(filename, 'unicode_path'):
|
|
|
filename = filename.unicode_path
|
|
|
|
|
|
if not isinstance(filename, basestring):
|
|
|
return None
|
|
|
|
|
|
line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
|
|
|
|
|
|
if comments and filename in comments:
|
|
|
file_comments = comments[filename]
|
|
|
if line_key in file_comments:
|
|
|
data = file_comments.pop(line_key)
|
|
|
return data
|
|
|
%>
|
|
|
|
|
|
<%def name="render_hunk_lines_sideside(filediff, hunk, use_comments=False, inline_comments=None)">
|
|
|
%for i, line in enumerate(hunk.sideside):
|
|
|
<%
|
|
|
old_line_anchor, new_line_anchor = None, None
|
|
|
|
|
|
if line.original.lineno:
|
|
|
old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, line.original.lineno, 'o')
|
|
|
if line.modified.lineno:
|
|
|
new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, line.modified.lineno, 'n')
|
|
|
%>
|
|
|
|
|
|
<tr class="cb-line">
|
|
|
<td class="cb-data ${action_class(line.original.action)}"
|
|
|
data-line-no="${line.original.lineno}"
|
|
|
>
|
|
|
<div>
|
|
|
|
|
|
<% line_old_comments = None %>
|
|
|
%if line.original.get_comment_args:
|
|
|
<% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %>
|
|
|
%endif
|
|
|
%if line_old_comments:
|
|
|
<% has_outdated = any([x.outdated for x in line_old_comments]) %>
|
|
|
% if has_outdated:
|
|
|
<i title="${_('comments including outdated')}:${len(line_old_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
|
|
|
% else:
|
|
|
<i title="${_('comments')}: ${len(line_old_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
|
|
|
% endif
|
|
|
%endif
|
|
|
</div>
|
|
|
</td>
|
|
|
<td class="cb-lineno ${action_class(line.original.action)}"
|
|
|
data-line-no="${line.original.lineno}"
|
|
|
%if old_line_anchor:
|
|
|
id="${old_line_anchor}"
|
|
|
%endif
|
|
|
>
|
|
|
%if line.original.lineno:
|
|
|
<a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
|
|
|
%endif
|
|
|
</td>
|
|
|
<td class="cb-content ${action_class(line.original.action)}"
|
|
|
data-line-no="o${line.original.lineno}"
|
|
|
>
|
|
|
%if use_comments and line.original.lineno:
|
|
|
${render_add_comment_button()}
|
|
|
%endif
|
|
|
<span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
|
|
|
|
|
|
%if use_comments and line.original.lineno and line_old_comments:
|
|
|
${inline_comments_container(line_old_comments, inline_comments)}
|
|
|
%endif
|
|
|
|
|
|
</td>
|
|
|
<td class="cb-data ${action_class(line.modified.action)}"
|
|
|
data-line-no="${line.modified.lineno}"
|
|
|
>
|
|
|
<div>
|
|
|
|
|
|
%if line.modified.get_comment_args:
|
|
|
<% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %>
|
|
|
%else:
|
|
|
<% line_new_comments = None%>
|
|
|
%endif
|
|
|
%if line_new_comments:
|
|
|
<% has_outdated = any([x.outdated for x in line_new_comments]) %>
|
|
|
% if has_outdated:
|
|
|
<i title="${_('comments including outdated')}:${len(line_new_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
|
|
|
% else:
|
|
|
<i title="${_('comments')}: ${len(line_new_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
|
|
|
% endif
|
|
|
%endif
|
|
|
</div>
|
|
|
</td>
|
|
|
<td class="cb-lineno ${action_class(line.modified.action)}"
|
|
|
data-line-no="${line.modified.lineno}"
|
|
|
%if new_line_anchor:
|
|
|
id="${new_line_anchor}"
|
|
|
%endif
|
|
|
>
|
|
|
%if line.modified.lineno:
|
|
|
<a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
|
|
|
%endif
|
|
|
</td>
|
|
|
<td class="cb-content ${action_class(line.modified.action)}"
|
|
|
data-line-no="n${line.modified.lineno}"
|
|
|
>
|
|
|
%if use_comments and line.modified.lineno:
|
|
|
${render_add_comment_button()}
|
|
|
%endif
|
|
|
<span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
|
|
|
%if use_comments and line.modified.lineno and line_new_comments:
|
|
|
${inline_comments_container(line_new_comments, inline_comments)}
|
|
|
%endif
|
|
|
</td>
|
|
|
</tr>
|
|
|
%endfor
|
|
|
</%def>
|
|
|
|
|
|
|
|
|
<%def name="render_hunk_lines_unified(filediff, hunk, use_comments=False, inline_comments=None)">
|
|
|
%for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
|
|
|
|
|
|
<%
|
|
|
old_line_anchor, new_line_anchor = None, None
|
|
|
if old_line_no:
|
|
|
old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, old_line_no, 'o')
|
|
|
if new_line_no:
|
|
|
new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, new_line_no, 'n')
|
|
|
%>
|
|
|
<tr class="cb-line">
|
|
|
<td class="cb-data ${action_class(action)}">
|
|
|
<div>
|
|
|
|
|
|
%if comments_args:
|
|
|
<% comments = get_comments_for('unified', inline_comments, *comments_args) %>
|
|
|
%else:
|
|
|
<% comments = None %>
|
|
|
%endif
|
|
|
|
|
|
% if comments:
|
|
|
<% has_outdated = any([x.outdated for x in comments]) %>
|
|
|
% if has_outdated:
|
|
|
<i title="${_('comments including outdated')}:${len(comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
|
|
|
% else:
|
|
|
<i title="${_('comments')}: ${len(comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
|
|
|
% endif
|
|
|
% endif
|
|
|
</div>
|
|
|
</td>
|
|
|
<td class="cb-lineno ${action_class(action)}"
|
|
|
data-line-no="${old_line_no}"
|
|
|
%if old_line_anchor:
|
|
|
id="${old_line_anchor}"
|
|
|
%endif
|
|
|
>
|
|
|
%if old_line_anchor:
|
|
|
<a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
|
|
|
%endif
|
|
|
</td>
|
|
|
<td class="cb-lineno ${action_class(action)}"
|
|
|
data-line-no="${new_line_no}"
|
|
|
%if new_line_anchor:
|
|
|
id="${new_line_anchor}"
|
|
|
%endif
|
|
|
>
|
|
|
%if new_line_anchor:
|
|
|
<a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
|
|
|
%endif
|
|
|
</td>
|
|
|
<td class="cb-content ${action_class(action)}"
|
|
|
data-line-no="${(new_line_no and 'n' or 'o')}${(new_line_no or old_line_no)}"
|
|
|
>
|
|
|
%if use_comments:
|
|
|
${render_add_comment_button()}
|
|
|
%endif
|
|
|
<span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
|
|
|
%if use_comments and comments:
|
|
|
${inline_comments_container(comments, inline_comments)}
|
|
|
%endif
|
|
|
</td>
|
|
|
</tr>
|
|
|
%endfor
|
|
|
</%def>
|
|
|
|
|
|
|
|
|
<%def name="render_hunk_lines(filediff, diff_mode, hunk, use_comments, inline_comments)">
|
|
|
% if diff_mode == 'unified':
|
|
|
${render_hunk_lines_unified(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments)}
|
|
|
% elif diff_mode == 'sideside':
|
|
|
${render_hunk_lines_sideside(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments)}
|
|
|
% else:
|
|
|
<tr class="cb-line">
|
|
|
<td>unknown diff mode</td>
|
|
|
</tr>
|
|
|
% endif
|
|
|
</%def>file changes
|
|
|
|
|
|
|
|
|
<%def name="render_add_comment_button()">
|
|
|
<button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
|
|
|
<span><i class="icon-comment"></i></span>
|
|
|
</button>
|
|
|
</%def>
|
|
|
|
|
|
<%def name="render_diffset_menu(diffset=None, range_diff_on=None)">
|
|
|
|
|
|
<div id="diff-file-sticky" class="diffset-menu clearinner">
|
|
|
## auto adjustable
|
|
|
<div class="sidebar__inner">
|
|
|
<div class="sidebar__bar">
|
|
|
<div class="pull-right">
|
|
|
<div class="btn-group">
|
|
|
|
|
|
## DIFF OPTIONS via Select2
|
|
|
<div class="pull-left">
|
|
|
${h.hidden('diff_menu')}
|
|
|
</div>
|
|
|
|
|
|
<a
|
|
|
class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-primary')} tooltip"
|
|
|
title="${h.tooltip(_('View side by side'))}"
|
|
|
href="${h.current_route_path(request, diffmode='sideside')}">
|
|
|
<span>${_('Side by Side')}</span>
|
|
|
</a>
|
|
|
|
|
|
<a
|
|
|
class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-primary')} tooltip"
|
|
|
title="${h.tooltip(_('View unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
|
|
|
<span>${_('Unified')}</span>
|
|
|
</a>
|
|
|
|
|
|
% if range_diff_on is True:
|
|
|
<a
|
|
|
title="${_('Turn off: Show the diff as commit range')}"
|
|
|
class="btn btn-primary"
|
|
|
href="${h.current_route_path(request, **{"range-diff":"0"})}">
|
|
|
<span>${_('Range Diff')}</span>
|
|
|
</a>
|
|
|
% elif range_diff_on is False:
|
|
|
<a
|
|
|
title="${_('Show the diff as commit range')}"
|
|
|
class="btn"
|
|
|
href="${h.current_route_path(request, **{"range-diff":"1"})}">
|
|
|
<span>${_('Range Diff')}</span>
|
|
|
</a>
|
|
|
% endif
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="pull-left">
|
|
|
<div class="btn-group">
|
|
|
<div class="pull-left">
|
|
|
${h.hidden('file_filter')}
|
|
|
</div>
|
|
|
<a
|
|
|
class="btn"
|
|
|
href="#"
|
|
|
onclick="$('input[class=filediff-collapse-state]').prop('checked', false); updateSticky(); return false">${_('Expand All Files')}</a>
|
|
|
<a
|
|
|
class="btn"
|
|
|
href="#"
|
|
|
onclick="$('input[class=filediff-collapse-state]').prop('checked', true); updateSticky(); return false">${_('Collapse All Files')}</a>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="fpath-placeholder">
|
|
|
<i class="icon-file-text"></i>
|
|
|
<strong class="fpath-placeholder-text">
|
|
|
Context file:
|
|
|
</strong>
|
|
|
</div>
|
|
|
<div class="sidebar_inner_shadow"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
% if diffset:
|
|
|
|
|
|
%if diffset.limited_diff:
|
|
|
<% file_placeholder = _ungettext('%(num)s file changed', '%(num)s files changed', diffset.changed_files) % {'num': diffset.changed_files} %>
|
|
|
%else:
|
|
|
<% file_placeholder = _ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted', '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}%>
|
|
|
%endif
|
|
|
## case on range-diff placeholder needs to be updated
|
|
|
% if range_diff_on is True:
|
|
|
<% file_placeholder = _('Disabled on range diff') %>
|
|
|
% endif
|
|
|
|
|
|
<script>
|
|
|
|
|
|
var feedFilesOptions = function (query, initialData) {
|
|
|
var data = {results: []};
|
|
|
var isQuery = typeof query.term !== 'undefined';
|
|
|
|
|
|
var section = _gettext('Changed files');
|
|
|
var filteredData = [];
|
|
|
|
|
|
//filter results
|
|
|
$.each(initialData.results, function (idx, value) {
|
|
|
|
|
|
if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
|
|
|
filteredData.push({
|
|
|
'id': this.id,
|
|
|
'text': this.text,
|
|
|
"ops": this.ops,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
data.results = filteredData;
|
|
|
|
|
|
query.callback(data);
|
|
|
};
|
|
|
|
|
|
var formatFileResult = function(result, container, query, escapeMarkup) {
|
|
|
return function(data, escapeMarkup) {
|
|
|
var container = '<div class="filelist" style="padding-right:100px">{0}</div>';
|
|
|
var tmpl = '<span style="margin-right:-50px"><strong>{0}</strong></span>'.format(escapeMarkup(data['text']));
|
|
|
var pill = '<span class="pill-group" style="float: right;margin-right: -100px">' +
|
|
|
'<span class="pill" op="added">{0}</span>' +
|
|
|
'<span class="pill" op="deleted">{1}</span>' +
|
|
|
'</span>'
|
|
|
;
|
|
|
var added = data['ops']['added'];
|
|
|
if (added === 0) {
|
|
|
// don't show +0
|
|
|
added = 0;
|
|
|
} else {
|
|
|
added = '+' + added;
|
|
|
}
|
|
|
|
|
|
var deleted = -1*data['ops']['deleted'];
|
|
|
|
|
|
tmpl += pill.format(added, deleted);
|
|
|
return container.format(tmpl);
|
|
|
|
|
|
}(result, escapeMarkup);
|
|
|
};
|
|
|
var preloadData = {
|
|
|
results: [
|
|
|
% for filediff in diffset.files:
|
|
|
{id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}",
|
|
|
text:"${filediff.patch['filename']}",
|
|
|
ops:${h.json.dumps(filediff.patch['stats'])|n}}${('' if loop.last else ',')}
|
|
|
% endfor
|
|
|
]
|
|
|
};
|
|
|
|
|
|
$(document).ready(function () {
|
|
|
|
|
|
var fileFilter = $("#file_filter").select2({
|
|
|
'dropdownAutoWidth': true,
|
|
|
'width': 'auto',
|
|
|
'placeholder': "${file_placeholder}",
|
|
|
containerCssClass: "drop-menu",
|
|
|
dropdownCssClass: "drop-menu-dropdown",
|
|
|
data: preloadData,
|
|
|
query: function(query) {
|
|
|
feedFilesOptions(query, preloadData);
|
|
|
},
|
|
|
formatResult: formatFileResult
|
|
|
});
|
|
|
% if range_diff_on is True:
|
|
|
fileFilter.select2("enable", false);
|
|
|
|
|
|
% endif
|
|
|
|
|
|
$("#file_filter").on('click', function (e) {
|
|
|
e.preventDefault();
|
|
|
var selected = $('#file_filter').select2('data');
|
|
|
var idSelector = "#"+selected.id;
|
|
|
window.location.hash = idSelector;
|
|
|
// expand the container if we quick-select the field
|
|
|
$(idSelector).next().prop('checked', false);
|
|
|
updateSticky()
|
|
|
});
|
|
|
|
|
|
var contextPrefix = _gettext('Context file: ');
|
|
|
## sticky sidebar
|
|
|
var sidebarElement = document.getElementById('diff-file-sticky');
|
|
|
sidebar = new StickySidebar(sidebarElement, {
|
|
|
topSpacing: 0,
|
|
|
bottomSpacing: 0,
|
|
|
innerWrapperSelector: '.sidebar__inner'
|
|
|
});
|
|
|
sidebarElement.addEventListener('affixed.static.stickySidebar', function () {
|
|
|
// reset our file so it's not holding new value
|
|
|
$('.fpath-placeholder-text').html(contextPrefix)
|
|
|
});
|
|
|
|
|
|
updateSticky = function () {
|
|
|
sidebar.updateSticky();
|
|
|
Waypoint.refreshAll();
|
|
|
};
|
|
|
|
|
|
var animateText = $.debounce(100, function(fPath, anchorId) {
|
|
|
// animate setting the text
|
|
|
var callback = function () {
|
|
|
$('.fpath-placeholder-text').animate({'opacity': 1.00}, 200)
|
|
|
$('.fpath-placeholder-text').html(contextPrefix + '<a href="#a_' + anchorId + '">' + fPath + '</a>')
|
|
|
};
|
|
|
$('.fpath-placeholder-text').animate({'opacity': 0.15}, 200, callback);
|
|
|
});
|
|
|
|
|
|
## dynamic file waypoints
|
|
|
var setFPathInfo = function(fPath, anchorId){
|
|
|
animateText(fPath, anchorId)
|
|
|
};
|
|
|
|
|
|
var codeBlock = $('.filediff');
|
|
|
// forward waypoint
|
|
|
codeBlock.waypoint(
|
|
|
function(direction) {
|
|
|
if (direction === "down"){
|
|
|
setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
|
|
|
}
|
|
|
}, {
|
|
|
offset: 70,
|
|
|
context: '.fpath-placeholder'
|
|
|
}
|
|
|
);
|
|
|
|
|
|
// backward waypoint
|
|
|
codeBlock.waypoint(
|
|
|
function(direction) {
|
|
|
if (direction === "up"){
|
|
|
setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
|
|
|
}
|
|
|
}, {
|
|
|
offset: function () {
|
|
|
return -this.element.clientHeight + 90
|
|
|
},
|
|
|
context: '.fpath-placeholder'
|
|
|
}
|
|
|
);
|
|
|
|
|
|
var preloadData = {
|
|
|
results: [
|
|
|
## Wide diff mode
|
|
|
{
|
|
|
id: 1,
|
|
|
text: _gettext('Toggle Wide Mode diff'),
|
|
|
action: function () {
|
|
|
updateSticky();
|
|
|
Rhodecode.comments.toggleWideMode(this);
|
|
|
return null;
|
|
|
},
|
|
|
url: null,
|
|
|
},
|
|
|
|
|
|
## Whitespace change
|
|
|
% if request.GET.get('ignorews', '') == '1':
|
|
|
{
|
|
|
id: 2,
|
|
|
text: _gettext('Show whitespace changes'),
|
|
|
action: function () {},
|
|
|
url: "${h.current_route_path(request, ignorews=0)|n}"
|
|
|
},
|
|
|
% else:
|
|
|
{
|
|
|
id: 2,
|
|
|
text: _gettext('Hide whitespace changes'),
|
|
|
action: function () {},
|
|
|
url: "${h.current_route_path(request, ignorews=1)|n}"
|
|
|
},
|
|
|
% endif
|
|
|
|
|
|
## FULL CONTEXT
|
|
|
% if request.GET.get('fullcontext', '') == '1':
|
|
|
{
|
|
|
id: 3,
|
|
|
text: _gettext('Hide full context diff'),
|
|
|
action: function () {},
|
|
|
url: "${h.current_route_path(request, fullcontext=0)|n}"
|
|
|
},
|
|
|
% else:
|
|
|
{
|
|
|
id: 3,
|
|
|
text: _gettext('Show full context diff'),
|
|
|
action: function () {},
|
|
|
url: "${h.current_route_path(request, fullcontext=1)|n}"
|
|
|
},
|
|
|
% endif
|
|
|
|
|
|
]
|
|
|
};
|
|
|
|
|
|
$("#diff_menu").select2({
|
|
|
minimumResultsForSearch: -1,
|
|
|
containerCssClass: "drop-menu",
|
|
|
dropdownCssClass: "drop-menu-dropdown",
|
|
|
dropdownAutoWidth: true,
|
|
|
data: preloadData,
|
|
|
placeholder: "${_('Diff Options')}",
|
|
|
});
|
|
|
$("#diff_menu").on('select2-selecting', function (e) {
|
|
|
e.choice.action();
|
|
|
if (e.choice.url !== null) {
|
|
|
window.location = e.choice.url
|
|
|
}
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
% endif
|
|
|
|
|
|
</%def>
|
|
|
|