Revert to original overflow behaviour by default; add overflow: 'scroll' as an option
Fixed issues with line-by-line rendering and HTML escaping
This commit is contained in:
parent
591334d942
commit
9790e447c6
10 changed files with 306 additions and 67 deletions
|
|
@ -76,15 +76,20 @@ export default class LineByLineRenderer {
|
||||||
fileTag: fileTagTemplate,
|
fileTag: fileTagTemplate,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
overflowClass: this.config.diffOverflow.concat('-overflow'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateEmptyDiff(): string {
|
generateEmptyDiff(): string {
|
||||||
return this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
return (
|
||||||
contentClass: 'd2h-code-line',
|
'<tr' +
|
||||||
colspan: '2',
|
this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
||||||
CSSLineClass: renderUtils.CSSLineClass,
|
contentClass: 'd2h-code-line',
|
||||||
});
|
colspan: '2',
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
}) +
|
||||||
|
'</tr>'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFileHtml(file: DiffFile): string {
|
generateFileHtml(file: DiffFile): string {
|
||||||
|
|
@ -253,17 +258,17 @@ export default class LineByLineRenderer {
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
|
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
|
||||||
fileHtml.left.push(...left);
|
fileHtml.left.push(left);
|
||||||
fileHtml.right.push(...right);
|
fileHtml.right.push(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileHtml;
|
return fileHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
|
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): LineHtml {
|
||||||
return {
|
return {
|
||||||
left: [this.generateSingleLineHtml(oldLine)],
|
left: this.generateSingleLineHtml(oldLine),
|
||||||
right: [this.generateSingleLineHtml(newLine)],
|
right: this.generateSingleLineHtml(newLine),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,6 +312,11 @@ type DiffPreparedLine = {
|
||||||
newNumber?: number;
|
newNumber?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type LineHtml = {
|
||||||
|
left: string;
|
||||||
|
right: string;
|
||||||
|
};
|
||||||
|
|
||||||
type FileHtml = {
|
type FileHtml = {
|
||||||
left: string[];
|
left: string[];
|
||||||
right: string[];
|
right: string[];
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,15 @@ import * as jsDiff from 'diff';
|
||||||
|
|
||||||
import { unifyPath, hashCode } from './utils';
|
import { unifyPath, hashCode } from './utils';
|
||||||
import * as rematch from './rematch';
|
import * as rematch from './rematch';
|
||||||
import { LineMatchingType, DiffStyleType, LineType, DiffLineParts, DiffFile, DiffFileName } from './types';
|
import {
|
||||||
|
LineMatchingType,
|
||||||
|
DiffStyleType,
|
||||||
|
DiffOverflowType,
|
||||||
|
LineType,
|
||||||
|
DiffLineParts,
|
||||||
|
DiffFile,
|
||||||
|
DiffFileName,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
export type CSSLineClass =
|
export type CSSLineClass =
|
||||||
| 'd2h-ins'
|
| 'd2h-ins'
|
||||||
|
|
@ -37,6 +45,7 @@ export interface RenderConfig {
|
||||||
matchWordsThreshold?: number;
|
matchWordsThreshold?: number;
|
||||||
maxLineLengthHighlight?: number;
|
maxLineLengthHighlight?: number;
|
||||||
diffStyle?: DiffStyleType;
|
diffStyle?: DiffStyleType;
|
||||||
|
diffOverflow?: DiffOverflowType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultRenderConfig = {
|
export const defaultRenderConfig = {
|
||||||
|
|
@ -44,6 +53,7 @@ export const defaultRenderConfig = {
|
||||||
matchWordsThreshold: 0.25,
|
matchWordsThreshold: 0.25,
|
||||||
maxLineLengthHighlight: 10000,
|
maxLineLengthHighlight: 10000,
|
||||||
diffStyle: DiffStyleType.WORD,
|
diffStyle: DiffStyleType.WORD,
|
||||||
|
diffOverflow: DiffOverflowType.SCROLL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const separator = '/';
|
const separator = '/';
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import * as renderUtils from './render-utils';
|
||||||
import {
|
import {
|
||||||
DiffLine,
|
DiffLine,
|
||||||
LineType,
|
LineType,
|
||||||
|
DiffOverflowType,
|
||||||
DiffFile,
|
DiffFile,
|
||||||
DiffBlock,
|
DiffBlock,
|
||||||
DiffLineContext,
|
DiffLineContext,
|
||||||
|
|
@ -42,27 +43,43 @@ export default class SideBySideRenderer {
|
||||||
render(diffFiles: DiffFile[]): string {
|
render(diffFiles: DiffFile[]): string {
|
||||||
const diffsHtml = diffFiles
|
const diffsHtml = diffFiles
|
||||||
.map(file => {
|
.map(file => {
|
||||||
let diffs;
|
let diffsFileHtml;
|
||||||
if (file.blocks.length) {
|
if (file.blocks.length) {
|
||||||
diffs = this.generateFileHtml(file);
|
diffsFileHtml = this.generateFileHtml(file);
|
||||||
} else {
|
} else {
|
||||||
diffs = this.generateEmptyDiff();
|
diffsFileHtml = this.generateEmptyDiff();
|
||||||
}
|
}
|
||||||
return this.makeFileDiffHtml(file, diffs);
|
return this.makeFileDiffHtml(file, diffsFileHtml);
|
||||||
})
|
})
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
|
||||||
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { content: diffsHtml });
|
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { content: diffsHtml });
|
||||||
}
|
}
|
||||||
|
|
||||||
makeFileDiffHtml(file: DiffFile, diffs: string): string {
|
makeFileDiffHtml(file: DiffFile, diffsFileHtml: FileHtml): string {
|
||||||
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
|
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
|
||||||
|
|
||||||
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
|
if (this.config.diffOverflow === DiffOverflowType.SCROLL) {
|
||||||
|
return this.makeScrollFileDiffHtml(file, diffsFileHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.diffOverflow === DiffOverflowType.WRAP) {
|
||||||
|
return this.makeWrappedFileDiffHtml(file, diffsFileHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unrecognised DiffOverflow setting');
|
||||||
|
}
|
||||||
|
|
||||||
|
makeWrappedFileDiffHtml(file: DiffFile, diffsFileHtml: FileHtml): string {
|
||||||
|
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
|
||||||
|
|
||||||
|
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'text-wrapped-file-diff');
|
||||||
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
|
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
|
||||||
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
|
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
|
||||||
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
|
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
|
||||||
|
|
||||||
|
const diffs = this.joinFileHtmlForWrappedDisplay(diffsFileHtml);
|
||||||
|
|
||||||
return fileDiffTemplate.render({
|
return fileDiffTemplate.render({
|
||||||
file: file,
|
file: file,
|
||||||
fileHtmlId: renderUtils.getHtmlId(file),
|
fileHtmlId: renderUtils.getHtmlId(file),
|
||||||
|
|
@ -79,15 +96,50 @@ export default class SideBySideRenderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateEmptyDiff(): string {
|
makeScrollFileDiffHtml(file: DiffFile, diffsFileHtml: FileHtml): string {
|
||||||
return this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
|
||||||
contentClass: 'd2h-code-side-line',
|
|
||||||
colspan: '4',
|
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
|
||||||
CSSLineClass: renderUtils.CSSLineClass,
|
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
|
||||||
|
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
|
||||||
|
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
|
||||||
|
|
||||||
|
const diffs = this.joinFileHtmlForScrollDisplay(diffsFileHtml);
|
||||||
|
|
||||||
|
return fileDiffTemplate.render({
|
||||||
|
file: file,
|
||||||
|
fileHtmlId: renderUtils.getHtmlId(file),
|
||||||
|
diffs: diffs,
|
||||||
|
filePath: filePathTemplate.render(
|
||||||
|
{
|
||||||
|
fileDiffName: renderUtils.filenameDiff(file),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileIcon: fileIconTemplate,
|
||||||
|
fileTag: fileTagTemplate,
|
||||||
|
},
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFileHtml(file: DiffFile): string {
|
generateEmptyDiff(): FileHtml {
|
||||||
|
return {
|
||||||
|
left: [
|
||||||
|
this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
||||||
|
contentClass: 'd2h-code-side-line',
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
right: [
|
||||||
|
this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
||||||
|
contentClass: 'd2h-code-side-line',
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateFileHtml(file: DiffFile): FileHtml {
|
||||||
const matcher = Rematch.newMatcherFn(
|
const matcher = Rematch.newMatcherFn(
|
||||||
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
|
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
|
||||||
);
|
);
|
||||||
|
|
@ -95,9 +147,12 @@ export default class SideBySideRenderer {
|
||||||
return file.blocks
|
return file.blocks
|
||||||
.map(block => {
|
.map(block => {
|
||||||
const fileHtml: FileHtml = {
|
const fileHtml: FileHtml = {
|
||||||
left: [this.makeHeaderHtml(block.header, file)],
|
left: [],
|
||||||
right: [''],
|
right: [],
|
||||||
};
|
};
|
||||||
|
const header = this.makeHeaderHtml(block.header, file);
|
||||||
|
fileHtml.left.push(header.left);
|
||||||
|
fileHtml.right.push(header.right);
|
||||||
|
|
||||||
this.applyLineGrouping(block).forEach(([contextLines, oldLines, newLines]) => {
|
this.applyLineGrouping(block).forEach(([contextLines, oldLines, newLines]) => {
|
||||||
if (oldLines.length && newLines.length && !contextLines.length) {
|
if (oldLines.length && newLines.length && !contextLines.length) {
|
||||||
|
|
@ -123,8 +178,8 @@ export default class SideBySideRenderer {
|
||||||
number: line.newNumber,
|
number: line.newNumber,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
fileHtml.left.push(...left);
|
fileHtml.left.push(left);
|
||||||
fileHtml.right.push(...right);
|
fileHtml.right.push(right);
|
||||||
});
|
});
|
||||||
} else if (oldLines.length || newLines.length) {
|
} else if (oldLines.length || newLines.length) {
|
||||||
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
|
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
|
||||||
|
|
@ -137,16 +192,36 @@ export default class SideBySideRenderer {
|
||||||
|
|
||||||
return fileHtml;
|
return fileHtml;
|
||||||
})
|
})
|
||||||
.map((block_html: FileHtml) => {
|
.reduce(
|
||||||
let block_html_string = '';
|
(accumulator: FileHtml, { left, right }) => {
|
||||||
for (let block_line_index = 0; block_line_index < block_html.left.length; block_line_index++) {
|
accumulator.left.push(...left);
|
||||||
block_html_string = block_html_string.concat(
|
accumulator.right.push(...right);
|
||||||
`<tr>${block_html.left[block_line_index]} ${block_html.right[block_line_index]}</tr>`,
|
return accumulator;
|
||||||
);
|
},
|
||||||
}
|
{ left: [], right: [] },
|
||||||
return block_html_string;
|
);
|
||||||
})
|
}
|
||||||
.join('\n');
|
|
||||||
|
joinFileHtmlForWrappedDisplay(fileHtml: FileHtml): string {
|
||||||
|
let joined_string = '';
|
||||||
|
for (let block_line_index = 0; block_line_index < fileHtml.left.length; block_line_index++) {
|
||||||
|
joined_string = joined_string.concat(
|
||||||
|
`<tr>${fileHtml.left[block_line_index]} ${fileHtml.right[block_line_index]}</tr>\n`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return joined_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
joinFileHtmlForScrollDisplay(fileHtml: FileHtml): { left: string; right: string } {
|
||||||
|
const joined_strings = {
|
||||||
|
left: '',
|
||||||
|
right: '',
|
||||||
|
};
|
||||||
|
for (let block_line_index = 0; block_line_index < fileHtml.left.length; block_line_index++) {
|
||||||
|
joined_strings.left = joined_strings.left.concat(`<tr>${fileHtml.left[block_line_index]}</tr>\n`);
|
||||||
|
joined_strings.right = joined_strings.right.concat(`<tr>${fileHtml.right[block_line_index]}</tr>\n`);
|
||||||
|
}
|
||||||
|
return joined_strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyLineGrouping(block: DiffBlock): DiffLineGroups {
|
applyLineGrouping(block: DiffBlock): DiffLineGroups {
|
||||||
|
|
@ -205,8 +280,40 @@ export default class SideBySideRenderer {
|
||||||
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
|
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
|
||||||
}
|
}
|
||||||
|
|
||||||
makeHeaderHtml(blockHeader: string, file?: DiffFile): string {
|
makeHeaderHtml(blockHeader: string, file?: DiffFile): LineHtml {
|
||||||
return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
|
if (this.config.diffOverflow === DiffOverflowType.WRAP) {
|
||||||
|
return this.makeWrappedHeaderHtml(blockHeader, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.diffOverflow === DiffOverflowType.SCROLL) {
|
||||||
|
return this.makeScrollHeaderHtml(blockHeader, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unrecognised DiffOverflow setting');
|
||||||
|
}
|
||||||
|
|
||||||
|
makeScrollHeaderHtml(blockHeader: string, file?: DiffFile): LineHtml {
|
||||||
|
const left = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
margin_colspan: '1',
|
||||||
|
colspan: '1',
|
||||||
|
blockHeader: file?.isTooBig ? blockHeader : renderUtils.escapeForHtml(blockHeader),
|
||||||
|
lineClass: 'd2h-code-side-linenumber',
|
||||||
|
contentClass: 'd2h-code-side-line',
|
||||||
|
});
|
||||||
|
|
||||||
|
const right = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
margin_colspan: '1',
|
||||||
|
colspan: '1',
|
||||||
|
lineClass: 'd2h-code-side-linenumber',
|
||||||
|
contentClass: 'd2h-code-side-line',
|
||||||
|
});
|
||||||
|
return { left, right };
|
||||||
|
}
|
||||||
|
|
||||||
|
makeWrappedHeaderHtml(blockHeader: string, file?: DiffFile): LineHtml {
|
||||||
|
const table_element = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
|
||||||
CSSLineClass: renderUtils.CSSLineClass,
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
margin_colspan: '1',
|
margin_colspan: '1',
|
||||||
colspan: '3',
|
colspan: '3',
|
||||||
|
|
@ -214,6 +321,10 @@ export default class SideBySideRenderer {
|
||||||
lineClass: 'd2h-code-side-linenumber',
|
lineClass: 'd2h-code-side-linenumber',
|
||||||
contentClass: 'd2h-code-side-line',
|
contentClass: 'd2h-code-side-line',
|
||||||
});
|
});
|
||||||
|
return {
|
||||||
|
left: table_element,
|
||||||
|
right: '',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
|
processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
|
||||||
|
|
@ -267,17 +378,17 @@ export default class SideBySideRenderer {
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
|
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
|
||||||
fileHtml.left.push(...left);
|
fileHtml.left.push(left);
|
||||||
fileHtml.right.push(...right);
|
fileHtml.right.push(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileHtml;
|
return fileHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
|
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): LineHtml {
|
||||||
return {
|
return {
|
||||||
left: [this.generateSingleHtml(oldLine)],
|
left: this.generateSingleHtml(oldLine),
|
||||||
right: [this.generateSingleHtml(newLine)],
|
right: this.generateSingleHtml(newLine),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,6 +428,11 @@ type DiffPreparedLine = {
|
||||||
number: number;
|
number: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type LineHtml = {
|
||||||
|
left: string;
|
||||||
|
right: string;
|
||||||
|
};
|
||||||
|
|
||||||
type FileHtml = {
|
type FileHtml = {
|
||||||
left: string[];
|
left: string[];
|
||||||
right: string[];
|
right: string[];
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<tr>
|
<td class="{{CSSLineClass.INFO}}" colspan="{{#colspan}}{{{colspan}}}{{/colspan}}{{^colspan}}2{{/colspan}}">
|
||||||
<td class="{{CSSLineClass.INFO}}" colspan="{{#colspan}}{{{.}}}{{/colspan}}{{^colspan}}2{{/colspan}}">
|
<div class="{{contentClass}}">
|
||||||
<div class="{{contentClass}}">
|
File without changes
|
||||||
File without changes
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="d2h-file-header">
|
<div class="d2h-file-header">
|
||||||
{{{filePath}}}
|
{{{filePath}}}
|
||||||
</div>
|
</div>
|
||||||
<div class="d2h-file-diff">
|
<div class="d2h-file-diff {{overflowClass}}">
|
||||||
<div class="d2h-code-wrapper">
|
<div class="d2h-code-wrapper">
|
||||||
<table class="d2h-diff-table">
|
<table class="d2h-diff-table">
|
||||||
<thead hidden>
|
<thead hidden>
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,44 @@
|
||||||
{{{filePath}}}
|
{{{filePath}}}
|
||||||
</div>
|
</div>
|
||||||
<div class="d2h-files-diff">
|
<div class="d2h-files-diff">
|
||||||
<div class="d2h-file-side-diff">
|
<div class="d2h-file-side-diff scroll-overflow">
|
||||||
<div class="d2h-code-wrapper">
|
<div class="d2h-code-wrapper">
|
||||||
<table class="d2h-diff-table d2h-split-diff">
|
<table class="d2h-diff-table d2h-split-diff">
|
||||||
<thead hidden>
|
<thead hidden>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Original file line number</th>
|
<th scope="col">Original file line number</th>
|
||||||
<th scope="col">Original file content</th>
|
<th scope="col">Original file content</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 40px;">
|
||||||
|
<col>
|
||||||
|
</colgroup>
|
||||||
|
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
{{{diffs.left}}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-side-diff scroll-overflow">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table d2h-split-diff">
|
||||||
|
<thead hidden>
|
||||||
|
<tr>
|
||||||
<th scope="col">Diff line number</th>
|
<th scope="col">Diff line number</th>
|
||||||
<th scope="col">Diff content</th>
|
<th scope="col">Diff content</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col width="40">
|
<col style="width: 40px;">
|
||||||
<col>
|
|
||||||
<col width="40">
|
|
||||||
<col>
|
<col>
|
||||||
</colgroup>
|
</colgroup>
|
||||||
|
|
||||||
<tbody class="d2h-diff-tbody">
|
<tbody class="d2h-diff-tbody">
|
||||||
{{{diffs}}}
|
{{{diffs.right}}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
32
src/templates/side-by-side-text-wrapped-file-diff.mustache
Normal file
32
src/templates/side-by-side-text-wrapped-file-diff.mustache
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
{{{filePath}}}
|
||||||
|
</div>
|
||||||
|
<div class="d2h-files-diff">
|
||||||
|
<div class="d2h-file-side-diff wrap-overflow">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table d2h-split-diff">
|
||||||
|
<thead hidden>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Original file line number</th>
|
||||||
|
<th scope="col">Original file content</th>
|
||||||
|
<th scope="col">Diff line number</th>
|
||||||
|
<th scope="col">Diff content</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 40px;">
|
||||||
|
<col>
|
||||||
|
<col style="width: 40px;">
|
||||||
|
<col>
|
||||||
|
</colgroup>
|
||||||
|
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
{{{diffs}}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -91,3 +91,10 @@ export const DiffStyleType: { [_: string]: DiffStyleType } = {
|
||||||
WORD: 'word',
|
WORD: 'word',
|
||||||
CHAR: 'char',
|
CHAR: 'char',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DiffOverflowType = 'scroll' | 'wrap';
|
||||||
|
|
||||||
|
export const DiffOverflowType: { [_: string]: DiffOverflowType } = {
|
||||||
|
SCROLL: 'scroll',
|
||||||
|
WRAP: 'wrap',
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
.d2h-file-header.d2h-sticky-header {
|
.d2h-file-header.d2h-sticky-header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-stats {
|
.d2h-file-stats {
|
||||||
|
|
@ -104,13 +104,20 @@
|
||||||
|
|
||||||
.d2h-diff-table {
|
.d2h-diff-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: separate;
|
|
||||||
font-family: 'Menlo', 'Consolas', monospace;
|
font-family: 'Menlo', 'Consolas', monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-diff-table.d2h-split-diff {
|
.scroll-overflow .d2h-diff-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap-overflow .d2h-diff-table {
|
||||||
|
border-collapse: separate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap-overflow .d2h-diff-table.d2h-split-diff {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,8 +131,10 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-side-diff {
|
.d2h-file-diff.scroll-overflow,
|
||||||
width: 100%;
|
.d2h-file-side-diff.scroll-overflow {
|
||||||
|
overflow-x: scroll;
|
||||||
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-code-line,
|
.d2h-code-line,
|
||||||
|
|
@ -138,6 +147,11 @@
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll-overflow .d2h-code-line,
|
||||||
|
.scroll-overflow .d2h-code-side-line {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-code-marker::before {
|
.d2h-code-marker::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
|
|
@ -146,13 +160,22 @@
|
||||||
content: attr(data-code-marker);
|
content: attr(data-code-marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrap-overflow .d2h-code-line-ctn {
|
||||||
|
word-wrap: anywhere;
|
||||||
|
overflow: visible;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-overflow .d2h-code-line-ctn {
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-code-line-ctn {
|
.d2h-code-line-ctn {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
word-wrap: anywhere;
|
word-wrap: normal;
|
||||||
white-space: pre-wrap;
|
white-space: pre;
|
||||||
overflow: visible;
|
|
||||||
user-select: text;
|
user-select: text;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
@ -197,14 +220,38 @@
|
||||||
content: '\200b';
|
content: '\200b';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrap-overflow .d2h-code-side-linenumber,
|
||||||
|
.wrap-overflow .d2h-code-linenumber,
|
||||||
|
.wrap-overflow .d2h-code-emptyplaceholder {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-overflow .d2h-code-side-linenumber,
|
||||||
|
.scroll-overflow .d2h-code-linenumber,
|
||||||
|
.scroll-overflow .d2h-code-emptyplaceholder {
|
||||||
|
position: sticky;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-overflow .d2h-code-side-linenumber {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-overflow.d2h-file-diff tr td:first-child {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-overflow.d2h-file-diff tr td:nth-child(2) {
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-code-side-linenumber,
|
.d2h-code-side-linenumber,
|
||||||
.d2h-code-linenumber {
|
.d2h-code-linenumber {
|
||||||
/* Keep the numbers fixed on line contents scroll */
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: relative;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: rgba(0, 0, 0, 0.3);
|
color: rgba(0, 0, 0, 0.3);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
vertical-align: top;
|
||||||
border: solid #eeeeee;
|
border: solid #eeeeee;
|
||||||
border-width: 0 1px 0 1px;
|
border-width: 0 1px 0 1px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,8 @@ export class Diff2HtmlUI {
|
||||||
this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => {
|
this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => {
|
||||||
const [left, right] = Array<Element>().slice.call(wrapper.querySelectorAll('.d2h-file-side-diff'));
|
const [left, right] = Array<Element>().slice.call(wrapper.querySelectorAll('.d2h-file-side-diff'));
|
||||||
|
|
||||||
|
console.log(left, right);
|
||||||
|
|
||||||
if (left === undefined || right === undefined) return;
|
if (left === undefined || right === undefined) return;
|
||||||
|
|
||||||
const onScroll = (event: Event): void => {
|
const onScroll = (event: Event): void => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue