mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-15 09:46:16 +01:00
Introduce expandable diffs
This commit is contained in:
312
scm-ui/ui-components/src/repos/DiffExpander.test.ts
Normal file
312
scm-ui/ui-components/src/repos/DiffExpander.test.ts
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import DiffExpander from "./DiffExpander";
|
||||||
|
|
||||||
|
const HUNK_0 = {
|
||||||
|
content: "@@ -1,8 +1,8 @@",
|
||||||
|
oldStart: 1,
|
||||||
|
newStart: 1,
|
||||||
|
oldLines: 8,
|
||||||
|
newLines: 8,
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
content: "// @flow",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 1,
|
||||||
|
newLineNumber: 1,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import React from "react";',
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 2,
|
||||||
|
newLineNumber: 2,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import { translate } from "react-i18next";',
|
||||||
|
type: "delete",
|
||||||
|
lineNumber: 3,
|
||||||
|
isDelete: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import { Textarea } from "@scm-manager/ui-components";',
|
||||||
|
type: "delete",
|
||||||
|
lineNumber: 4,
|
||||||
|
isDelete: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import type { Me } from "@scm-manager/ui-types";',
|
||||||
|
type: "delete",
|
||||||
|
lineNumber: 5,
|
||||||
|
isDelete: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import {translate} from "react-i18next";',
|
||||||
|
type: "insert",
|
||||||
|
lineNumber: 3,
|
||||||
|
isInsert: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import {Textarea} from "@scm-manager/ui-components";',
|
||||||
|
type: "insert",
|
||||||
|
lineNumber: 4,
|
||||||
|
isInsert: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import type {Me} from "@scm-manager/ui-types";',
|
||||||
|
type: "insert",
|
||||||
|
lineNumber: 5,
|
||||||
|
isInsert: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'import injectSheet from "react-jss";',
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 6,
|
||||||
|
newLineNumber: 6,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 7,
|
||||||
|
newLineNumber: 7,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "const styles = {",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 8,
|
||||||
|
newLineNumber: 8,
|
||||||
|
isNormal: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const HUNK_1 = {
|
||||||
|
content: "@@ -14,6 +14,7 @@",
|
||||||
|
oldStart: 14,
|
||||||
|
newStart: 14,
|
||||||
|
oldLines: 6,
|
||||||
|
newLines: 7,
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
content: "type Props = {",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 14,
|
||||||
|
newLineNumber: 14,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " me: Me,",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 15,
|
||||||
|
newLineNumber: 15,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " onChange: string => void,",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 16,
|
||||||
|
newLineNumber: 16,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " disabled: boolean,",
|
||||||
|
type: "insert",
|
||||||
|
lineNumber: 17,
|
||||||
|
isInsert: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " //context props",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 17,
|
||||||
|
newLineNumber: 18,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " t: string => string,",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 18,
|
||||||
|
newLineNumber: 19,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " classes: any",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 19,
|
||||||
|
newLineNumber: 20,
|
||||||
|
isNormal: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const HUNK_2 = {
|
||||||
|
content: "@@ -21,7 +22,7 @@",
|
||||||
|
oldStart: 21,
|
||||||
|
newStart: 22,
|
||||||
|
oldLines: 7,
|
||||||
|
newLines: 7,
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
content: "",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 21,
|
||||||
|
newLineNumber: 22,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "class CommitMessage extends React.Component<Props> {",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 22,
|
||||||
|
newLineNumber: 23,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " render() {",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 23,
|
||||||
|
newLineNumber: 24,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " const { t, classes, me, onChange } = this.props;",
|
||||||
|
type: "delete",
|
||||||
|
lineNumber: 24,
|
||||||
|
isDelete: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " const {t, classes, me, onChange, disabled} = this.props;",
|
||||||
|
type: "insert",
|
||||||
|
lineNumber: 25,
|
||||||
|
isInsert: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " return (",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 25,
|
||||||
|
newLineNumber: 26,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " <>",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 26,
|
||||||
|
newLineNumber: 27,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " <div className={classes.marginBottom}>",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 27,
|
||||||
|
newLineNumber: 28,
|
||||||
|
isNormal: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const HUNK_3 = {
|
||||||
|
content: "@@ -33,6 +34,7 @@",
|
||||||
|
oldStart: 33,
|
||||||
|
newStart: 34,
|
||||||
|
oldLines: 6,
|
||||||
|
newLines: 7,
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
content: " <Textarea",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 33,
|
||||||
|
newLineNumber: 34,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: ' placeholder={t("scm-editor-plugin.commit.placeholder")}',
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 34,
|
||||||
|
newLineNumber: 35,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " onChange={message => onChange(message)}",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 35,
|
||||||
|
newLineNumber: 36,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " disabled={disabled}",
|
||||||
|
type: "insert",
|
||||||
|
lineNumber: 37,
|
||||||
|
isInsert: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " />",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 36,
|
||||||
|
newLineNumber: 38,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " </>",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 37,
|
||||||
|
newLineNumber: 39,
|
||||||
|
isNormal: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: " );",
|
||||||
|
type: "normal",
|
||||||
|
oldLineNumber: 38,
|
||||||
|
newLineNumber: 40,
|
||||||
|
isNormal: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const TEST_CONTENT = {
|
||||||
|
oldPath: "src/main/js/CommitMessage.js",
|
||||||
|
newPath: "src/main/js/CommitMessage.js",
|
||||||
|
oldEndingNewLine: true,
|
||||||
|
newEndingNewLine: true,
|
||||||
|
oldRevision: "e05c8495bb1dc7505d73af26210c8ff4825c4500",
|
||||||
|
newRevision: "4305a8df175b7bec25acbe542a13fbe2a718a608",
|
||||||
|
type: "modify",
|
||||||
|
language: "javascript",
|
||||||
|
hunks: [HUNK_0, HUNK_1, HUNK_2, HUNK_3],
|
||||||
|
_links: {
|
||||||
|
lines: {
|
||||||
|
href:
|
||||||
|
"http://localhost:8081/scm/api/v2/repositories/scm-manager/scm-editor-plugin/content/f7a23064f3f2418f26140a9545559e72d595feb5/src/main/js/CommitMessage.js?start={start}?end={end}",
|
||||||
|
templated: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("diff expander", () => {
|
||||||
|
const diffExpander = new DiffExpander(TEST_CONTENT);
|
||||||
|
it("should have hunk count from origin", () => {
|
||||||
|
expect(diffExpander.hunkCount()).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return correct hunk", () => {
|
||||||
|
expect(diffExpander.getHunk(1).hunk).toBe(HUNK_1);
|
||||||
|
});
|
||||||
|
});
|
||||||
61
scm-ui/ui-components/src/repos/DiffExpander.ts
Normal file
61
scm-ui/ui-components/src/repos/DiffExpander.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { File, Hunk } from "./DiffTypes";
|
||||||
|
|
||||||
|
class DiffExpander {
|
||||||
|
file: File;
|
||||||
|
|
||||||
|
constructor(file: File) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
hunkCount = () => {
|
||||||
|
return this.file.hunks.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
getHunk: (n: number) => ExpandableHunk = (n: number) => {
|
||||||
|
return {
|
||||||
|
maxExpandHeadRange: 10,
|
||||||
|
maxExpandBottomRange: 10,
|
||||||
|
expandHead: () => {
|
||||||
|
console.log("expand head", n);
|
||||||
|
},
|
||||||
|
expandBottom: () => {
|
||||||
|
console.log("expand bottom", n);
|
||||||
|
},
|
||||||
|
hunk: this.file.hunks[n]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExpandableHunk = {
|
||||||
|
hunk: Hunk;
|
||||||
|
maxExpandHeadRange: number;
|
||||||
|
maxExpandBottomRange: number;
|
||||||
|
expandHead: () => void;
|
||||||
|
expandBottom: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiffExpander;
|
||||||
@@ -34,6 +34,7 @@ import { Change, ChangeEvent, DiffObjectProps, File, Hunk as HunkType } from "./
|
|||||||
import TokenizedDiffView from "./TokenizedDiffView";
|
import TokenizedDiffView from "./TokenizedDiffView";
|
||||||
import DiffButton from "./DiffButton";
|
import DiffButton from "./DiffButton";
|
||||||
import { MenuContext } from "@scm-manager/ui-components";
|
import { MenuContext } from "@scm-manager/ui-components";
|
||||||
|
import DiffExpander, { ExpandableHunk } from "./DiffExpander";
|
||||||
|
|
||||||
const EMPTY_ANNOTATION_FACTORY = {};
|
const EMPTY_ANNOTATION_FACTORY = {};
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ type Collapsible = {
|
|||||||
|
|
||||||
type State = Collapsible & {
|
type State = Collapsible & {
|
||||||
sideBySide?: boolean;
|
sideBySide?: boolean;
|
||||||
|
diffExpander: DiffExpander;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DiffFilePanel = styled.div`
|
const DiffFilePanel = styled.div`
|
||||||
@@ -74,8 +76,9 @@ const ButtonWrapper = styled.div`
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const HunkDivider = styled.hr`
|
const HunkDivider = styled.div`
|
||||||
margin: 0.5rem 0;
|
background: #33b2e8;
|
||||||
|
font-size: 0.7rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChangeTypeTag = styled(Tag)`
|
const ChangeTypeTag = styled(Tag)`
|
||||||
@@ -92,7 +95,8 @@ class DiffFile extends React.Component<Props, State> {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
collapsed: this.defaultCollapse(),
|
collapsed: this.defaultCollapse(),
|
||||||
sideBySide: props.sideBySide
|
sideBySide: props.sideBySide,
|
||||||
|
diffExpander: new DiffExpander(props.file)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,9 +143,25 @@ class DiffFile extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
createHunkHeader = (hunk: HunkType, i: number) => {
|
createHunkHeader = (expandableHunk: ExpandableHunk) => {
|
||||||
if (i > 0) {
|
if (expandableHunk.maxExpandHeadRange > 0) {
|
||||||
return <HunkDivider />;
|
return (
|
||||||
|
<Decoration>
|
||||||
|
<HunkDivider onClick={expandableHunk.expandHead}>{"Load first n lines"}</HunkDivider>
|
||||||
|
</Decoration>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// hunk header must be defined
|
||||||
|
return <span />;
|
||||||
|
};
|
||||||
|
|
||||||
|
createHunkFooter = (expandableHunk: ExpandableHunk) => {
|
||||||
|
if (expandableHunk.maxExpandBottomRange > 0) {
|
||||||
|
return (
|
||||||
|
<Decoration>
|
||||||
|
<HunkDivider onClick={expandableHunk.expandBottom}>{"Load last n lines"}</HunkDivider>
|
||||||
|
</Decoration>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// hunk header must be defined
|
// hunk header must be defined
|
||||||
return <span />;
|
return <span />;
|
||||||
@@ -183,19 +203,27 @@ class DiffFile extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderHunk = (hunk: HunkType, i: number) => {
|
renderHunk = (file: File, expandableHunk: ExpandableHunk, i: number) => {
|
||||||
|
const hunk = expandableHunk.hunk;
|
||||||
if (this.props.markConflicts && hunk.changes) {
|
if (this.props.markConflicts && hunk.changes) {
|
||||||
this.markConflicts(hunk);
|
this.markConflicts(hunk);
|
||||||
}
|
}
|
||||||
return [
|
const items = [];
|
||||||
<Decoration key={"decoration-" + hunk.content}>{this.createHunkHeader(hunk, i)}</Decoration>,
|
if (file._links?.lines) {
|
||||||
|
items.push(this.createHunkHeader(expandableHunk));
|
||||||
|
}
|
||||||
|
items.push(
|
||||||
<Hunk
|
<Hunk
|
||||||
key={"hunk-" + hunk.content}
|
key={"hunk-" + hunk.content}
|
||||||
hunk={hunk}
|
hunk={expandableHunk.hunk}
|
||||||
widgets={this.collectHunkAnnotations(hunk)}
|
widgets={this.collectHunkAnnotations(hunk)}
|
||||||
gutterEvents={this.createGutterEvents(hunk)}
|
gutterEvents={this.createGutterEvents(hunk)}
|
||||||
/>
|
/>
|
||||||
];
|
);
|
||||||
|
if (file._links?.lines) {
|
||||||
|
items.push(this.createHunkFooter(expandableHunk));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
};
|
};
|
||||||
|
|
||||||
markConflicts = (hunk: HunkType) => {
|
markConflicts = (hunk: HunkType) => {
|
||||||
@@ -251,19 +279,11 @@ class DiffFile extends React.Component<Props, State> {
|
|||||||
return <ChangeTypeTag className={classNames("is-rounded", "has-text-weight-normal")} color={color} label={value} />;
|
return <ChangeTypeTag className={classNames("is-rounded", "has-text-weight-normal")} color={color} label={value} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
concat = (array: object[][]) => {
|
|
||||||
if (array.length > 0) {
|
|
||||||
return array.reduce((a, b) => a.concat(b));
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
hasContent = (file: File) => file && !file.isBinary && file.hunks && file.hunks.length > 0;
|
hasContent = (file: File) => file && !file.isBinary && file.hunks && file.hunks.length > 0;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { file, fileControlFactory, fileAnnotationFactory, t } = this.props;
|
const { file, fileControlFactory, fileAnnotationFactory, t } = this.props;
|
||||||
const { collapsed, sideBySide } = this.state;
|
const { collapsed, sideBySide, diffExpander } = this.state;
|
||||||
const viewType = sideBySide ? "split" : "unified";
|
const viewType = sideBySide ? "split" : "unified";
|
||||||
|
|
||||||
let body = null;
|
let body = null;
|
||||||
@@ -275,7 +295,7 @@ class DiffFile extends React.Component<Props, State> {
|
|||||||
<div className="panel-block is-paddingless">
|
<div className="panel-block is-paddingless">
|
||||||
{fileAnnotations}
|
{fileAnnotations}
|
||||||
<TokenizedDiffView className={viewType} viewType={viewType} file={file}>
|
<TokenizedDiffView className={viewType} viewType={viewType} file={file}>
|
||||||
{(hunks: HunkType[]) => this.concat(hunks.map(this.renderHunk))}
|
{(hunks: HunkType[]) => hunks.map((hunk, n) => this.renderHunk(file, diffExpander.getHunk(n), n))}
|
||||||
</TokenizedDiffView>
|
</TokenizedDiffView>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { DefaultCollapsed } from "./defaultCollapsed";
|
import { DefaultCollapsed } from "./defaultCollapsed";
|
||||||
|
import { Links } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
// We place the types here and not in @scm-manager/ui-types,
|
// We place the types here and not in @scm-manager/ui-types,
|
||||||
// because they represent not a real scm-manager related type.
|
// because they represent not a real scm-manager related type.
|
||||||
@@ -46,11 +47,16 @@ export type File = {
|
|||||||
language?: string;
|
language?: string;
|
||||||
// TODO does this property exists?
|
// TODO does this property exists?
|
||||||
isBinary?: boolean;
|
isBinary?: boolean;
|
||||||
|
_links: Links;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Hunk = {
|
export type Hunk = {
|
||||||
changes: Change[];
|
changes: Change[];
|
||||||
content: string;
|
content: string;
|
||||||
|
oldStart: number;
|
||||||
|
newStart: number;
|
||||||
|
oldLines: number;
|
||||||
|
newLines: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChangeType = "insert" | "delete" | "normal" | "conflict";
|
export type ChangeType = "insert" | "delete" | "normal" | "conflict";
|
||||||
|
|||||||
Reference in New Issue
Block a user