From e66f6d098a14a588cc98eb58b17aad3677b1f7b2 Mon Sep 17 00:00:00 2001 From: Thomas Nordquist Date: Tue, 4 Jun 2019 11:32:59 +0200 Subject: [PATCH] Refactor CodeDiff --- .../components/Sidebar/CodeDiff/DiffCount.tsx | 39 ++++++++ .../components/Sidebar/CodeDiff/Gutters.tsx | 38 +++++++ .../{CodeDiff.tsx => CodeDiff/index.tsx} | 99 +++---------------- app/src/components/Sidebar/CodeDiff/util.tsx | 33 +++++++ backend/src/JsonAstParser.ts | 14 ++- 5 files changed, 136 insertions(+), 87 deletions(-) create mode 100644 app/src/components/Sidebar/CodeDiff/DiffCount.tsx create mode 100644 app/src/components/Sidebar/CodeDiff/Gutters.tsx rename app/src/components/Sidebar/{CodeDiff.tsx => CodeDiff/index.tsx} (50%) create mode 100644 app/src/components/Sidebar/CodeDiff/util.tsx diff --git a/app/src/components/Sidebar/CodeDiff/DiffCount.tsx b/app/src/components/Sidebar/CodeDiff/DiffCount.tsx new file mode 100644 index 0000000..f15860f --- /dev/null +++ b/app/src/components/Sidebar/CodeDiff/DiffCount.tsx @@ -0,0 +1,39 @@ +import * as React from 'react' +import { Theme } from '@material-ui/core'; +import { withStyles } from '@material-ui/styles' + +interface Props { + changes: Array + classes: {[s: string]: any} + nameOfCompareMessage: string +} + +function changeAmount(props: Props) { + const additions = props.changes.map(change => (change.added === true) ? (change.count || 0) : 0).reduce((a, b) => a + b) + const deletions = props.changes.map(change => (change.removed === true) ? (change.count || 0) : 0).reduce((a, b) => a + b) + if (additions === 0 && deletions === 0) { + return null + } + + return ( + + + Comparing with {props.nameOfCompareMessage} message:  + + + {additions} line{additions === 1 ? '' : 's'} + , - {deletions} line{deletions === 1 ? '' : 's'} + + + ) +} + +const style = (theme: Theme) => ({ + additions: { + color: 'rgb(10, 255, 10)', + }, + deletions: { + color: 'rgb(255, 10, 10)', + }, +}) + +export default withStyles(style)(changeAmount) diff --git a/app/src/components/Sidebar/CodeDiff/Gutters.tsx b/app/src/components/Sidebar/CodeDiff/Gutters.tsx new file mode 100644 index 0000000..ee2c6b5 --- /dev/null +++ b/app/src/components/Sidebar/CodeDiff/Gutters.tsx @@ -0,0 +1,38 @@ +import * as diff from 'diff' +import * as React from 'react' +import ShowChart from '@material-ui/icons/ShowChart' +import { JsonPropertyLocation, literalsMappedByLines } from '../../../../../backend/src/JsonAstParser' +import { lineChangeStyle, trimNewlineRight } from './util' +import { Theme } from '@material-ui/core' +import { withStyles } from '@material-ui/styles' + +interface Props { + changes: Array, + literalPositions: Array + classes: any +} + +const style = (theme: Theme) => { + return { + gutterLine: { + textAlign: 'right' as 'right', + paddingRight: theme.spacing(0.5), + }, + } +} + +function Gutters(props: Props) { + const gutters = props.changes.map((change, key) => { + return trimNewlineRight(change.value) + .split('\n') + .map((_, idx) => ( +
+ {change.added ? '+' : null}{change.removed ? '-' : null}{!change.added && !change.removed ? ' ' : null} +
+ )) + }).reduce((a, b) => a.concat(b), []) + + return
{gutters}
+} + +export default withStyles(style)(Gutters) diff --git a/app/src/components/Sidebar/CodeDiff.tsx b/app/src/components/Sidebar/CodeDiff/index.tsx similarity index 50% rename from app/src/components/Sidebar/CodeDiff.tsx rename to app/src/components/Sidebar/CodeDiff/index.tsx index b3f057f..19fd3fd 100644 --- a/app/src/components/Sidebar/CodeDiff.tsx +++ b/app/src/components/Sidebar/CodeDiff/index.tsx @@ -1,10 +1,14 @@ import * as diff from 'diff' import * as Prism from 'prismjs' import * as React from 'react' -import { CodeBlockColors, CodeBlockColorsBraceMonokai } from './CodeBlockColors' -import { selectTextWithCtrlA } from '../../utils/handleTextSelectWithCtrlA' +import DiffCount from './DiffCount' +import { CodeBlockColors, CodeBlockColorsBraceMonokai } from '../CodeBlockColors' +import { literalsMappedByLines, parseJson } from '../../../../../backend/src/JsonAstParser' +import { selectTextWithCtrlA } from '../../../utils/handleTextSelectWithCtrlA' import { Theme, withStyles } from '@material-ui/core' import 'prismjs/components/prism-json' +import { trimNewlineRight, lineChangeStyle } from './util'; +import Gutters from './Gutters' interface Props { previous: string @@ -21,48 +25,10 @@ class CodeDiff extends React.Component { super(props) } - private renderChangeAmount(changes: Array) { - const additions = changes.map(change => (change.added === true) ? (change.count || 0) : 0).reduce((a, b) => a + b) - const deletions = changes.map(change => (change.removed === true) ? (change.count || 0) : 0).reduce((a, b) => a + b) - if (additions === 0 && deletions === 0) { - return null - } - - return ( - - - Comparing with {this.props.nameOfCompareMessage} message:  - - + {additions} line{additions === 1 ? '' : 's'} - , - {deletions} line{deletions === 1 ? '' : 's'} - - - ) - } - - private trimNewlineRight(str: string) { - if (str.slice(-1) === '\n') { - return str.slice(0, -1) - } - - return str - } - - private cssClassForChange(change: diff.Change) { - if (change.added === true) { - return this.props.classes.addition - } - - if (change.removed === true) { - return this.props.classes.deletion - } - - return this.props.classes.noChange - } - public render() { const changes = diff.diffLines(this.props.previous, this.props.current) const styledLines = Prism.highlight(this.props.current, Prism.languages.json, 'json').split('\n') + const literalPositions = literalsMappedByLines(this.props.current) let lineNumber = 0 const code = changes.map((change, key) => { @@ -71,37 +37,27 @@ class CodeDiff extends React.Component { if (hasStyledCode && this.props.language === 'json') { const currentLines = styledLines.slice(lineNumber, lineNumber + changedLines) const lines = currentLines.map((html: string, idx: number) => { - return
+ return
}) lineNumber += changedLines return
{lines}
} - return this.trimNewlineRight(change.value) + return trimNewlineRight(change.value) .split('\n') .map((line, idx) => { - return
{line}
+ return
{line}
}) }) - const gutters = changes.map((change, key) => { - return this.trimNewlineRight(change.value) - .split('\n') - .map((_, idx) => ( -
- {change.added ? '+' : null}{change.removed ? '-' : null}{!change.added && !change.removed ? ' ' : null} -
- )) - }) - return (
-
{gutters}
+
{code}
- {this.renderChangeAmount(changes)} +
) } @@ -109,9 +65,6 @@ class CodeDiff extends React.Component { const style = (theme: Theme) => { const codeBlockColors = theme.palette.type === 'light' ? CodeBlockColors : CodeBlockColorsBraceMonokai - const gutterBaseStyle = { - width: '100%', - } const codeBaseStyle = { font: "12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace", @@ -121,19 +74,10 @@ const style = (theme: Theme) => { } return { - additions: { - color: 'rgb(10, 255, 10)', - }, - deletions: { - color: 'rgb(255, 10, 10)', - }, line: { lineHeight: 'normal' as 'normal', paddingLeft: '4px', - }, - gutterLine: { - textAlign: 'right' as 'right', - paddingRight: theme.spacing(0.5), + width: '100%', }, codeWrapper: { maxHeight: '15em', @@ -176,23 +120,6 @@ const style = (theme: Theme) => { color: codeBlockColors.text, }, }, - noChange: { - ...gutterBaseStyle, - }, - deletion: { - ...gutterBaseStyle, - backgroundColor: 'rgba(255, 10, 10, 0.3)', - // '&:hover': { - // backgroundColor: 'rgba(255, 10, 10, 0.3)', - // }, - }, - addition: { - ...gutterBaseStyle, - backgroundColor: 'rgba(10, 255, 10, 0.3)', - // '&:hover': { - // backgroundColor: 'rgba(10, 255, 10, 0.5)', - // }, - }, } } diff --git a/app/src/components/Sidebar/CodeDiff/util.tsx b/app/src/components/Sidebar/CodeDiff/util.tsx new file mode 100644 index 0000000..b203034 --- /dev/null +++ b/app/src/components/Sidebar/CodeDiff/util.tsx @@ -0,0 +1,33 @@ +export function trimNewlineRight(str: string) { + if (str.slice(-1) === '\n') { + return str.slice(0, -1) + } + + return str +} + +const gutterBaseStyle = { + width: '100%', +} + +const additionStyle = { + ...gutterBaseStyle, + backgroundColor: 'rgba(10, 255, 10, 0.3)', +} + +const deletionStyle = { + ...gutterBaseStyle, + backgroundColor: 'rgba(255, 10, 10, 0.3)', +} + +export function lineChangeStyle(change: Diff.Change) { + if (change.added === true) { + return additionStyle + } + + if (change.removed === true) { + return deletionStyle + } + + return gutterBaseStyle +} \ No newline at end of file diff --git a/backend/src/JsonAstParser.ts b/backend/src/JsonAstParser.ts index b6bb6c0..543b1ea 100644 --- a/backend/src/JsonAstParser.ts +++ b/backend/src/JsonAstParser.ts @@ -1,8 +1,9 @@ const parse = require('json-to-ast') -interface JsonPropertyLocation { +export interface JsonPropertyLocation { path: string line: number + value: any column: number } @@ -59,6 +60,7 @@ function jsonToPropertyPaths(ast: JsonAst, previousPath: Array = []): Ar let children: Array> = [] if (ast.type === 'Literal') { return [{ + value: ast.value, path: previousPath.join('.'), line: ast.loc.start.line, column: ast.loc.start.column, @@ -75,3 +77,13 @@ function jsonToPropertyPaths(ast: JsonAst, previousPath: Array = []): Ar export function parseJson(formattedJson: string): Array { return jsonToPropertyPaths((parse(formattedJson) as JsonAst), []) } + +export function literalsMappedByLines(formattedJson: string): Array { + const literals = jsonToPropertyPaths((parse(formattedJson) as JsonAst), []) + const lines = [] + for (const literal of literals) { + lines[literal.line - 1] = literal + } + + return lines +}