Skip to content

Commit 7605184

Browse files
committed
Show tabs for deleted files
1 parent 45e65ee commit 7605184

File tree

4 files changed

+128
-50
lines changed

4 files changed

+128
-50
lines changed

webapp/public/js/domjudge.js

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,8 +1305,19 @@ function initScoreboardSubmissions() {
13051305
});
13061306
}
13071307

1308+
const enableButton = (btn, url) => {
1309+
btn.href = url;
1310+
btn.classList.remove('disabled');
1311+
btn.ariaDisabled=false;
1312+
};
1313+
1314+
const disableButton = (btn) => {
1315+
btn.classList.add('disabled');
1316+
btn.ariaDisabled=true;
1317+
}
1318+
13081319
const editors = [];
1309-
function initDiffEditor(editorId) {
1320+
function initDiffEditor(editorId, deletedFiles) {
13101321
const wrapper = $(`#${editorId}-wrapper`);
13111322

13121323
const initialTag = getDiffTag();
@@ -1339,14 +1350,14 @@ function initDiffEditor(editorId) {
13391350
if (rank) {
13401351
let url = new URL(download.href);
13411352
url.searchParams.set("fetch", rank);
1342-
download.href = url;
1353+
enableButton(download, url);
13431354

13441355
url = new URL(edit.href);
13451356
url.searchParams.set("rank", rank);
1346-
edit.href = url;
1357+
enableButton(edit, url);
13471358
} else {
1348-
download.href = "#";
1349-
edit.href = "#";
1359+
disableButton(download);
1360+
disableButton(edit);
13501361
}
13511362
};
13521363
wrapper.find(".nav").on('show.bs.tab', (e) => {
@@ -1365,37 +1376,6 @@ function initDiffEditor(editorId) {
13651376
let s = select[0];
13661377
return s.options[s.selectedIndex].value;
13671378
},
1368-
'updateIcon': (rank, icon) => {
1369-
const element = wrapper.find(".nav-link[data-rank]")[rank].querySelector('.fa-fw');
1370-
element.className = 'fas fa-fw fa-' + icon;
1371-
},
1372-
'renamedFrom': (rank, oldName) => {
1373-
const navItem = wrapper.find(".nav-link[data-rank]")[rank];
1374-
let renamed = navItem.querySelector('.renamed');
1375-
let arrow = navItem.querySelector('.fa-arrow-right');
1376-
if (oldName === undefined) {
1377-
if (renamed) {
1378-
navItem.removeChild(renamed);
1379-
}
1380-
if (arrow) {
1381-
navItem.removeChild(arrow);
1382-
}
1383-
return;
1384-
}
1385-
1386-
if (!renamed) {
1387-
renamed = document.createElement('span');
1388-
renamed.className = 'renamed';
1389-
navItem.insertBefore(renamed, navItem.childNodes[1]);
1390-
}
1391-
renamed.innerText = ` ${oldName} `;
1392-
1393-
if (!arrow) {
1394-
arrow = document.createElement('i');
1395-
arrow.className = 'fas fa-arrow-right';
1396-
navItem.insertBefore(arrow, navItem.childNodes[2]);
1397-
}
1398-
},
13991379
'onDiffModeChange': (f) => {
14001380
radios.change((e) => {
14011381
const diffMode = e.target.value;
@@ -1404,8 +1384,8 @@ function initDiffEditor(editorId) {
14041384
},
14051385
'onDiffSelectChange': (f) => {
14061386
select.change((e) => {
1407-
const submitId = e.target.value;
1408-
const noDiff = submitId === "";
1387+
const noDiff = e.target.value === "";
1388+
const submitId = parseInt(e.target.value);
14091389
f(submitId, noDiff);
14101390
});
14111391
}
@@ -1427,15 +1407,14 @@ function initDiffEditor(editorId) {
14271407
if (selected && selected.dataset.tag) {
14281408
setDiffTag(selected.dataset.tag);
14291409
}
1430-
1431-
// TODO: add tab panes for deleted source files.
14321410
};
1433-
updateSelect(select[0].value, select[0].value === "");
1411+
updateSelect(parseInt(select[0].value), select[0].value === "");
14341412
editor.onDiffSelectChange(updateSelect);
14351413
}
14361414

14371415
function initDiffEditorTab(editorId, diffId, rank, models, modifiedModel) {
14381416
const empty = monaco.editor.getModel(monaco.Uri.file("empty")) ?? monaco.editor.createModel("", undefined, monaco.Uri.file("empty"));
1417+
const navItem = document.getElementById(`${diffId}-link`);
14391418

14401419
const diffEditor = monaco.editor.createDiffEditor(
14411420
document.getElementById(diffId), {
@@ -1457,19 +1436,52 @@ function initDiffEditorTab(editorId, diffId, rank, models, modifiedModel) {
14571436
};
14581437
editors[editorId].onDiffModeChange(updateMode);
14591438

1439+
const renamedFrom = (oldName) => {
1440+
let renamed = navItem.querySelector('.renamed');
1441+
let arrow = navItem.querySelector('.fa-arrow-right');
1442+
if (oldName === undefined) {
1443+
if (renamed) {
1444+
navItem.removeChild(renamed);
1445+
}
1446+
if (arrow) {
1447+
navItem.removeChild(arrow);
1448+
}
1449+
return;
1450+
}
1451+
1452+
if (!renamed) {
1453+
renamed = document.createElement('span');
1454+
renamed.className = 'renamed';
1455+
navItem.insertBefore(renamed, navItem.childNodes[1]);
1456+
}
1457+
renamed.innerText = ` ${oldName} `;
1458+
1459+
if (!arrow) {
1460+
arrow = document.createElement('i');
1461+
arrow.className = 'fas fa-arrow-right';
1462+
navItem.insertBefore(arrow, navItem.childNodes[2]);
1463+
}
1464+
};
1465+
14601466
const updateSelect = (submitId, noDiff) => {
1467+
const exists = submitId in models;
1468+
if (rank === undefined) {
1469+
document.getElementById(diffId).parentElement.style.display = exists ? 'block' : 'none';
1470+
navItem.style.display = exists ? 'block' : 'none';
1471+
if (!exists) return;
1472+
}
1473+
14611474
const model = models[submitId] ??= {'model': empty};
14621475
if (!noDiff) {
14631476
if (!model['model']) {
1464-
// TODO: show source code instead of diff to empty file?
14651477
model['model'] = monaco.editor.createModel(model['source'], undefined, monaco.Uri.file("test/" + submitId + "/" + model['filename']));
14661478
}
14671479
}
14681480

14691481
if (noDiff || !model['renamedFrom']) {
1470-
editors[editorId].renamedFrom(rank, undefined);
1482+
renamedFrom(undefined);
14711483
} else {
1472-
editors[editorId].renamedFrom(rank, model['renamedFrom']);
1484+
renamedFrom(model['renamedFrom']);
14731485
}
14741486

14751487
diffEditor.updateOptions({
@@ -1503,19 +1515,24 @@ function initDiffEditorTab(editorId, diffId, rank, models, modifiedModel) {
15031515
updateSelect(editors[editorId].getDiffSelection(), editors[editorId].getDiffSelection() === "");
15041516

15051517
const updateIcon = () => {
1518+
if (rank === undefined) return;
1519+
const update = (icon) => {
1520+
const element = navItem.querySelector('.fa-fw');
1521+
element.className = 'fas fa-fw fa-' + icon;
1522+
};
15061523
const noDiff = editors[editorId].getDiffSelection() === "";
15071524
if (noDiff) {
1508-
editors[editorId].updateIcon(rank, 'file');
1525+
update('file');
15091526
return;
15101527
}
15111528

15121529
const lineChanges = diffEditor.getLineChanges();
15131530
if (diffEditor.getModel().original == empty) {
1514-
editors[editorId].updateIcon(rank, 'file-circle-plus');
1531+
update('file-circle-plus');
15151532
} else if (lineChanges !== null && lineChanges.length > 0) {
1516-
editors[editorId].updateIcon(rank, 'file-circle-exclamation');
1533+
update('file-circle-exclamation');
15171534
} else {
1518-
editors[editorId].updateIcon(rank, 'file-circle-check');
1535+
update('file-circle-check');
15191536
}
15201537
}
15211538
diffEditor.onDidUpdateDiff(updateIcon);

webapp/src/Controller/Jury/SubmissionController.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,7 @@ public function sourceAction(
848848
->orderBy('file.ranknumber')
849849
->getQuery()
850850
->getResult();
851+
// TODO: change array to `filename -> file` for efficiency of renaming?
851852
852853
$otherSubmissions = [];
853854
$originalSubmission = $submission->getOriginalSubmission();
@@ -929,6 +930,23 @@ public function sourceAction(
929930
}
930931
}
931932

933+
$deletedFiles = [];
934+
foreach ($otherSubmissions as $s) {
935+
$submitId = $s->getSubmitid();
936+
$sf = $otherFiles[$submitId];
937+
if (count($files) === 1 && count($sf) === 1) {
938+
continue;
939+
}
940+
foreach ($sf as $name => $file) {
941+
if (!array_key_exists($name, $files)) {
942+
// TODO: note to self: rotated the key order s.t. we can iterate over deleted filenames in a.o. twig
943+
$deletedFiles[$name] ??= [];
944+
$deletedFiles[$name][$submitId] = $otherFiles[$submitId][$name];
945+
}
946+
}
947+
}
948+
dump($deletedFiles);
949+
932950
return $this->render('jury/submission_source.html.twig', [
933951
'submission' => $submission,
934952
'files' => $files,
@@ -937,6 +955,7 @@ public function sourceAction(
937955
'allowEdit' => $this->allowEdit(),
938956
'otherSubmissions' => $otherSubmissions,
939957
'otherFiles' => $otherFiles,
958+
'deletedFiles' => $deletedFiles,
940959
]);
941960
}
942961

webapp/src/Twig/TwigExtension.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public function getFunctions(): array
7373
new TwigFunction('globalBannerAssetPath', $this->dj->globalBannerAssetPath(...)),
7474
new TwigFunction('shadowMode', $this->shadowMode(...)),
7575
new TwigFunction('showDiff', $this->showDiff(...), ['is_safe' => ['html']]),
76+
new TwigFunction('showDeleted', $this->showDeleted(...), ['is_safe' => ['html']]),
7677
];
7778
}
7879

@@ -994,6 +995,32 @@ public function showDiff(string $editorId, string $diffId, SubmissionFile $newFi
994995
);
995996
}
996997

998+
/** @param array<int, SubmissionFile[]> $deletedFiles */
999+
public function showDeleted(string $editorId, string $diffId, string $filename, array $deletedFiles): string
1000+
{
1001+
$editor = <<<HTML
1002+
<div class="editor" id="$diffId"></div>
1003+
<script>
1004+
$(function() {
1005+
const editorId = '%s';
1006+
const diffId = '%s';
1007+
const models = %s;
1008+
require(['vs/editor/editor.main'], () => {
1009+
const modifiedModel = monaco.editor.getModel(monaco.Uri.file("empty"));
1010+
initDiffEditorTab(editorId, diffId, undefined, models, modifiedModel);
1011+
});
1012+
});
1013+
</script>
1014+
HTML;
1015+
1016+
return sprintf(
1017+
$editor,
1018+
$editorId,
1019+
$diffId,
1020+
$this->serializer->serialize($deletedFiles, 'json'),
1021+
);
1022+
}
1023+
9971024
public function printContestStart(Contest $contest): string
9981025
{
9991026
$res = "scheduled to start ";

webapp/templates/jury/partials/submission_diff.html.twig

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@
55
{%- for file in files %}
66
{% set diff_id = "diff" ~ file.submitfileid %}
77
<li class="nav-item">
8-
<a class="nav-link {{ extra_css_classes }}" data-bs-toggle="tab" data-rank="{{ file.rank }}" href="#{{ diff_id }}-tab" role="tab"><i class="fas fa-fw fa-file"></i> {{ file.filename }}</a>
8+
<a id="{{ diff_id }}-link" class="nav-link {{ extra_css_classes }}" data-bs-toggle="tab" data-rank="{{ file.rank }}" href="#{{ diff_id }}-tab" role="tab"><i class="fas fa-fw fa-file"></i> {{ file.filename }}</a>
99
</li>
1010
{% set extra_css_classes = "" %}
1111
{%- endfor %}
12+
{%- for deletedName, _ in deletedFiles %}
13+
{% set diff_id = "diff-deleted-" ~ deletedName %}
14+
<li class="nav-item">
15+
<a id="{{ diff_id }}-link" class="nav-link source-deleted" data-bs-toggle="tab" href="#{{ diff_id }}-tab" role="tab"><i class="fas fa-fw fa-file-circle-minus"></i> {{ deletedName }}</a>
16+
</li>
17+
{%- endfor -%}
1218

1319
<li class="nav-item flex-grow-1 text-end mb-1">
1420
<a class="download btn btn-secondary btn-sm"
@@ -50,18 +56,27 @@
5056
<script>
5157
$(() => {
5258
require(['vs/editor/editor.main'], () => {
53-
initDiffEditor('{{ editor_id }}');
59+
const deletedFiles = {{ deletedFiles | json_encode | raw }};
60+
initDiffEditor('{{ editor_id }}', deletedFiles);
5461
});
5562
});
5663
</script>
5764
{% set extra_css_classes = "show active" %}
5865
<div class="tab-content source-tab">
5966
{%- for file in files %}
67+
{# TODO: rewrite s.t. files is also a nice array like otherFiles #}
68+
{# TODO: allow null to showDiff work as deleted file #}
6069
{% set diff_id = "diff" ~ file.submitfileid %}
6170
<div class="tab-pane fade {{ extra_css_classes }}" id="{{ diff_id }}-tab" role="tabpanel">
6271
{{ showDiff(editor_id, diff_id, file, otherFiles) }}
6372
</div>
6473
{% set extra_css_classes = "" %}
6574
{%- endfor %}
75+
{%- for deletedName, deletedF in deletedFiles %}
76+
{% set diff_id = "diff-deleted-" ~ deletedName %}
77+
<div class="tab-pane fade source-deleted" id="{{ diff_id }}-tab" role="tabpanel">
78+
{{ showDeleted(editor_id, diff_id, deletedName, deletedF) }}
79+
</div>
80+
{%- endfor %}
6681
</div>
6782
</div>

0 commit comments

Comments
 (0)