diff --git a/src/line-by-line-renderer.ts b/src/line-by-line-renderer.ts
index 4258095..2dd310f 100644
--- a/src/line-by-line-renderer.ts
+++ b/src/line-by-line-renderer.ts
@@ -82,6 +82,7 @@ export default class LineByLineRenderer {
generateEmptyDiff(): string {
return this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
contentClass: 'd2h-code-line',
+ colspan: '2',
CSSLineClass: renderUtils.CSSLineClass,
});
}
@@ -93,35 +94,43 @@ export default class LineByLineRenderer {
return file.blocks
.map(block => {
- let lines = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
- CSSLineClass: renderUtils.CSSLineClass,
- blockHeader: file.isTooBig ? block.header : renderUtils.escapeForHtml(block.header),
- lineClass: 'd2h-code-linenumber',
- contentClass: 'd2h-code-line',
- });
+ const lines: string[] = [];
- this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
+ lines.push(
+ this.hoganUtils.render(genericTemplatesPath, 'block-header', {
+ CSSLineClass: renderUtils.CSSLineClass,
+ margin_colspan: '2',
+ colspan: '1',
+ blockHeader: file.isTooBig ? block.header : renderUtils.escapeForHtml(block.header),
+ lineClass: 'd2h-code-linenumber',
+ contentClass: 'd2h-code-line',
+ }),
+ );
+
+ this.applyLineGrouping(block).forEach(([contextLines, oldLines, newLines]) => {
if (oldLines.length && newLines.length && !contextLines.length) {
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
- lines += left;
- lines += right;
+ lines.push(...left);
+ lines.push(...right);
});
} else if (contextLines.length) {
contextLines.forEach(line => {
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
- lines += this.generateSingleLineHtml({
- type: renderUtils.CSSLineClass.CONTEXT,
- prefix: prefix,
- content: content,
- oldNumber: line.oldNumber,
- newNumber: line.newNumber,
- });
+ lines.push(
+ this.generateSingleLineHtml({
+ type: renderUtils.CSSLineClass.CONTEXT,
+ prefix: prefix,
+ content: content,
+ oldNumber: line.oldNumber,
+ newNumber: line.newNumber,
+ }),
+ );
});
} else if (oldLines.length || newLines.length) {
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
- lines += left;
- lines += right;
+ lines.push(...left);
+ lines.push(...right);
} else {
console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
}
@@ -129,10 +138,13 @@ export default class LineByLineRenderer {
return lines;
})
+ .map((block_html: string[]) => {
+ return block_html.map(line => `
${line}
`).join('\n');
+ })
.join('\n');
}
- applyLineGroupping(block: DiffBlock): DiffLineGroups {
+ applyLineGrouping(block: DiffBlock): DiffLineGroups {
const blockLinesGroups: DiffLineGroups = [];
let oldLines: (DiffLineDeleted & DiffLineContent)[] = [];
@@ -189,9 +201,9 @@ export default class LineByLineRenderer {
}
processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
- const fileHtml = {
- right: '',
- left: '',
+ const fileHtml: FileHtml = {
+ left: [],
+ right: [],
};
const maxLinesNumber = Math.max(oldLines.length, newLines.length);
@@ -241,8 +253,8 @@ export default class LineByLineRenderer {
: undefined;
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
- fileHtml.left += left;
- fileHtml.right += right;
+ fileHtml.left.push(...left);
+ fileHtml.right.push(...right);
}
return fileHtml;
@@ -250,27 +262,34 @@ export default class LineByLineRenderer {
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
return {
- left: this.generateSingleLineHtml(oldLine),
- right: this.generateSingleLineHtml(newLine),
+ left: [this.generateSingleLineHtml(oldLine)],
+ right: [this.generateSingleLineHtml(newLine)],
};
}
generateSingleLineHtml(line?: DiffPreparedLine): string {
if (line === undefined) return '';
- const lineNumberHtml = this.hoganUtils.render(baseTemplatesPath, 'numbers', {
- oldNumber: line.oldNumber || '',
- newNumber: line.newNumber || '',
+ const oldLineNumberHtml = this.hoganUtils.render(genericTemplatesPath, 'line-number', {
+ type: line.type,
+ lineClass: line.oldNumber ? 'd2h-code-linenumber' : 'd2h-code-emptyplaceholder',
+ lineNumber: line.oldNumber,
});
- return this.hoganUtils.render(genericTemplatesPath, 'line', {
+ const newLineNumberHtml = this.hoganUtils.render(genericTemplatesPath, 'line-number', {
+ type: line.type,
+ lineClass: line.newNumber ? 'd2h-code-linenumber' : 'd2h-code-emptyplaceholder',
+ lineNumber: line.newNumber,
+ });
+
+ const newLineContentHtml = this.hoganUtils.render(genericTemplatesPath, 'line', {
type: line.type,
- lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line',
prefix: line.prefix === ' ' ? ' ' : line.prefix,
content: line.content,
- lineNumber: lineNumberHtml,
});
+
+ return oldLineNumberHtml.concat(newLineNumberHtml, newLineContentHtml);
}
}
@@ -289,6 +308,6 @@ type DiffPreparedLine = {
};
type FileHtml = {
- left: string;
- right: string;
+ left: string[];
+ right: string[];
};
diff --git a/src/side-by-side-renderer.ts b/src/side-by-side-renderer.ts
index 9a0fb6b..1216632 100644
--- a/src/side-by-side-renderer.ts
+++ b/src/side-by-side-renderer.ts
@@ -55,7 +55,7 @@ export default class SideBySideRenderer {
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { content: diffsHtml });
}
- makeFileDiffHtml(file: DiffFile, diffs: FileHtml): string {
+ makeFileDiffHtml(file: DiffFile, diffs: string): string {
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
@@ -79,34 +79,32 @@ export default class SideBySideRenderer {
});
}
- generateEmptyDiff(): FileHtml {
- return {
- right: '',
- left: this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
- contentClass: 'd2h-code-side-line',
- CSSLineClass: renderUtils.CSSLineClass,
- }),
- };
+ generateEmptyDiff(): string {
+ return this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
+ contentClass: 'd2h-code-side-line',
+ colspan: '4',
+ CSSLineClass: renderUtils.CSSLineClass,
+ });
}
- generateFileHtml(file: DiffFile): FileHtml {
+ generateFileHtml(file: DiffFile): string {
const matcher = Rematch.newMatcherFn(
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
);
return file.blocks
.map(block => {
- const fileHtml = {
- left: this.makeHeaderHtml(block.header, file),
- right: this.makeHeaderHtml(''),
+ const fileHtml: FileHtml = {
+ left: [this.makeHeaderHtml(block.header, file)],
+ right: [''],
};
- this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
+ this.applyLineGrouping(block).forEach(([contextLines, oldLines, newLines]) => {
if (oldLines.length && newLines.length && !contextLines.length) {
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
- fileHtml.left += left;
- fileHtml.right += right;
+ fileHtml.left.push(...left);
+ fileHtml.right.push(...right);
});
} else if (contextLines.length) {
contextLines.forEach(line => {
@@ -125,13 +123,13 @@ export default class SideBySideRenderer {
number: line.newNumber,
},
);
- fileHtml.left += left;
- fileHtml.right += right;
+ fileHtml.left.push(...left);
+ fileHtml.right.push(...right);
});
} else if (oldLines.length || newLines.length) {
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
- fileHtml.left += left;
- fileHtml.right += right;
+ fileHtml.left.push(...left);
+ fileHtml.right.push(...right);
} else {
console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
}
@@ -139,15 +137,19 @@ export default class SideBySideRenderer {
return fileHtml;
})
- .reduce(
- (accomulated, html) => {
- return { left: accomulated.left + html.left, right: accomulated.right + html.right };
- },
- { left: '', right: '' },
- );
+ .map((block_html: FileHtml) => {
+ let block_html_string = '';
+ for (let block_line_index = 0; block_line_index < block_html.left.length; block_line_index++) {
+ block_html_string = block_html_string.concat(
+ `${block_html.left[block_line_index]} ${block_html.right[block_line_index]}
`,
+ );
+ }
+ return block_html_string;
+ })
+ .join('\n');
}
- applyLineGroupping(block: DiffBlock): DiffLineGroups {
+ applyLineGrouping(block: DiffBlock): DiffLineGroups {
const blockLinesGroups: DiffLineGroups = [];
let oldLines: (DiffLineDeleted & DiffLineContent)[] = [];
@@ -206,6 +208,8 @@ export default class SideBySideRenderer {
makeHeaderHtml(blockHeader: string, file?: DiffFile): string {
return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass,
+ margin_colspan: '1',
+ colspan: '3',
blockHeader: file?.isTooBig ? blockHeader : renderUtils.escapeForHtml(blockHeader),
lineClass: 'd2h-code-side-linenumber',
contentClass: 'd2h-code-side-line',
@@ -213,9 +217,9 @@ export default class SideBySideRenderer {
}
processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
- const fileHtml = {
- right: '',
- left: '',
+ const fileHtml: FileHtml = {
+ left: [],
+ right: [],
};
const maxLinesNumber = Math.max(oldLines.length, newLines.length);
@@ -263,8 +267,8 @@ export default class SideBySideRenderer {
: undefined;
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
- fileHtml.left += left;
- fileHtml.right += right;
+ fileHtml.left.push(...left);
+ fileHtml.right.push(...right);
}
return fileHtml;
@@ -272,8 +276,8 @@ export default class SideBySideRenderer {
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
return {
- left: this.generateSingleHtml(oldLine),
- right: this.generateSingleHtml(newLine),
+ left: [this.generateSingleHtml(oldLine)],
+ right: [this.generateSingleHtml(newLine)],
};
}
@@ -281,14 +285,22 @@ export default class SideBySideRenderer {
const lineClass = 'd2h-code-side-linenumber';
const contentClass = 'd2h-code-side-line';
- return this.hoganUtils.render(genericTemplatesPath, 'line', {
- type: line?.type || `${renderUtils.CSSLineClass.CONTEXT} d2h-emptyplaceholder`,
+ const type = line?.type || `${renderUtils.CSSLineClass.CONTEXT} d2h-emptyplaceholder`;
+
+ const line_number_cell = this.hoganUtils.render(genericTemplatesPath, 'line-number', {
+ type: type,
lineClass: line !== undefined ? lineClass : `${lineClass} d2h-code-side-emptyplaceholder`,
+ lineNumber: line?.number,
+ });
+
+ const line_content_cell = this.hoganUtils.render(genericTemplatesPath, 'line', {
+ type: type,
contentClass: line !== undefined ? contentClass : `${contentClass} d2h-code-side-emptyplaceholder`,
prefix: line?.prefix === ' ' ? ' ' : line?.prefix,
content: line?.content,
- lineNumber: line?.number,
});
+
+ return line_number_cell.concat(line_content_cell);
}
}
@@ -306,6 +318,6 @@ type DiffPreparedLine = {
};
type FileHtml = {
- left: string;
- right: string;
+ left: string[];
+ right: string[];
};
diff --git a/src/templates/generic-block-header.mustache b/src/templates/generic-block-header.mustache
index efbbc42..ebf67ec 100644
--- a/src/templates/generic-block-header.mustache
+++ b/src/templates/generic-block-header.mustache
@@ -1,6 +1,4 @@
-
- |
-
- {{#blockHeader}}{{{blockHeader}}}{{/blockHeader}}{{^blockHeader}} {{/blockHeader}}
- |
-
+ |
+
+ {{#blockHeader}}{{{blockHeader}}}{{/blockHeader}}{{^blockHeader}} {{/blockHeader}}
+ |
diff --git a/src/templates/generic-empty-diff.mustache b/src/templates/generic-empty-diff.mustache
index 8cf1443..9107aa3 100644
--- a/src/templates/generic-empty-diff.mustache
+++ b/src/templates/generic-empty-diff.mustache
@@ -1,7 +1,7 @@
- |
-
- File without changes
-
- |
+
+
+ File without changes
+
+ |
diff --git a/src/templates/generic-line-number.mustache b/src/templates/generic-line-number.mustache
new file mode 100644
index 0000000..01a2ffa
--- /dev/null
+++ b/src/templates/generic-line-number.mustache
@@ -0,0 +1 @@
+ |
diff --git a/src/templates/generic-line.mustache b/src/templates/generic-line.mustache
index cf8f7e6..df01661 100644
--- a/src/templates/generic-line.mustache
+++ b/src/templates/generic-line.mustache
@@ -1,21 +1,5 @@
-
- |
- {{{lineNumber}}}
- |
-
-
- {{#prefix}}
- {{{prefix}}}
- {{/prefix}}
- {{^prefix}}
-
- {{/prefix}}
- {{#content}}
- {{{content}}}
- {{/content}}
- {{^content}}
-
- {{/content}}
-
- |
-
+
+
+ {{#prefix}}{{/prefix}}{{^prefix}}{{/prefix}}{{#content}}{{content}}{{/content}}{{^content}} {{/content}}
+
+ |
diff --git a/src/templates/line-by-line-file-diff.mustache b/src/templates/line-by-line-file-diff.mustache
index 8af1ccd..f5ce559 100644
--- a/src/templates/line-by-line-file-diff.mustache
+++ b/src/templates/line-by-line-file-diff.mustache
@@ -5,6 +5,14 @@
+
+
+ | Original file line number |
+ Diff line number |
+ Content |
+
+
+
{{{diffs}}}
diff --git a/src/templates/line-by-line-numbers.mustache b/src/templates/line-by-line-numbers.mustache
deleted file mode 100644
index c0ef8c6..0000000
--- a/src/templates/line-by-line-numbers.mustache
+++ /dev/null
@@ -1,2 +0,0 @@
-{{oldNumber}}
-{{newNumber}}
diff --git a/src/templates/side-by-side-file-diff.mustache b/src/templates/side-by-side-file-diff.mustache
index 3f3e5d3..e0e9d00 100644
--- a/src/templates/side-by-side-file-diff.mustache
+++ b/src/templates/side-by-side-file-diff.mustache
@@ -5,18 +5,25 @@
-
+
+
+
+ | Original file line number |
+ Original file content |
+ Diff line number |
+ Diff content |
+
+
+
+
+
+
+
+
+
+
- {{{diffs.left}}}
-
-
-
-
-
-
-
-
- {{{diffs.right}}}
+ {{{diffs}}}
diff --git a/src/ui/css/diff2html.css b/src/ui/css/diff2html.css
index e88a20f..1d547d0 100644
--- a/src/ui/css/diff2html.css
+++ b/src/ui/css/diff2html.css
@@ -7,6 +7,17 @@
.d2h-wrapper {
text-align: left;
+ word-wrap: break-word;
+}
+
+.d2h-wrapper * {
+ box-sizing: border-box;
+}
+
+.d2h-wrapper table {
+ border-spacing: 0;
+ border-collapse: collapse;
+ table-layout: fixed;
}
.d2h-file-header {
@@ -94,9 +105,14 @@
.d2h-diff-table {
width: 100%;
- border-collapse: collapse;
+ border-collapse: separate;
font-family: 'Menlo', 'Consolas', monospace;
font-size: 13px;
+ word-wrap: break-word;
+}
+
+.d2h-diff-table.d2h-split-diff {
+ table-layout: fixed;
}
.d2h-files-diff {
@@ -104,46 +120,40 @@
width: 100%;
}
-.d2h-file-diff {
- overflow-y: hidden;
-}
-
.d2h-files-diff.d2h-d-none,
.d2h-file-diff.d2h-d-none {
display: none;
}
.d2h-file-side-diff {
- display: inline-block;
- overflow-x: scroll;
- overflow-y: hidden;
- width: 50%;
-}
-
-.d2h-code-line {
- display: inline-block;
- white-space: nowrap;
- user-select: none;
- width: calc(100% - 16em);
- /* Compensate for the absolute positioning of the line numbers */
- padding: 0 8em;
+ width: 100%;
}
+.d2h-code-line,
.d2h-code-side-line {
- display: inline-block;
- white-space: nowrap;
user-select: none;
- width: calc(100% - 9em);
- /* Compensate for the absolute positioning of the line numbers */
- padding: 0 4.5em;
+ padding-left: 22px;
+ position: relative;
+ padding-right: 10px;
+ line-height: 20px;
+ vertical-align: top;
+}
+
+.d2h-code-marker::before {
+ position: absolute;
+ top: 1px;
+ left: 8px;
+ padding-right: 8px;
+ content: attr(data-code-marker);
}
.d2h-code-line-ctn {
- display: inline-block;
+ display: table-cell;
background: none;
padding: 0;
- word-wrap: normal;
- white-space: pre;
+ word-wrap: anywhere;
+ white-space: pre-wrap;
+ overflow: visible;
user-select: text;
width: 100%;
vertical-align: middle;
@@ -178,63 +188,33 @@
white-space: pre;
}
-.line-num1 {
- box-sizing: border-box;
- float: left;
- width: 3.5em;
- overflow: hidden;
- text-overflow: ellipsis;
- padding: 0 0.5em 0 0.5em;
-}
-
-.line-num2 {
- box-sizing: border-box;
- float: right;
- width: 3.5em;
- overflow: hidden;
- text-overflow: ellipsis;
- padding: 0 0.5em 0 0.5em;
-}
-
-.d2h-code-linenumber {
- box-sizing: border-box;
- width: 7.5em;
- /* Keep the numbers fixed on line contents scroll */
- position: absolute;
- display: inline-block;
- background-color: #fff;
- color: rgba(0, 0, 0, 0.3);
- text-align: right;
- border: solid #eeeeee;
- border-width: 0 1px 0 1px;
- cursor: pointer;
-}
-
-.d2h-code-linenumber:after {
- content: '\200b';
-}
-
-.d2h-code-side-linenumber {
- /* Keep the numbers fixed on line contents scroll */
- position: absolute;
- display: inline-block;
- box-sizing: border-box;
- width: 4em;
- background-color: #fff;
- color: rgba(0, 0, 0, 0.3);
- text-align: right;
- border: solid #eeeeee;
- border-width: 0 1px 0 1px;
- cursor: pointer;
- overflow: hidden;
- text-overflow: ellipsis;
- padding: 0 0.5em 0 0.5em;
+.d2h-code-linenumber::before,
+.d2h-code-side-linenumber::before {
+ content: attr(data-line-number);
}
+.d2h-code-linenumber:after,
.d2h-code-side-linenumber:after {
content: '\200b';
}
+.d2h-code-side-linenumber,
+.d2h-code-linenumber {
+ /* Keep the numbers fixed on line contents scroll */
+ position: relative;
+ background-color: #fff;
+ color: rgba(0, 0, 0, 0.3);
+ text-align: right;
+ border: solid #eeeeee;
+ border-width: 0 1px 0 1px;
+ line-height: 20px;
+ font-size: 12px;
+ width: 1%;
+ min-width: 50px;
+ padding-right: 10px;
+ padding-left: 10px;
+}
+
.d2h-code-side-emptyplaceholder,
.d2h-emptyplaceholder {
background-color: #f1f1f1;