From 783a2a22025b7638030f2b56af3b04ecc8c4786e Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Mon, 18 Mar 2024 21:36:00 +0100 Subject: [PATCH 1/9] export match columns --- .../jplag/reporting/jsonfactory/ComparisonReportWriter.java | 6 ++++-- .../java/de/jplag/reporting/reportobject/model/Match.java | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java b/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java index 914d58672..b3fc5b8dc 100644 --- a/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java +++ b/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java @@ -98,7 +98,7 @@ private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Mat List tokensFirst = comparison.firstSubmission().getTokenList().subList(match.startOfFirst(), match.endOfFirst() + 1); List tokensSecond = comparison.secondSubmission().getTokenList().subList(match.startOfSecond(), match.endOfSecond() + 1); - Comparator lineComparator = Comparator.comparingInt(Token::getLine); + Comparator lineComparator = Comparator.comparingInt(Token::getLine).thenComparingInt(Token::getColumn); Token startOfFirst = tokensFirst.stream().min(lineComparator).orElseThrow(); Token endOfFirst = tokensFirst.stream().max(lineComparator).orElseThrow(); @@ -108,7 +108,9 @@ private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Mat return new Match( FilePathUtil.getRelativeSubmissionPath(startOfFirst.getFile(), comparison.firstSubmission(), submissionToIdFunction).toString(), FilePathUtil.getRelativeSubmissionPath(startOfSecond.getFile(), comparison.secondSubmission(), submissionToIdFunction).toString(), - startOfFirst.getLine(), endOfFirst.getLine(), startOfSecond.getLine(), endOfSecond.getLine(), match.length()); + startOfFirst.getLine(), startOfFirst.getColumn(), endOfFirst.getLine(), endOfFirst.getColumn() + endOfFirst.getLength(), + startOfSecond.getLine(), startOfSecond.getColumn(), endOfSecond.getLine(), endOfSecond.getColumn() + endOfSecond.getLength(), + match.length()); } } diff --git a/core/src/main/java/de/jplag/reporting/reportobject/model/Match.java b/core/src/main/java/de/jplag/reporting/reportobject/model/Match.java index 8af13af20..5ef5d728c 100644 --- a/core/src/main/java/de/jplag/reporting/reportobject/model/Match.java +++ b/core/src/main/java/de/jplag/reporting/reportobject/model/Match.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; public record Match(@JsonProperty("file1") String firstFileName, @JsonProperty("file2") String secondFileName, - @JsonProperty("start1") int startInFirst, @JsonProperty("end1") int endInFirst, @JsonProperty("start2") int startInSecond, - @JsonProperty("end2") int endInSecond, @JsonProperty("tokens") int tokens) { + @JsonProperty("start1") int startInFirst, @JsonProperty("start1_col") int startColumnInFirst, @JsonProperty("end1") int endInFirst, + @JsonProperty("end1_col") int endColumnInFirst, @JsonProperty("start2") int startInSecond, + @JsonProperty("start2_col") int startColumnInSecond, @JsonProperty("end2") int endInSecond, @JsonProperty("end2_col") int endColumnInSecond, + @JsonProperty("tokens") int tokens) { } From d8cb26ed6ce5ced62ec2830d44ab81dbba6f41ce Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Mon, 18 Mar 2024 21:37:58 +0100 Subject: [PATCH 2/9] extract match columns from report --- report-viewer/src/model/Match.ts | 4 ++++ report-viewer/src/model/MatchInSingleFile.ts | 16 ++++++++++++++++ .../src/model/factories/ComparisonFactory.ts | 4 ++++ 3 files changed, 24 insertions(+) diff --git a/report-viewer/src/model/Match.ts b/report-viewer/src/model/Match.ts index 21a597af0..e03451371 100644 --- a/report-viewer/src/model/Match.ts +++ b/report-viewer/src/model/Match.ts @@ -13,9 +13,13 @@ export interface Match { firstFile: string secondFile: string startInFirst: number + startColumnInFirst: number endInFirst: number + endColumnInFirst: number startInSecond: number + startColumnInSecond: number endInSecond: number + endColumnInSecond: number tokens: number colorIndex?: number } diff --git a/report-viewer/src/model/MatchInSingleFile.ts b/report-viewer/src/model/MatchInSingleFile.ts index bcaae43c0..e959d47e0 100644 --- a/report-viewer/src/model/MatchInSingleFile.ts +++ b/report-viewer/src/model/MatchInSingleFile.ts @@ -40,4 +40,20 @@ export class MatchInSingleFile { return this._match.endInSecond } } + + get startColumn(): number { + if (this._index === 1) { + return this._match.startColumnInFirst + } else { + return this._match.startColumnInSecond + } + } + + get endColumn(): number { + if (this._index === 1) { + return this._match.endColumnInFirst + } else { + return this._match.endColumnInSecond + } + } } diff --git a/report-viewer/src/model/factories/ComparisonFactory.ts b/report-viewer/src/model/factories/ComparisonFactory.ts index d6257e93e..98097f605 100644 --- a/report-viewer/src/model/factories/ComparisonFactory.ts +++ b/report-viewer/src/model/factories/ComparisonFactory.ts @@ -123,9 +123,13 @@ export class ComparisonFactory extends BaseFactory { firstFile: slash(match.file1 as string), secondFile: slash(match.file2 as string), startInFirst: match.start1 as number, + startColumnInFirst: ((match['start1_col'] as number) || 1) - 1, endInFirst: match.end1 as number, + endColumnInFirst: ((match['end1_col'] as number) || Infinity) - 1, startInSecond: match.start2 as number, + startColumnInSecond: ((match['start2_col'] as number) || 1) - 1, endInSecond: match.end2 as number, + endColumnInSecond: ((match['end2_col'] as number) || Infinity) - 1, tokens: match.tokens as number } } From 202d63699d43b66bd6b2eb91f07d9a951cc377cb Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Mon, 18 Mar 2024 21:54:38 +0100 Subject: [PATCH 3/9] display matches character precise --- .../components/fileDisplaying/CodeLine.vue | 156 ++++++++++++++++++ .../components/fileDisplaying/CodePanel.vue | 77 +++------ .../fileDisplaying/FilesContainer.vue | 4 +- report-viewer/src/views/ComparisonView.vue | 4 +- 4 files changed, 187 insertions(+), 54 deletions(-) create mode 100644 report-viewer/src/components/fileDisplaying/CodeLine.vue diff --git a/report-viewer/src/components/fileDisplaying/CodeLine.vue b/report-viewer/src/components/fileDisplaying/CodeLine.vue new file mode 100644 index 000000000..1b765ef18 --- /dev/null +++ b/report-viewer/src/components/fileDisplaying/CodeLine.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/report-viewer/src/components/fileDisplaying/CodePanel.vue b/report-viewer/src/components/fileDisplaying/CodePanel.vue index 506936f51..a02ff2f5b 100644 --- a/report-viewer/src/components/fileDisplaying/CodePanel.vue +++ b/report-viewer/src/components/fileDisplaying/CodePanel.vue @@ -22,39 +22,31 @@
- +
+ {{ index + 1 }} +
- - - - - - -
{{ index + 1 }}
+ ref="lineRefs" + :line="line.line" + :lineNumber="index + 1" + :matches="line.matches" + @matchSelected="matchSelected" + /> +
Empty File @@ -68,12 +60,12 @@ import type { MatchInSingleFile } from '@/model/MatchInSingleFile' import { ref, nextTick, type PropType, computed, type Ref } from 'vue' import Interactable from '../InteractableComponent.vue' -import type { Match } from '@/model/Match' import type { SubmissionFile } from '@/model/File' import { highlight } from '@/utils/CodeHighlighter' import type { Language } from '@/model/Language' -import { getMatchColor } from '@/utils/ColorUtils' import ToolTipComponent from '../ToolTipComponent.vue' +import CodeLine from './CodeLine.vue' +import type { Match } from '@/model/Match' const props = defineProps({ /** @@ -99,24 +91,22 @@ const props = defineProps({ } }) -const emit = defineEmits(['lineSelected']) +const emit = defineEmits(['matchSelected']) const collapsed = ref(true) const lineRefs = ref([]) -const codeLines: Ref<{ line: string; match: null | Match }[]> = computed(() => +const codeLines: Ref<{ line: string; matches: MatchInSingleFile[] }[]> = computed(() => highlight(props.file.data, props.highlightLanguage).map((line, index) => { return { line, - match: props.matches?.find((m) => m.start <= index + 1 && index + 1 <= m.end)?.match ?? null + matches: props.matches?.filter((m) => m.start <= index + 1 && index + 1 <= m.end) ?? [] } }) ) -function lineSelected(lineIndex: number) { - if (codeLines.value[lineIndex].match !== null) { - emit('lineSelected', codeLines.value[lineIndex].match) - } +function matchSelected(match: Match) { + emit('matchSelected', match) } /** @@ -154,16 +144,3 @@ function getFileDisplayName(file: SubmissionFile): string { : file.fileName } - - diff --git a/report-viewer/src/components/fileDisplaying/FilesContainer.vue b/report-viewer/src/components/fileDisplaying/FilesContainer.vue index 58c394e4e..1e9b2365e 100644 --- a/report-viewer/src/components/fileDisplaying/FilesContainer.vue +++ b/report-viewer/src/components/fileDisplaying/FilesContainer.vue @@ -28,7 +28,7 @@ !matches.get(file.fileName) ? [] : (matches.get(file.fileName) as MatchInSingleFile[]) " :highlight-language="highlightLanguage" - @line-selected="(match) => $emit('lineSelected', match)" + @match-selected="(match) => $emit('matchSelected', match)" class="mt-1 first:mt-0" /> @@ -83,7 +83,7 @@ const props = defineProps({ } }) -defineEmits(['lineSelected']) +defineEmits(['matchSelected']) const codePanels: Ref<(typeof CodePanel)[]> = ref([]) diff --git a/report-viewer/src/views/ComparisonView.vue b/report-viewer/src/views/ComparisonView.vue index 58380799a..b901b8d4a 100644 --- a/report-viewer/src/views/ComparisonView.vue +++ b/report-viewer/src/views/ComparisonView.vue @@ -85,7 +85,7 @@ :matches="comparison.matchesInFirstSubmission" :file-owner-display-name="store().getDisplayName(comparison.firstSubmissionId)" :highlight-language="language" - @line-selected="showMatchInSecond" + @match-selected="showMatchInSecond" class="max-h-0 min-h-full flex-1 overflow-hidden print:max-h-none print:overflow-y-visible" />
From 89a33016042bf3ff75286e5fc96d83efb70a15db Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Mon, 18 Mar 2024 22:24:09 +0100 Subject: [PATCH 4/9] clean up code --- .../components/fileDisplaying/CodeLine.vue | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/report-viewer/src/components/fileDisplaying/CodeLine.vue b/report-viewer/src/components/fileDisplaying/CodeLine.vue index 1b765ef18..413177c87 100644 --- a/report-viewer/src/components/fileDisplaying/CodeLine.vue +++ b/report-viewer/src/components/fileDisplaying/CodeLine.vue @@ -7,7 +7,7 @@ }" >
a.startColumn - b.startColumn) .sort((a, b) => a.start - b.start) - let matchParts: MatchPart[] = [] + let lineParts: { + start: number + end: number + match?: MatchInSingleFile + }[] = [] if (sortedMatches[0].start == props.lineNumber && sortedMatches[0].startColumn > 0) { const end = sortedMatches[0].startColumn - 1 - matchParts.push({ start: 0, end: end }) + lineParts.push({ start: 0, end: end }) } const start = sortedMatches[0].start == props.lineNumber ? sortedMatches[0].startColumn : 0 const end = sortedMatches[0].end == props.lineNumber ? sortedMatches[0].endColumn : props.line.length - matchParts.push({ start: start, end: end, match: sortedMatches[0] }) + lineParts.push({ start: start, end: end, match: sortedMatches[0] }) let matchIndex = 1 while (matchIndex < sortedMatches.length) { const match = sortedMatches[matchIndex] - const prevMatchPart = matchParts[matchIndex - 1] + const prevMatchPart = lineParts[matchIndex - 1] if (prevMatchPart.end + 1 < match.startColumn) { const end = match.startColumn - 1 - matchParts.push({ start: prevMatchPart.end + 1, end: end }) + lineParts.push({ start: prevMatchPart.end + 1, end: end }) } const end = match.end == props.lineNumber ? match.endColumn : props.line.length - matchParts.push({ start: match.startColumn, end: end, match }) + lineParts.push({ start: match.startColumn, end: end, match }) matchIndex++ } - if (matchParts[matchParts.length - 1].end < props.line.length) { - matchParts.push({ start: matchParts[matchParts.length - 1].end + 1, end: props.line.length }) + if (lineParts[lineParts.length - 1].end < props.line.length) { + lineParts.push({ start: lineParts[lineParts.length - 1].end + 1, end: props.line.length }) } - let parts: Part[] = [] + let textParts: TextPart[] = [] lineIndex.value = 0 colIndex.value = 0 - for (let i = 0; i < matchParts.length; i++) { - const matchPart = matchParts[i] - const line = getNextLinePartTill(matchPart.end) - parts.push({ line, ...matchPart }) + for (let i = 0; i < lineParts.length; i++) { + const matchPart = lineParts[i] + const line = getNextLinePartTillColumn(matchPart.end) + textParts.push({ line, match: matchPart.match }) } - return parts + return textParts } -const parts = compParts() +const textParts = computeTextParts() -function getNextLinePartTill(endCol: number) { +function getNextLinePartTillColumn(endCol: number) { let part = '' while (colIndex.value <= endCol && lineIndex.value < props.line.length) { if (props.line[lineIndex.value] == '<') { From 73809b6637ce165ddea90dedafca704494d2c30e Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Mon, 18 Mar 2024 23:05:54 +0100 Subject: [PATCH 5/9] fix scrolling --- .../src/components/fileDisplaying/CodeLine.vue | 15 +++++++++++++-- .../src/components/fileDisplaying/CodePanel.vue | 6 +++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/report-viewer/src/components/fileDisplaying/CodeLine.vue b/report-viewer/src/components/fileDisplaying/CodeLine.vue index 413177c87..fd6d0fc99 100644 --- a/report-viewer/src/components/fileDisplaying/CodeLine.vue +++ b/report-viewer/src/components/fileDisplaying/CodeLine.vue @@ -5,11 +5,12 @@ :style="{ gridRowStart: lineNumber }" + ref="lineRef" >
@@ -94,7 +94,7 @@ const props = defineProps({ const emit = defineEmits(['matchSelected']) const collapsed = ref(true) -const lineRefs = ref([]) +const lineRefs = ref<(typeof CodeLine)[]>([]) const codeLines: Ref<{ line: string; matches: MatchInSingleFile[] }[]> = computed(() => highlight(props.file.data, props.highlightLanguage).map((line, index) => { @@ -116,7 +116,7 @@ function matchSelected(match: Match) { function scrollTo(lineNumber: number) { collapsed.value = false nextTick(function () { - lineRefs.value[lineNumber - 1].scrollIntoView({ block: 'center' }) + lineRefs.value[lineNumber - 1].scrollTo() }) } From c343ec2899955d6a5eae7cf421f97d4bb6cf02b3 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 26 Mar 2024 15:37:37 +0100 Subject: [PATCH 6/9] fix tab indent --- report-viewer/src/components/fileDisplaying/CodeLine.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report-viewer/src/components/fileDisplaying/CodeLine.vue b/report-viewer/src/components/fileDisplaying/CodeLine.vue index fd6d0fc99..bc6ff058c 100644 --- a/report-viewer/src/components/fileDisplaying/CodeLine.vue +++ b/report-viewer/src/components/fileDisplaying/CodeLine.vue @@ -141,7 +141,7 @@ function getNextLinePartTillColumn(endCol: number) { } else if (props.line[lineIndex.value] == '\t') { part += ' ' lineIndex.value++ - colIndex.value++ + colIndex.value += 8 } else { part += props.line[lineIndex.value] lineIndex.value++ From 560a0676f73081dea3bbfd5e3ac5a4b837a043f1 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Fri, 29 Mar 2024 13:17:17 +0100 Subject: [PATCH 7/9] clean up match writing --- .../jsonfactory/ComparisonReportWriter.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java b/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java index b3fc5b8dc..2b2281b91 100644 --- a/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java +++ b/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java @@ -105,12 +105,23 @@ private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Mat Token startOfSecond = tokensSecond.stream().min(lineComparator).orElseThrow(); Token endOfSecond = tokensSecond.stream().max(lineComparator).orElseThrow(); - return new Match( - FilePathUtil.getRelativeSubmissionPath(startOfFirst.getFile(), comparison.firstSubmission(), submissionToIdFunction).toString(), - FilePathUtil.getRelativeSubmissionPath(startOfSecond.getFile(), comparison.secondSubmission(), submissionToIdFunction).toString(), - startOfFirst.getLine(), startOfFirst.getColumn(), endOfFirst.getLine(), endOfFirst.getColumn() + endOfFirst.getLength(), - startOfSecond.getLine(), startOfSecond.getColumn(), endOfSecond.getLine(), endOfSecond.getColumn() + endOfSecond.getLength(), - match.length()); + String firstFileName = FilePathUtil.getRelativeSubmissionPath(startOfFirst.getFile(), comparison.firstSubmission(), submissionToIdFunction) + .toString(); + String secondFileName = FilePathUtil.getRelativeSubmissionPath(startOfSecond.getFile(), comparison.secondSubmission(), submissionToIdFunction) + .toString(); + + int startLineFirst = startOfFirst.getLine(); + int startColumnFirst = startOfFirst.getColumn(); + int endLineFirst = endOfFirst.getLine(); + int endColumnFirst = endOfFirst.getColumn() + endOfFirst.getLength(); + + int startLineSecond = startOfSecond.getLine(); + int startColumnSecond = startOfSecond.getColumn(); + int endLineSecond = endOfSecond.getLine(); + int endColumnSecond = endOfSecond.getColumn() + endOfSecond.getLength(); + + return new Match(firstFileName, secondFileName, startLineFirst, startColumnFirst, endLineFirst, endColumnFirst, startLineSecond, + startColumnSecond, endLineSecond, endColumnSecond, match.length()); } } From fd53dab5802d8062d74eb930f5a340bc2bdec9de Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 9 Apr 2024 15:18:22 +0200 Subject: [PATCH 8/9] correct line displaying --- .../reporting/jsonfactory/ComparisonReportWriter.java | 4 ++-- .../src/components/fileDisplaying/CodeLine.vue | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java b/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java index 2b2281b91..28445ab36 100644 --- a/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java +++ b/core/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java @@ -113,12 +113,12 @@ private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Mat int startLineFirst = startOfFirst.getLine(); int startColumnFirst = startOfFirst.getColumn(); int endLineFirst = endOfFirst.getLine(); - int endColumnFirst = endOfFirst.getColumn() + endOfFirst.getLength(); + int endColumnFirst = endOfFirst.getColumn() + endOfFirst.getLength() - 1; int startLineSecond = startOfSecond.getLine(); int startColumnSecond = startOfSecond.getColumn(); int endLineSecond = endOfSecond.getLine(); - int endColumnSecond = endOfSecond.getColumn() + endOfSecond.getLength(); + int endColumnSecond = endOfSecond.getColumn() + endOfSecond.getLength() - 1; return new Match(firstFileName, secondFileName, startLineFirst, startColumnFirst, endLineFirst, endColumnFirst, startLineSecond, startColumnSecond, endLineSecond, endColumnSecond, match.length()); diff --git a/report-viewer/src/components/fileDisplaying/CodeLine.vue b/report-viewer/src/components/fileDisplaying/CodeLine.vue index bc6ff058c..4da4080c2 100644 --- a/report-viewer/src/components/fileDisplaying/CodeLine.vue +++ b/report-viewer/src/components/fileDisplaying/CodeLine.vue @@ -131,6 +131,7 @@ const textParts = computeTextParts() function getNextLinePartTillColumn(endCol: number) { let part = '' while (colIndex.value <= endCol && lineIndex.value < props.line.length) { + // spans from highlighting do not count as characters in the code if (props.line[lineIndex.value] == '<') { while (props.line[lineIndex.value] != '>') { part += props.line[lineIndex.value] @@ -139,9 +140,18 @@ function getNextLinePartTillColumn(endCol: number) { part += props.line[lineIndex.value] lineIndex.value++ } else if (props.line[lineIndex.value] == '\t') { + // display tabs properly part += ' ' lineIndex.value++ colIndex.value += 8 + } else if (props.line[lineIndex.value] == '&') { + // html escape characters for e.g. <,>,& + while (props.line[lineIndex.value] != ';') { + part += props.line[lineIndex.value] + lineIndex.value++ + } + lineIndex.value++ + colIndex.value++ } else { part += props.line[lineIndex.value] lineIndex.value++ From 0f3fb760254b525960e7e9a850e3b7641db71db2 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 9 Apr 2024 17:00:54 +0200 Subject: [PATCH 9/9] replace loop --- report-viewer/src/components/fileDisplaying/CodeLine.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/report-viewer/src/components/fileDisplaying/CodeLine.vue b/report-viewer/src/components/fileDisplaying/CodeLine.vue index 4da4080c2..6d2b2ab99 100644 --- a/report-viewer/src/components/fileDisplaying/CodeLine.vue +++ b/report-viewer/src/components/fileDisplaying/CodeLine.vue @@ -117,8 +117,7 @@ function computeTextParts() { lineIndex.value = 0 colIndex.value = 0 - for (let i = 0; i < lineParts.length; i++) { - const matchPart = lineParts[i] + for (const matchPart of lineParts) { const line = getNextLinePartTillColumn(matchPart.end) textParts.push({ line, match: matchPart.match }) }