mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 06:55:47 +01:00
allow syntax highlighting in diffs
This commit is contained in:
@@ -7,8 +7,9 @@ import simpleDiff from "../__resources__/Diff.simple";
|
||||
import hunksDiff from "../__resources__/Diff.hunks";
|
||||
import binaryDiff from "../__resources__/Diff.binary";
|
||||
import Button from "../buttons/Button";
|
||||
import { DiffEventContext } from "./DiffTypes";
|
||||
import { DiffEventContext, File } from "./DiffTypes";
|
||||
import Toast from "../toast/Toast";
|
||||
import { getPath } from "./diffs";
|
||||
|
||||
const diffFiles = parser.parse(simpleDiff);
|
||||
|
||||
@@ -57,4 +58,16 @@ storiesOf("Diff", module)
|
||||
.add("Binaries", () => {
|
||||
const binaryDiffFiles = parser.parse(binaryDiff);
|
||||
return <Diff diff={binaryDiffFiles} />;
|
||||
})
|
||||
.add("SyntaxHighlighting", () => {
|
||||
const filesWithLanguage = diffFiles.map((file: File) => {
|
||||
const ext = getPath(file).split(".")[1];
|
||||
if (ext === "tsx") {
|
||||
file.language = "typescript";
|
||||
} else {
|
||||
file.language = ext;
|
||||
}
|
||||
return file;
|
||||
});
|
||||
return <Diff diff={filesWithLanguage} />;
|
||||
});
|
||||
|
||||
@@ -3,11 +3,12 @@ import { withTranslation, WithTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
// @ts-ignore
|
||||
import { Diff as DiffComponent, getChangeKey, Hunk, Decoration } from "react-diff-view";
|
||||
import { getChangeKey, Hunk, Decoration } from "react-diff-view";
|
||||
import { Button, ButtonGroup } from "../buttons";
|
||||
import Tag from "../Tag";
|
||||
import Icon from "../Icon";
|
||||
import { ChangeEvent, Change, File, Hunk as HunkType, DiffObjectProps } from "./DiffTypes";
|
||||
import TokenizedDiffView from "./TokenizedDiffView";
|
||||
|
||||
const EMPTY_ANNOTATION_FACTORY = {};
|
||||
|
||||
@@ -57,33 +58,6 @@ const ChangeTypeTag = styled(Tag)`
|
||||
margin-left: 0.75rem;
|
||||
`;
|
||||
|
||||
const ModifiedDiffComponent = styled(DiffComponent)`
|
||||
/* align line numbers */
|
||||
& .diff-gutter {
|
||||
text-align: right;
|
||||
}
|
||||
/* column sizing */
|
||||
> colgroup .diff-gutter-col {
|
||||
width: 3.25rem;
|
||||
}
|
||||
/* prevent following content from moving down */
|
||||
> .diff-gutter:empty:hover::after {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
/* smaller font size for code */
|
||||
& .diff-line {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
/* comment padding for sidebyside view */
|
||||
&.split .diff-widget-content .is-indented-line {
|
||||
padding-left: 3.25rem;
|
||||
}
|
||||
/* comment padding for combined view */
|
||||
&.unified .diff-widget-content .is-indented-line {
|
||||
padding-left: 6.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
class DiffFile extends React.Component<Props, State> {
|
||||
static defaultProps: Partial<Props> = {
|
||||
defaultCollapse: false,
|
||||
@@ -264,9 +238,9 @@ class DiffFile extends React.Component<Props, State> {
|
||||
body = (
|
||||
<div className="panel-block is-paddingless">
|
||||
{fileAnnotations}
|
||||
<ModifiedDiffComponent className={viewType} viewType={viewType} hunks={file.hunks} diffType={file.type}>
|
||||
<TokenizedDiffView className={viewType} viewType={viewType} file={file}>
|
||||
{(hunks: HunkType[]) => this.concat(hunks.map(this.renderHunk))}
|
||||
</ModifiedDiffComponent>
|
||||
</TokenizedDiffView>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export type File = {
|
||||
oldPath: string;
|
||||
oldRevision?: string;
|
||||
type: FileChangeType;
|
||||
language?: string;
|
||||
// TODO does this property exists?
|
||||
isBinary?: boolean;
|
||||
};
|
||||
|
||||
29
scm-ui/ui-components/src/repos/Tokenize.worker.ts
Normal file
29
scm-ui/ui-components/src/repos/Tokenize.worker.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
// @ts-ignore we have no types for react-diff-view
|
||||
import { tokenize } from "react-diff-view";
|
||||
import refractor from "./refractorAdapter";
|
||||
|
||||
self.addEventListener("message", ({ data: { id, payload } }) => {
|
||||
const { hunks, language } = payload;
|
||||
const options = {
|
||||
highlight: language !== "text",
|
||||
language: language,
|
||||
refractor
|
||||
};
|
||||
try {
|
||||
const tokens = tokenize(hunks, options);
|
||||
const payload = {
|
||||
success: true,
|
||||
tokens: tokens
|
||||
};
|
||||
// @ts-ignore seems to use wrong typing
|
||||
self.postMessage({ id, payload });
|
||||
} catch (ex) {
|
||||
const payload = {
|
||||
success: false,
|
||||
reason: ex.message
|
||||
};
|
||||
// @ts-ignore seems to use wrong typing
|
||||
self.postMessage({ id, payload });
|
||||
}
|
||||
});
|
||||
62
scm-ui/ui-components/src/repos/TokenizedDiffView.tsx
Normal file
62
scm-ui/ui-components/src/repos/TokenizedDiffView.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
// @ts-ignore we have no typings for react-diff-view
|
||||
import { Diff, useTokenizeWorker } from "react-diff-view";
|
||||
// @ts-ignore we use webpack worker-loader to load the web worker
|
||||
import TokenizeWorker from "./Tokenize.worker";
|
||||
import { File } from "./DiffTypes";
|
||||
|
||||
// styling for the diff tokens
|
||||
// this must be aligned with th style, which is used in the SyntaxHighlighter component
|
||||
import "highlight.js/styles/arduino-light.css";
|
||||
|
||||
const DiffView = styled(Diff)`
|
||||
/* align line numbers */
|
||||
& .diff-gutter {
|
||||
text-align: right;
|
||||
}
|
||||
/* column sizing */
|
||||
> colgroup .diff-gutter-col {
|
||||
width: 3.25rem;
|
||||
}
|
||||
/* prevent following content from moving down */
|
||||
> .diff-gutter:empty:hover::after {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
/* smaller font size for code */
|
||||
& .diff-line {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
/* comment padding for sidebyside view */
|
||||
&.split .diff-widget-content .is-indented-line {
|
||||
padding-left: 3.25rem;
|
||||
}
|
||||
/* comment padding for combined view */
|
||||
&.unified .diff-widget-content .is-indented-line {
|
||||
padding-left: 6.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
// WebWorker which creates tokens for syntax highlighting
|
||||
const tokenize = new TokenizeWorker();
|
||||
|
||||
type Props = {
|
||||
file: File;
|
||||
viewType: "split" | "unified";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const TokenizedDiffView: FC<Props> = ({ file, viewType, className, children }) => {
|
||||
const { tokens } = useTokenizeWorker(tokenize, {
|
||||
hunks: file.hunks,
|
||||
language: file.language || "text"
|
||||
});
|
||||
|
||||
return (
|
||||
<DiffView className={className} viewType={viewType} tokens={tokens} hunks={file.hunks} diffType={file.type}>
|
||||
{children}
|
||||
</DiffView>
|
||||
);
|
||||
};
|
||||
|
||||
export default TokenizedDiffView;
|
||||
14
scm-ui/ui-components/src/repos/refractorAdapter.ts
Normal file
14
scm-ui/ui-components/src/repos/refractorAdapter.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import lowlight from "lowlight";
|
||||
|
||||
// adapter to let lowlight look like refractor
|
||||
// this is required because react-diff-view does only support refractor,
|
||||
// but we want same highlighting as in the source code browser.
|
||||
|
||||
const refractorAdapter = {
|
||||
...lowlight,
|
||||
highlight: (value: string, language: string) => {
|
||||
return lowlight.highlight(language, value).value;
|
||||
}
|
||||
};
|
||||
|
||||
export default refractorAdapter;
|
||||
Reference in New Issue
Block a user