diff --git a/app/src/components/Sidebar/CodeDiff/Gutters.tsx b/app/src/components/Sidebar/CodeDiff/Gutters.tsx index f1ad076..0190615 100644 --- a/app/src/components/Sidebar/CodeDiff/Gutters.tsx +++ b/app/src/components/Sidebar/CodeDiff/Gutters.tsx @@ -5,7 +5,7 @@ import Remove from '@material-ui/icons/Remove' import ShowChart from '@material-ui/icons/ShowChart' import { JsonPropertyLocation } from '../../../../../backend/src/JsonAstParser' import { lineChangeStyle, trimNewlineRight } from './util' -import { Theme } from '@material-ui/core' +import { Theme, Tooltip } from '@material-ui/core' import { withStyles } from '@material-ui/styles' interface Props { @@ -13,42 +13,55 @@ interface Props { literalPositions: Array classes: any className: string - showDiagram: (dotPath: string, target: EventTarget) => void + showDiagram: (dotPath: string, target: React.Ref) => void hideDiagram: () => void + hasEnoughDataToDisplayDiagrams: boolean } const style = (theme: Theme) => { + const icon = { + verticalAlign: 'top', + width: '12px', + height: '12px', + marginTop: '2px', + borderRadius: '50%', + } + return { + icon, + iconButton: { + ...icon, + width: '16px', + height: '16px', + marginTop: '1px', + padding: '2px', + '&:hover': { + color: theme.palette.primary.contrastText, + backgroundColor: theme.palette.primary.main, + }, + }, gutterLine: { textAlign: 'right' as 'right', paddingRight: theme.spacing(0.5), height: '16px', width: '100%', }, - icon: { - width: '12px', - height: '12px', - marginTop: '2px', - borderRadius: '50%', - '&:hover': { - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.main, - }, - }, } } -function ChartIcon(props: { classes: any, literal: JsonPropertyLocation, showDiagram: (dotPath: string, target: EventTarget) => void, hideDiagram: () => void }) { - const mouseOver = (event: React.MouseEvent) => { - props.showDiagram(props.literal.path, event.target) - } +function ChartIcon(props: { classes: any, literal: JsonPropertyLocation, showDiagram: (dotPath: string, target: React.Ref) => void, hideDiagram: () => void }) { + const chartIconRef = React.useRef(null) - const mouseOut = (event: React.MouseEvent) => { + const mouseOver = React.useCallback((event: React.MouseEvent) => { + props.showDiagram(props.literal.path, chartIconRef) + }, [props.literal.path]) + + const mouseOut = React.useCallback(() => { props.hideDiagram() - } + }, []) return ( - + ) } @@ -56,7 +69,7 @@ function tokensForLine(change: diff.Change, line: number, props: Props) { const { classes, literalPositions } = props const literal = literalPositions[line] - const diagram = literal ? : null + const diagram = literal ? : null if (change.added) { return [diagram, ] diff --git a/app/src/components/Sidebar/CodeDiff/index.tsx b/app/src/components/Sidebar/CodeDiff/index.tsx index 20c7c06..274be61 100644 --- a/app/src/components/Sidebar/CodeDiff/index.tsx +++ b/app/src/components/Sidebar/CodeDiff/index.tsx @@ -5,11 +5,16 @@ import * as React from 'react' import DiffCount from './DiffCount' import Gutters from './Gutters' import TopicPlot from '../TopicPlot' -import { CodeBlockColors, CodeBlockColorsBraceMonokai } from '../CodeBlockColors' +import { + Fade, + Paper, + Popper, + withStyles + } from '@material-ui/core' import { isPlottable, lineChangeStyle, trimNewlineRight } from './util' import { JsonPropertyLocation, literalsMappedByLines } from '../../../../../backend/src/JsonAstParser' -import { Theme, withStyles, Popper, Paper, Fade, Zoom } from '@material-ui/core' import { selectTextWithCtrlA } from '../../../utils/handleTextSelectWithCtrlA' +import { style } from './style' import 'prismjs/components/prism-json' const throttle = require('lodash.throttle') @@ -27,8 +32,8 @@ interface State { } interface DiagramOptions { - dotPath?: string - anchorEl?: EventTarget + dotPath: string + anchorEl: React.Ref } class CodeDiff extends React.Component { @@ -43,33 +48,35 @@ class CodeDiff extends React.Component { this.state = {} } - private showDiagram(dotPath: string, target: EventTarget) { + private showDiagram = (dotPath: string, target: React.Ref) => { this.updateDiagram({ dotPath, anchorEl: target, }) } - private hideDiagram() { + private hideDiagram = () => { this.updateDiagram(undefined) } - 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) || []) - .map((l: JsonPropertyLocation) => isPlottable(l.value) ? l : undefined) - ) as Array + private plottableLiteralsIndexedWithLineNumbers() { + const allLiterals = this.isValidJson(this.props.current) ? (literalsMappedByLines(this.props.current) || []) : [] + return allLiterals + .map((l: JsonPropertyLocation) => isPlottable(l.value) ? l : undefined) as Array + } + + private renderLines(changes: Array) { + const styledLines = Prism.highlight(this.props.current, Prism.languages.json, 'json').split('\n') let lineNumber = 0 - const code = changes.map((change, key) => { + + return changes.map((change, key) => { const hasStyledCode = change.removed !== true const changedLines = change.count || 0 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 @@ -82,23 +89,31 @@ class CodeDiff extends React.Component { return
{line}
}) }).reduce((a, b) => a.concat(b), []) + } + + public render() { + const changes = diff.diffLines(this.props.previous, this.props.current) + const literalPositions = this.plottableLiteralsIndexedWithLineNumbers() + + const code = this.renderLines(changes) const { diagram } = this.state - + const hasEnoughDataToDisplayDiagrams = this.props.messageHistory.count() > 1 return (
this.showDiagram(dotPath, target)} - hideDiagram={() => this.hideDiagram()} + showDiagram={this.showDiagram} + hideDiagram={this.hideDiagram} className={this.props.classes.gutters} + hasEnoughDataToDisplayDiagrams={hasEnoughDataToDisplayDiagrams} changes={changes} literalPositions={literalPositions} />
{code}
@@ -111,67 +126,14 @@ class CodeDiff extends React.Component {
) } -} -const style = (theme: Theme) => { - const codeBlockColors = theme.palette.type === 'light' ? CodeBlockColors : CodeBlockColorsBraceMonokai - - const codeBaseStyle = { - font: "12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace", - display: 'inline-grid' as 'inline-grid', - margin: '0', - padding: '1px 0 0 0', - } - - return { - line: { - lineHeight: 'normal' as 'normal', - paddingLeft: '4px', - width: '100%', - height: '16px', - }, - codeWrapper: { - display: 'flex', - maxHeight: '15em', - overflow: 'auto', - backgroundColor: `${codeBlockColors.background}`, - margin: '8px 0 0 0', - }, - gutters: { - ...codeBaseStyle, - width: '33px', - backgroundColor: codeBlockColors.gutters, - userSelect: 'none' as 'none', - }, - codeBlock: { - ...codeBaseStyle, - width: 'calc(100% - 33px)', - backgroundColor: 'inherit !important', - '& span': { - color: codeBlockColors.text, - }, - '& .token.number': { - color: codeBlockColors.numeric, - }, - '& .token.boolean': { - color: codeBlockColors.numeric, - }, - '& .token.property': { - color: codeBlockColors.variable, - }, - '& .token.string': { - color: codeBlockColors.string, - }, - '& .token': { - color: codeBlockColors.text, - }, - '& .token.operator': { - color: codeBlockColors.text, - }, - '& .token.punctuation': { - color: codeBlockColors.text, - }, - }, + private isValidJson(str: string) { + try { + JSON.parse(str) + return true + } catch (error) { + return false + } } } diff --git a/app/src/components/Sidebar/CodeDiff/util.tsx b/app/src/components/Sidebar/CodeDiff/util.tsx index 89eb453..ab71b47 100644 --- a/app/src/components/Sidebar/CodeDiff/util.tsx +++ b/app/src/components/Sidebar/CodeDiff/util.tsx @@ -53,4 +53,4 @@ export function toPlottableValue(value: any): number | undefined { export function isPlottable(value: any) { return !isNaN(toPlottableValue(value) as any) -} \ No newline at end of file +} diff --git a/app/src/components/Sidebar/HistoryDrawer.tsx b/app/src/components/Sidebar/HistoryDrawer.tsx index e59cc0c..64b0e6c 100644 --- a/app/src/components/Sidebar/HistoryDrawer.tsx +++ b/app/src/components/Sidebar/HistoryDrawer.tsx @@ -34,7 +34,7 @@ class HistoryDrawer extends React.Component { this.setState({ collapsed: !this.state.collapsed }) } - private handleCtrlA = selectTextWithCtrlA({targetSelector: 'pre'}) + private handleCtrlA = selectTextWithCtrlA({ targetSelector: 'pre' }) public renderHistory() { const style = (element: HistoryItem) => ({ diff --git a/backend/src/Model/RingBuffer.ts b/backend/src/Model/RingBuffer.ts index 824308d..519b854 100644 --- a/backend/src/Model/RingBuffer.ts +++ b/backend/src/Model/RingBuffer.ts @@ -62,6 +62,10 @@ export class RingBuffer { return this.items.slice(this.start, this.end) } + public count() { + return this.end - this.start + } + public add(item: T) { const size = item.length this.enforceCapacityConstraints(size)