mirror of
https://github.com/bendtherules/completion-viz.git
synced 2026-08-18 13:42:51 +00:00
Add basic CompletionViewer
This commit is contained in:
+16
-6
@@ -1,8 +1,10 @@
|
|||||||
import React, { useState, useLayoutEffect } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { SourceViewer } from "./components/SourceViewer";
|
import { SourceViewer } from "./components/SourceViewer";
|
||||||
import { CompletionViewer } from "./components/CompletionViewer";
|
import { CompletionViewer } from "./components/CompletionViewer";
|
||||||
|
|
||||||
import { completionMapping } from "./logic/completionMapping";
|
import { completionMapping } from "./logic/completionMapping";
|
||||||
|
import { sourceMapping } from "./logic/sourceMapping";
|
||||||
import { runEngine } from "./logic/runEngine";
|
import { runEngine } from "./logic/runEngine";
|
||||||
|
|
||||||
import "./tailwind.output.css";
|
import "./tailwind.output.css";
|
||||||
@@ -19,11 +21,19 @@ function App() {
|
|||||||
// }, [inputCode]);
|
// }, [inputCode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="flex flex-col justify-between md:flex-row">
|
||||||
<SourceViewer code={inputCode} onChangeCode={setInputCode} />
|
<div className="flex-1 max-w-half">
|
||||||
<CompletionViewer
|
<SourceViewer code={inputCode} onChangeCode={setInputCode} />
|
||||||
completionDetails={completionMapping.completionDetails}
|
</div>
|
||||||
/>
|
<div className="flex-1 max-w-half">
|
||||||
|
{/* Separator */}
|
||||||
|
{/* <div className="w-2 mx-1 bg-gray-800"></div> */}
|
||||||
|
<CompletionViewer
|
||||||
|
code={inputCode}
|
||||||
|
completionDetails={completionMapping.completionDetails}
|
||||||
|
sourceMapping={sourceMapping}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ICompletionDetail } from "../../logic/completionMapping";
|
||||||
|
import { TSourceMapping } from "../../logic/sourceMapping";
|
||||||
|
import { CompletionTypes } from "../../types/engine262-stubs";
|
||||||
|
|
||||||
|
import { InlineResultViewer } from "../InlineResultViewer";
|
||||||
|
import { CompletionIcon } from "./CompletionIcon";
|
||||||
|
|
||||||
|
function getCompletionClassname(inputCompletion: ICompletionDetail) {
|
||||||
|
const typeToClassnameMapping: { [key in CompletionTypes]: string } = {
|
||||||
|
[CompletionTypes.Normal]: "bg-gray-300",
|
||||||
|
[CompletionTypes.Return]: "bg-green-200",
|
||||||
|
[CompletionTypes.Continue]: "bg-yellow-100",
|
||||||
|
[CompletionTypes.Break]: "bg-yellow-300",
|
||||||
|
[CompletionTypes.Throw]: "bg-red-300",
|
||||||
|
};
|
||||||
|
|
||||||
|
return typeToClassnameMapping[inputCompletion.completionType];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCodeTextFromCompletion(
|
||||||
|
code: string,
|
||||||
|
completion: ICompletionDetail
|
||||||
|
) {
|
||||||
|
return code.substring(completion.start, completion.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICompletionCardProps {
|
||||||
|
completionDetail: ICompletionDetail;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CompletionCard(props: ICompletionCardProps) {
|
||||||
|
const { completionDetail, code } = props;
|
||||||
|
|
||||||
|
// TODO: have diff version to be shown as tooltip over source code
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`px-4 py-1 my-1 rounded-md ${getCompletionClassname(
|
||||||
|
completionDetail
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center my-1">
|
||||||
|
<div className="flex-initial flex-shrink-0">
|
||||||
|
<CompletionIcon completionDetail={completionDetail} />
|
||||||
|
</div>
|
||||||
|
<div className="flex-initial min-w-0">
|
||||||
|
<InlineResultViewer
|
||||||
|
className="px-1 rounded"
|
||||||
|
code={completionDetail.completionValueString || "// No value"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="my-1">From source ⤵️ </div>
|
||||||
|
<InlineResultViewer
|
||||||
|
className="px-1 py-1 my-1 rounded md:w-fit-content"
|
||||||
|
code={getCodeTextFromCompletion(code, completionDetail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="my-1">Full completion record ⤵️ </div>
|
||||||
|
<table className="border border-gray-800">
|
||||||
|
<tr className="border-b border-gray-800">
|
||||||
|
<td className="p-1 border-r border-gray-800">[[Type]]</td>
|
||||||
|
<td className="px-2 py-1">{completionDetail.completionType}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-gray-800">
|
||||||
|
<td className="p-1 border-r border-gray-800">[[Value]]</td>
|
||||||
|
<td className="px-2 py-1">
|
||||||
|
{completionDetail.completionValueString}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-gray-800">
|
||||||
|
<td className="p-1 border-r border-gray-800">[[Target]]</td>
|
||||||
|
<td className="px-2 py-1">
|
||||||
|
{completionDetail.completionTargetString}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ICompletionDetail } from "../../logic/completionMapping";
|
||||||
|
import { CompletionTypes } from "../../types/engine262-stubs";
|
||||||
|
|
||||||
|
export interface ICompletionIconProps {
|
||||||
|
completionDetail: ICompletionDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const typeToLetterMapping: {
|
||||||
|
[key in CompletionTypes]: string;
|
||||||
|
} = {
|
||||||
|
[CompletionTypes.Normal]: "N",
|
||||||
|
[CompletionTypes.Return]: "R",
|
||||||
|
[CompletionTypes.Continue]: "C",
|
||||||
|
[CompletionTypes.Break]: "B",
|
||||||
|
[CompletionTypes.Throw]: "T",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CompletionIcon(props: ICompletionIconProps) {
|
||||||
|
const { completionDetail } = props;
|
||||||
|
|
||||||
|
const tmpLetter = typeToLetterMapping[completionDetail.completionType];
|
||||||
|
return (
|
||||||
|
// Icon container
|
||||||
|
<div className="flex items-center justify-center w-8 h-8 mr-2 text-base font-semibold align-middle bg-white rounded-circle ">
|
||||||
|
<div>{tmpLetter}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { CompletionCard } from "./CompletionCard";
|
||||||
|
|
||||||
|
export { CompletionCard };
|
||||||
@@ -1,7 +1,32 @@
|
|||||||
|
import React from "react";
|
||||||
import { ICompletionDetail } from "../../logic/completionMapping";
|
import { ICompletionDetail } from "../../logic/completionMapping";
|
||||||
|
import { TSourceMapping } from "../../logic/sourceMapping";
|
||||||
|
|
||||||
|
import { CompletionCard } from "../CompletionCard";
|
||||||
|
|
||||||
export function CompletionViewer(props: {
|
export function CompletionViewer(props: {
|
||||||
|
code: string;
|
||||||
completionDetails: ICompletionDetail[];
|
completionDetails: ICompletionDetail[];
|
||||||
|
sourceMapping: TSourceMapping;
|
||||||
}) {
|
}) {
|
||||||
return null;
|
const { code, completionDetails } = props;
|
||||||
|
|
||||||
|
// function renderCompletionResult(inputCompletion: ICompletionDetail) {}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
{/* Title for completion section */}
|
||||||
|
<div className="px-4 py-1 bg-gray-200 ">Completion Records</div>
|
||||||
|
{/* Completion list */}
|
||||||
|
<div className="ml-1 mr-1">
|
||||||
|
{completionDetails.map((tmpCompletion) => (
|
||||||
|
// Card for each completion record
|
||||||
|
<CompletionCard
|
||||||
|
completionDetail={tmpCompletion}
|
||||||
|
code={code}
|
||||||
|
></CompletionCard>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Highlight, { defaultProps } from "prism-react-renderer";
|
||||||
|
|
||||||
|
interface IInlineResultViewerProps {
|
||||||
|
code: string;
|
||||||
|
className: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InlineResultViewer(props: IInlineResultViewerProps) {
|
||||||
|
const { code, className: classNameFromProps } = props;
|
||||||
|
return (
|
||||||
|
<Highlight {...defaultProps} code={code} language="javascript">
|
||||||
|
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||||
|
<pre
|
||||||
|
className={`${className} ${classNameFromProps} overflow-auto`}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{tokens.map((line, i) => (
|
||||||
|
<div {...getLineProps({ line, key: i })}>
|
||||||
|
{line.map((token, key) => (
|
||||||
|
<span {...getTokenProps({ token, key })} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</Highlight>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { InlineResultViewer } from "./InlineResultViewer";
|
||||||
|
|
||||||
|
export { InlineResultViewer };
|
||||||
@@ -3,8 +3,6 @@ import Highlight, { defaultProps } from "prism-react-renderer";
|
|||||||
|
|
||||||
import { sourceMapping } from "../../logic/sourceMapping";
|
import { sourceMapping } from "../../logic/sourceMapping";
|
||||||
|
|
||||||
import "./SourceViewer.css";
|
|
||||||
|
|
||||||
interface ISourceViewerProps {
|
interface ISourceViewerProps {
|
||||||
code: string;
|
code: string;
|
||||||
onChangeCode(newCode: string): void;
|
onChangeCode(newCode: string): void;
|
||||||
@@ -13,26 +11,31 @@ interface ISourceViewerProps {
|
|||||||
export function SourceViewer(props: ISourceViewerProps) {
|
export function SourceViewer(props: ISourceViewerProps) {
|
||||||
const { code } = props;
|
const { code } = props;
|
||||||
return (
|
return (
|
||||||
<Highlight {...defaultProps} code={code} language="jsx">
|
<div>
|
||||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
{/* Title of code section */}
|
||||||
// eslint-disable-next-line no-sequences
|
<div className="px-4 py-1 bg-gray-200 ">Code</div>
|
||||||
sourceMapping.reset(),
|
{/* Code viewer */}
|
||||||
(
|
<Highlight {...defaultProps} code={code} language="jsx">
|
||||||
<pre className={className} style={style}>
|
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||||
{tokens.map((line, i) => (
|
// eslint-disable-next-line no-sequences
|
||||||
<div {...getLineProps({ line, key: i })}>
|
sourceMapping.reset(),
|
||||||
{line.map(function (token, key) {
|
(
|
||||||
const refFn = sourceMapping.registerToken(token);
|
<pre className={`${className} px-4 py-2 overflow-auto`} style={style}>
|
||||||
return (
|
{tokens.map((line, i) => (
|
||||||
<span ref={refFn} {...getTokenProps({ token, key })} />
|
<div {...getLineProps({ line, key: i })}>
|
||||||
);
|
{line.map(function (token, key) {
|
||||||
})}
|
const refFn = sourceMapping.registerToken(token);
|
||||||
{(sourceMapping.registerLineEnd(), null)}
|
return (
|
||||||
</div>
|
<span ref={refFn} {...getTokenProps({ token, key })} />
|
||||||
))}
|
);
|
||||||
</pre>
|
})}
|
||||||
)
|
{(sourceMapping.registerLineEnd(), null)}
|
||||||
)}
|
</div>
|
||||||
</Highlight>
|
))}
|
||||||
|
</pre>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Highlight>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ body {
|
|||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
font-size: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Token } from "../types/prism-types";
|
import { Token } from "../types/prism-types";
|
||||||
|
|
||||||
interface TMappingELement {
|
export interface TMappingELement {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
ref: { current: HTMLElement | null };
|
ref: { current: HTMLElement | null };
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TSourceMapping {
|
export interface TSourceMapping {
|
||||||
mappingArray: Array<TMappingELement>;
|
mappingArray: Array<TMappingELement>;
|
||||||
lastEnd: number;
|
lastEnd: number;
|
||||||
registerToken(t: Token): (ele: HTMLElement | null) => void;
|
registerToken(t: Token): (ele: HTMLElement | null) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user