Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions templates/repo/diff/blob_excerpt.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
</td>
{{else}}
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
{{- $leftAnchor := Iif $line.LeftIdx (printf "diff-%sL%d" $.FileNameHash $line.LeftIdx) "" -}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span{{if $leftAnchor}} id="{{$leftAnchor}}"{{end}}></span></td>
<td class="lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
<td class="lines-code lines-code-old">
Expand All @@ -27,7 +28,8 @@
<code class="code-inner"></code>
{{- end -}}
</td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
{{- $rightAnchor := Iif $line.RightIdx (printf "diff-%sR%d" $.FileNameHash $line.RightIdx) "" -}}
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span{{if $rightAnchor}} id="{{$rightAnchor}}"{{end}}></span></td>
<td class="lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
<td class="lines-code lines-code-new">
Expand Down Expand Up @@ -65,8 +67,10 @@
{{if eq .GetType 4}}
<td colspan="2" class="lines-num">{{$line.RenderBlobExcerptButtons $.FileNameHash $diffBlobExcerptData}}</td>
{{else}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
{{- $leftAnchor := Iif $line.LeftIdx (printf "diff-%sL%d" $.FileNameHash $line.LeftIdx) "" -}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span{{if $leftAnchor}} id="{{$leftAnchor}}"{{end}}></span></td>
{{- $rightAnchor := Iif $line.RightIdx (printf "diff-%sR%d" $.FileNameHash $line.RightIdx) "" -}}
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span{{if $rightAnchor}} id="{{$rightAnchor}}"{{end}}></span></td>
{{end}}
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
<td class="lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
Expand Down
12 changes: 8 additions & 4 deletions templates/repo/diff/section_split.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
{{$match := index $section.Lines $line.Match}}
{{- $leftDiff := ""}}{{if $line.LeftIdx}}{{$leftDiff = $section.GetComputedInlineDiffFor $line ctx.Locale}}{{end}}
{{- $rightDiff := ""}}{{if $match.RightIdx}}{{$rightDiff = $section.GetComputedInlineDiffFor $match ctx.Locale}}{{end}}
<td class="lines-num lines-num-old del-code" data-line-num="{{$line.LeftIdx}}"><span rel="diff-{{$file.NameHash}}L{{$line.LeftIdx}}"></span></td>
{{- $leftAnchor := Iif $line.LeftIdx (printf "diff-%sL%d" $file.NameHash $line.LeftIdx) "" -}}
<td class="lines-num lines-num-old del-code" data-line-num="{{$line.LeftIdx}}"><span{{if $leftAnchor}} id="{{$leftAnchor}}"{{end}}></span></td>
<td class="lines-escape del-code lines-escape-old">{{if $line.LeftIdx}}{{if $leftDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $leftDiff}}"></button>{{end}}{{end}}</td>
<td class="lines-type-marker lines-type-marker-old del-code"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
<td class="lines-code lines-code-old del-code">
Expand All @@ -39,7 +40,8 @@
<code class="code-inner"></code>
{{- end -}}
</td>
<td class="lines-num lines-num-new add-code" data-line-num="{{if $match.RightIdx}}{{$match.RightIdx}}{{end}}"><span rel="{{if $match.RightIdx}}diff-{{$file.NameHash}}R{{$match.RightIdx}}{{end}}"></span></td>
{{- $matchRightAnchor := Iif $match.RightIdx (printf "diff-%sR%d" $file.NameHash $match.RightIdx) "" -}}
<td class="lines-num lines-num-new add-code" data-line-num="{{if $match.RightIdx}}{{$match.RightIdx}}{{end}}"><span{{if $matchRightAnchor}} id="{{$matchRightAnchor}}"{{end}}></span></td>
<td class="lines-escape add-code lines-escape-new">{{if $match.RightIdx}}{{if $rightDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $rightDiff}}"></button>{{end}}{{end}}</td>
<td class="lines-type-marker lines-type-marker-new add-code">{{if $match.RightIdx}}<span class="tw-font-mono" data-type-marker="{{$match.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-new add-code">
Expand All @@ -56,7 +58,8 @@
</td>
{{else}}
{{$inlineDiff := $section.GetComputedInlineDiffFor $line ctx.Locale}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$file.NameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
{{- $leftAnchor := Iif $line.LeftIdx (printf "diff-%sL%d" $file.NameHash $line.LeftIdx) "" -}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span{{if $leftAnchor}} id="{{$leftAnchor}}"{{end}}></span></td>
<td class="lines-escape lines-escape-old">{{if $line.LeftIdx}}{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}{{end}}</td>
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-old">
Expand All @@ -71,7 +74,8 @@
<code class="code-inner"></code>
{{- end -}}
</td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$file.NameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
{{- $rightAnchor := Iif $line.RightIdx (printf "diff-%sR%d" $file.NameHash $line.RightIdx) "" -}}
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span{{if $rightAnchor}} id="{{$rightAnchor}}"{{end}}></span></td>
<td class="lines-escape lines-escape-new">{{if $line.RightIdx}}{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}{{end}}</td>
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-new">
Expand Down
6 changes: 4 additions & 2 deletions templates/repo/diff/section_unified.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
<td colspan="2" class="lines-num"></td>
{{end}}
{{else}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$file.NameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$file.NameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
{{- $leftAnchor := Iif $line.LeftIdx (printf "diff-%sL%d" $file.NameHash $line.LeftIdx) "" -}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span{{if $leftAnchor}} id="{{$leftAnchor}}"{{end}}></span></td>
{{- $rightAnchor := Iif $line.RightIdx (printf "diff-%sR%d" $file.NameHash $line.RightIdx) "" -}}
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span{{if $rightAnchor}} id="{{$rightAnchor}}"{{end}}></span></td>
{{end}}
{{$inlineDiff := $section.GetComputedInlineDiffFor $line ctx.Locale -}}
<td class="lines-escape">
Expand Down
34 changes: 34 additions & 0 deletions web_src/css/repo.css
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,14 @@ td .commit-summary {
text-align: right;
}

.repository .diff-file-box .code-diff .lines-num[data-line-num] {
cursor: pointer;
}

.repository .diff-file-box .code-diff .lines-num[data-line-num]:hover {
color: var(--color-text-dark);
}

.repository .diff-file-box .code-diff tbody tr .lines-type-marker {
width: 10px;
min-width: 10px;
Expand All @@ -997,6 +1005,32 @@ td .commit-summary {
display: inline-block;
}

.repository .diff-file-box .code-diff tr.active .lines-num,
.repository .diff-file-box .code-diff tr.active .lines-escape,
.repository .diff-file-box .code-diff tr.active .lines-type-marker,
.repository .diff-file-box .code-diff tr.active .lines-code {
background: var(--color-highlight-bg);
}

.repository .diff-file-box .code-diff tr.active .lines-num {
position: relative;
}

.repository .diff-file-box .code-diff tr.active .lines-num::after {
display: none;
}

.repository .diff-file-box .code-diff tr.active .lines-num:first-of-type::after {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 2px;
background: var(--color-highlight-fg);
display: block;
}

.repository .diff-file-box .code-diff-split .tag-code .lines-code code.code-inner {
padding-left: 10px !important;
}
Expand Down
47 changes: 44 additions & 3 deletions web_src/js/features/file-fold.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,60 @@
import {svg} from '../svg.ts';

function parseTransitionValue(value: string): number {
let max = 0;
for (const current of value.split(',')) {
const trimmed = current.trim();
if (!trimmed) continue;
const isMs = trimmed.endsWith('ms');
const numericPortion = Number.parseFloat(trimmed.replace(/ms|s$/u, ''));
if (Number.isNaN(numericPortion)) continue;
const duration = numericPortion * (isMs ? 1 : 1000);
max = Math.max(max, duration);
}
return max;
}

function waitForTransitionEnd(element: Element): Promise<void> {
if (!(element instanceof HTMLElement)) return Promise.resolve();
const transitionTarget = element.querySelector<HTMLElement>('.diff-file-body') ?? element;
const styles = window.getComputedStyle(transitionTarget);
const transitionDuration = parseTransitionValue(styles.transitionDuration);
const transitionDelay = parseTransitionValue(styles.transitionDelay);
const total = transitionDuration + transitionDelay;
if (total === 0) return Promise.resolve();

return new Promise((resolve) => {
let resolved = false;
function cleanup() {
if (resolved) return;
resolved = true;
transitionTarget.removeEventListener('transitionend', onTransitionEnd);
resolve();
}
function onTransitionEnd(event: TransitionEvent) {
if (event.target !== transitionTarget) return;
cleanup();
}
transitionTarget.addEventListener('transitionend', onTransitionEnd);
window.setTimeout(cleanup, total + 50);
});
}

// Hides the file if newFold is true, and shows it otherwise. The actual hiding is performed using CSS.
//
// The fold arrow is the icon displayed on the upper left of the file box, especially intended for components having the 'fold-file' class.
// The file content box is the box that should be hidden or shown, especially intended for components having the 'file-content' class.
//
export function setFileFolding(fileContentBox: Element, foldArrow: HTMLElement, newFold: boolean) {
export function setFileFolding(fileContentBox: Element, foldArrow: HTMLElement, newFold: boolean): Promise<void> {
foldArrow.innerHTML = svg(`octicon-chevron-${newFold ? 'right' : 'down'}`, 18);
fileContentBox.setAttribute('data-folded', String(newFold));
if (newFold && fileContentBox.getBoundingClientRect().top < 0) {
fileContentBox.scrollIntoView();
}
return waitForTransitionEnd(fileContentBox);
}

// Like `setFileFolding`, except that it automatically inverts the current file folding state.
export function invertFileFolding(fileContentBox:HTMLElement, foldArrow: HTMLElement) {
setFileFolding(fileContentBox, foldArrow, fileContentBox.getAttribute('data-folded') !== 'true');
export function invertFileFolding(fileContentBox:HTMLElement, foldArrow: HTMLElement): Promise<void> {
return setFileFolding(fileContentBox, foldArrow, fileContentBox.getAttribute('data-folded') !== 'true');
}
10 changes: 1 addition & 9 deletions web_src/js/features/repo-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@ import {createTippy} from '../modules/tippy.ts';
import {toAbsoluteUrl} from '../utils.ts';
import {addDelegatedEventListener} from '../utils/dom.ts';

function changeHash(hash: string) {
if (window.history.pushState) {
window.history.pushState(null, '', hash);
} else {
window.location.hash = hash;
}
}

// it selects the code lines defined by range: `L1-L3` (3 lines) or `L2` (singe line)
function selectRange(range: string): Element | null {
for (const el of document.querySelectorAll('.code-view tr.active')) el.classList.remove('active');
Expand Down Expand Up @@ -65,7 +57,7 @@ function selectRange(range: string): Element | null {
for (let i = startLineNum - 1; i <= stopLineNum - 1 && i < elLineNums.length; i++) {
elLineNums[i].closest('tr')!.classList.add('active');
}
changeHash(`#${range}`);
window.history.replaceState(null, '', `#${range}`);
updateIssueHref(range);
updateViewGitBlameFragment(range);
updateCopyPermalinkUrl(range);
Expand Down
86 changes: 86 additions & 0 deletions web_src/js/features/repo-diff-selection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {applyDiffLineSelection} from './repo-diff-selection.ts';

function createDiffRow(tbody: HTMLTableSectionElement, options: {id?: string, lineType?: string} = {}) {
const tr = document.createElement('tr');
if (options.lineType) tr.setAttribute('data-line-type', options.lineType);

const numberCell = document.createElement('td');
numberCell.classList.add('lines-num');
const span = document.createElement('span');
if (options.id) span.id = options.id;
numberCell.append(span);
tr.append(numberCell);

tr.append(document.createElement('td'));
tbody.append(tr);
return tr;
}

describe('applyDiffLineSelection', () => {
beforeEach(() => {
document.body.innerHTML = '';
});

test('selects contiguous diff rows, skips expansion rows, and clears previous selection', () => {
const fragment = 'diff-selection';

const otherBox = document.createElement('div');
const otherTable = document.createElement('table');
otherTable.classList.add('code-diff');
const otherTbody = document.createElement('tbody');
const staleActiveRow = document.createElement('tr');
staleActiveRow.classList.add('active');
otherTbody.append(staleActiveRow);
otherTable.append(otherTbody);
otherBox.append(otherTable);

const container = document.createElement('div');
container.classList.add('diff-file-box');
const table = document.createElement('table');
table.classList.add('code-diff');
const tbody = document.createElement('tbody');
table.append(tbody);
container.append(table);

const rows = [
createDiffRow(tbody, {id: `${fragment}L1`}),
createDiffRow(tbody),
createDiffRow(tbody, {lineType: '4'}),
createDiffRow(tbody),
createDiffRow(tbody, {id: `${fragment}R5`}),
createDiffRow(tbody),
];

document.body.append(otherBox, container);

const range = {fragment, startSide: 'L' as const, startLine: 1, endSide: 'R' as const, endLine: 5};
const applied = applyDiffLineSelection(container, range);

expect(applied).toBe(true);
expect(rows[0].classList.contains('active')).toBe(true);
expect(rows[1].classList.contains('active')).toBe(true);
expect(rows[2].classList.contains('active')).toBe(false);
expect(rows[3].classList.contains('active')).toBe(true);
expect(rows[4].classList.contains('active')).toBe(true);
expect(rows[5].classList.contains('active')).toBe(false);
expect(staleActiveRow.classList.contains('active')).toBe(false);
});

test('returns false when either anchor is missing', () => {
const fragment = 'diff-missing';
const container = document.createElement('div');
container.classList.add('diff-file-box');
const table = document.createElement('table');
table.classList.add('code-diff');
const tbody = document.createElement('tbody');
table.append(tbody);
container.append(table);
document.body.append(container);

createDiffRow(tbody, {id: `${fragment}L1`});

const applied = applyDiffLineSelection(container, {fragment, startSide: 'L' as const, startLine: 1, endSide: 'R' as const, endLine: 2});
expect(applied).toBe(false);
expect(container.querySelectorAll('tr.active').length).toBe(0);
});
});
Loading