diff --git a/app/src/components/Sidebar/CodeDiff/Gutters.tsx b/app/src/components/Sidebar/CodeDiff/Gutters.tsx index ee2c6b5..78d5cdf 100644 --- a/app/src/components/Sidebar/CodeDiff/Gutters.tsx +++ b/app/src/components/Sidebar/CodeDiff/Gutters.tsx @@ -1,7 +1,7 @@ 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 { JsonPropertyLocation } from '../../../../../backend/src/JsonAstParser' import { lineChangeStyle, trimNewlineRight } from './util' import { Theme } from '@material-ui/core' import { withStyles } from '@material-ui/styles' @@ -15,21 +15,39 @@ interface Props { const style = (theme: Theme) => { return { gutterLine: { + display: 'flex' as 'flex', textAlign: 'right' as 'right', paddingRight: theme.spacing(0.5), + height: '16px', }, } } +function tokensForLine(change: diff.Change, line: number, literalPositions: Array) { + let diagram = literalPositions[line] ? : '' + + if (change.added) { + return [diagram, '+'] + } else if (change.removed) { + return '-' + } else { + return [diagram, ' '] + } +} + function Gutters(props: Props) { + let currentLine = -1 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} -
- )) + .map((_, idx) => { + currentLine = !change.removed ? currentLine + 1 : currentLine + return ( +
+ {tokensForLine(change, currentLine, props.literalPositions)} +
+ ) + }) }).reduce((a, b) => a.concat(b), []) return
{gutters}
diff --git a/app/src/components/Sidebar/CodeDiff/index.tsx b/app/src/components/Sidebar/CodeDiff/index.tsx index 19fd3fd..a0f9a1a 100644 --- a/app/src/components/Sidebar/CodeDiff/index.tsx +++ b/app/src/components/Sidebar/CodeDiff/index.tsx @@ -3,7 +3,7 @@ import * as Prism from 'prismjs' import * as React from 'react' import DiffCount from './DiffCount' import { CodeBlockColors, CodeBlockColorsBraceMonokai } from '../CodeBlockColors' -import { literalsMappedByLines, parseJson } from '../../../../../backend/src/JsonAstParser' +import { literalsMappedByLines } from '../../../../../backend/src/JsonAstParser' import { selectTextWithCtrlA } from '../../../utils/handleTextSelectWithCtrlA' import { Theme, withStyles } from '@material-ui/core' import 'prismjs/components/prism-json' @@ -28,7 +28,7 @@ class CodeDiff extends React.Component { 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) + const literalPositions = literalsMappedByLines(this.props.current) || [] let lineNumber = 0 const code = changes.map((change, key) => { @@ -78,6 +78,7 @@ const style = (theme: Theme) => { lineHeight: 'normal' as 'normal', paddingLeft: '4px', width: '100%', + height: '16px', }, codeWrapper: { maxHeight: '15em', diff --git a/backend/src/JsonAstParser.ts b/backend/src/JsonAstParser.ts index 543b1ea..730137a 100644 --- a/backend/src/JsonAstParser.ts +++ b/backend/src/JsonAstParser.ts @@ -74,16 +74,24 @@ function jsonToPropertyPaths(ast: JsonAst, previousPath: Array = []): Ar return children.reduce((a, b) => a.concat(b), []) } +// Used for testing only export function parseJson(formattedJson: string): Array { - return jsonToPropertyPaths((parse(formattedJson) as JsonAst), []) + const parsedJson = parse(formattedJson) as JsonAst + + return jsonToPropertyPaths(parsedJson, []) } -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 +export function literalsMappedByLines(formattedJson: string): Array | undefined { + try { + const parsedJson = parse(formattedJson) as JsonAst + const literals = jsonToPropertyPaths(parsedJson, []) + const lines = [] + for (const literal of literals) { + lines[literal.line - 1] = literal + } + + return lines + } catch (error) { + return undefined } - - return lines } diff --git a/backend/src/spec/JsonAstParser.spec.ts b/backend/src/spec/JsonAstParser.spec.ts index 37d43e8..35131de 100644 --- a/backend/src/spec/JsonAstParser.spec.ts +++ b/backend/src/spec/JsonAstParser.spec.ts @@ -1,28 +1,28 @@ -import 'mocha' - import { expect } from 'chai' import { parseJson } from '../JsonAstParser' +import 'mocha' + const dotProp = require('dot-prop') describe('access JSON values via dot property paths', () => { it('object with literal', () => { - let data = { + const data = { foo: 4, } - let result = parseJson(JSON.stringify(data, undefined, 2)) + const result = parseJson(JSON.stringify(data, undefined, 2)) expect(result[0].path).to.eq('foo') expect(result[0].line).to.eq(2) }) it('nested object', () => { - let data = { + const data = { foo: { - bar: 4 + bar: 4, }, } - let result = parseJson(JSON.stringify(data, undefined, 2)) + const result = parseJson(JSON.stringify(data, undefined, 2)) expect(result[0].path).to.eq('foo.bar') expect(result[0].line).to.eq(3) expect(dotProp.get(data, result[0].path)).to.eq(4) @@ -30,7 +30,7 @@ describe('access JSON values via dot property paths', () => { }) it('array path', () => { - let data = { + const data = { foo: [ 1, 2, @@ -38,11 +38,17 @@ describe('access JSON values via dot property paths', () => { ], } - let result = parseJson(JSON.stringify(data, undefined, 2)) + const result = parseJson(JSON.stringify(data, undefined, 2)) expect(result.length).to.eq(3) expect(result[2].path).to.eq('foo.2') expect(result[2].line).to.eq(5) expect(dotProp.get(data, result[2].path)).to.eq(3) }) + + it('should fail parsing invalid json', () => { + expect(() => { + const result = parseJson("BLE2MQTT-8C48") + }).to.throw() + }) })