From a32cd478ac3a3b1a9b70a940dd38815649b9e21c Mon Sep 17 00:00:00 2001 From: bendtherules Date: Wed, 29 Jan 2020 13:42:38 +0530 Subject: [PATCH] lib - Add node highlighting and consume in log-identifier --- example/src/App.tsx | 6 ++- example/src/transforms/log-identifiers.ts | 3 ++ lib/src/components/ASTOutline/ASTOutline.css | 6 +++ lib/src/components/ASTOutline/ASTOutline.tsx | 53 ++++++++------------ lib/src/components/ASTOutline/index.tsx | 6 +-- lib/src/index.tsx | 32 +++++++++--- lib/src/store/index.tsx | 11 +++- lib/src/store/sourceFiles/actions.ts | 38 ++++++++++++-- lib/src/store/sourceFiles/reducer.ts | 32 ++++++++++-- lib/src/store/sourceFiles/types.ts | 26 ++++++++-- 10 files changed, 153 insertions(+), 60 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 7c6311b..4ecc62b 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -4,7 +4,8 @@ import { ASTVisualizer, initSourceFile, updateSourceFile, - clearSourceFile + clearSourceFile, + clearHighlightedNodes } from "ts-transformer-visualizer"; import * as ts from "typescript"; @@ -14,7 +15,7 @@ import { moduleMap, ModuleNames } from "./transforms"; export default function App({ moduleName, - timeDelayMs = 3000 + timeDelayMs = 1000 }: { moduleName: ModuleNames; timeDelayMs?: number; @@ -41,6 +42,7 @@ export default function App({ // 3. Clean timeout and reset visualizer clearTimeout(timeoutID); clearSourceFile(); + clearHighlightedNodes(); }; }); diff --git a/example/src/transforms/log-identifiers.ts b/example/src/transforms/log-identifiers.ts index 2ac77f3..c06b3c2 100644 --- a/example/src/transforms/log-identifiers.ts +++ b/example/src/transforms/log-identifiers.ts @@ -1,4 +1,6 @@ import * as ts from "typescript"; +import { highlightNode } from "ts-transformer-visualizer"; + import { TransformModuleExport } from "../types"; const sourceCodeString = `\ @@ -15,6 +17,7 @@ const transformer: ts.TransformerFactory = context => { return sourceFile => { const visitor = (node: ts.Node): ts.Node => { if (ts.isIdentifier(node)) { + highlightNode(node); console.log(node.text); } return ts.visitEachChild(node, visitor, context); diff --git a/lib/src/components/ASTOutline/ASTOutline.css b/lib/src/components/ASTOutline/ASTOutline.css index b67085a..589b07e 100644 --- a/lib/src/components/ASTOutline/ASTOutline.css +++ b/lib/src/components/ASTOutline/ASTOutline.css @@ -23,3 +23,9 @@ } .exitDone { } +.node { + padding: 0 0.5em; +} +.highlightNode { + background-color: rgba(254, 246, 2, 0.5); +} diff --git a/lib/src/components/ASTOutline/ASTOutline.tsx b/lib/src/components/ASTOutline/ASTOutline.tsx index e4cf276..0aeabcc 100644 --- a/lib/src/components/ASTOutline/ASTOutline.tsx +++ b/lib/src/components/ASTOutline/ASTOutline.tsx @@ -1,20 +1,29 @@ import React from "react"; -import { connect } from "react-redux"; import * as ts from "typescript"; import { CSSTransition, TransitionGroup } from "react-transition-group"; -import { AppState } from "../../store"; +import { useTypedSelector } from "../../store"; import { forEachChild, syntaxKindNameMapping, ObjectHash } from "../../utils"; import styles from "./ASTOutline.css"; interface ASTOutlineProps { node?: ts.Node; - sourceFile?: ts.SourceFile; } function ASTOutline(props: ASTOutlineProps) { - const { node, sourceFile } = props; + const { node: nodeFromProps } = props; + const { + node: nodeFromRedux, + sourceFile, + highlightedNodes + } = useTypedSelector(state => ({ + node: state.sourceFiles.currentSourceFile, + sourceFile: state.sourceFiles.currentSourceFile, + highlightedNodes: state.highlightedNodes.nodes + })); + const node = nodeFromProps || nodeFromRedux; + if (node === undefined || sourceFile === undefined) { return null; } @@ -22,7 +31,13 @@ function ASTOutline(props: ASTOutlineProps) { const output = ( <>
  • - {syntaxKindNameMapping[node.kind]} + + {syntaxKindNameMapping[node.kind]} +
      {forEachChild(node).map(childNode => ( @@ -31,7 +46,7 @@ function ASTOutline(props: ASTOutlineProps) { timeout={1000} classNames={styles} > - + ))} @@ -51,28 +66,4 @@ function ASTOutline(props: ASTOutlineProps) { } } -type ASTOutlineStateProps = ASTOutlineProps; - -const mapStateToProps = (state: AppState) => ({ - node: state.sourceFiles.currentSourceFile, - sourceFile: state.sourceFiles.currentSourceFile -}); -// currentSourceFile from ownProps should take preceedence -const mergeProps = ( - stateProps: ASTOutlineStateProps, - _dispatchProps: never, - ownProps: ASTOutlineProps -) => { - return { - ...stateProps, - ...ownProps - }; -}; - -export { ASTOutline as ASTOutlineUnconnected, ASTOutlineProps }; -const ASTOutlineConnected = connect( - mapStateToProps, - null, - mergeProps -)(ASTOutline); -export default ASTOutlineConnected; +export default ASTOutline; diff --git a/lib/src/components/ASTOutline/index.tsx b/lib/src/components/ASTOutline/index.tsx index 807a9a5..ce5efff 100644 --- a/lib/src/components/ASTOutline/index.tsx +++ b/lib/src/components/ASTOutline/index.tsx @@ -1,7 +1,3 @@ -import ASTOutline, { - ASTOutlineUnconnected, - ASTOutlineProps -} from "./ASTOutline"; +import ASTOutline from "./ASTOutline"; -export { ASTOutlineUnconnected, ASTOutlineProps }; export default ASTOutline; diff --git a/lib/src/index.tsx b/lib/src/index.tsx index 866b6f0..2f72a10 100644 --- a/lib/src/index.tsx +++ b/lib/src/index.tsx @@ -10,7 +10,9 @@ import configureStore from "./store"; import { initSourceFile, updateSourceFile, - clearSourceFile + clearSourceFile, + highlightNode, + clearHighlightedNodes } from "./store/sourceFiles/actions"; import styles from "./styles.css"; @@ -27,19 +29,23 @@ export function ASTVisualizer() { - +

      AST Inline Diff

      - +

      Code output Diff

      - Input - Output + + Input + + + Output + @@ -52,14 +58,24 @@ export function ASTVisualizer() { const { initSourceFile: initSourceFileConnected, updateSourceFile: updateSourceFileConnected, - clearSourceFile: clearSourceFileConnected + clearSourceFile: clearSourceFileConnected, + highlightNode: highlightNodeConnected, + clearHighlightedNodes: clearHighlightedNodesConnected } = bindActionCreators( - { initSourceFile, updateSourceFile, clearSourceFile }, + { + initSourceFile, + updateSourceFile, + clearSourceFile, + highlightNode, + clearHighlightedNodes + }, store.dispatch ); export { initSourceFileConnected as initSourceFile, updateSourceFileConnected as updateSourceFile, - clearSourceFileConnected as clearSourceFile + clearSourceFileConnected as clearSourceFile, + highlightNodeConnected as highlightNode, + clearHighlightedNodesConnected as clearHighlightedNodes }; diff --git a/lib/src/store/index.tsx b/lib/src/store/index.tsx index 2c6d5c5..4166b53 100644 --- a/lib/src/store/index.tsx +++ b/lib/src/store/index.tsx @@ -4,12 +4,17 @@ import { applyMiddleware, Middleware } from "redux"; +import { useSelector, TypedUseSelectorHook } from "react-redux"; import { composeWithDevTools } from "redux-devtools-extension"; -import { sourceFilesReducer } from "./sourceFiles/reducer"; +import { + sourceFilesReducer, + highlightedNodesReducer +} from "./sourceFiles/reducer"; const rootReducer = combineReducers({ - sourceFiles: sourceFilesReducer + sourceFiles: sourceFilesReducer, + highlightedNodes: highlightedNodesReducer }); export type AppState = ReturnType; @@ -25,3 +30,5 @@ export default function configureStore() { return store; } + +export const useTypedSelector: TypedUseSelectorHook = useSelector; diff --git a/lib/src/store/sourceFiles/actions.ts b/lib/src/store/sourceFiles/actions.ts index c0a00ff..0137143 100644 --- a/lib/src/store/sourceFiles/actions.ts +++ b/lib/src/store/sourceFiles/actions.ts @@ -1,22 +1,50 @@ import * as ts from "typescript"; -import { INIT_SOURCEFILE, UPDATE_SOURCEFILE, CLEAR_SOURCEFILE } from "./types"; +import { + INIT_SOURCEFILE, + UPDATE_SOURCEFILE, + CLEAR_SOURCEFILE, + HIGHLIGHT_NODE, + CLEAR_HIGHLIGHTED_NODES, + InitSourceFileAction, + UpdateSourceFileAction, + ClearSourceFileAction, + HighlightNodeAction, + ClearHighlightedNodesAction +} from "./types"; -export function initSourceFile(sourceFile: ts.SourceFile) { +export function initSourceFile( + sourceFile: ts.SourceFile +): InitSourceFileAction { return { type: INIT_SOURCEFILE, payload: { sourceFile } }; } -export function updateSourceFile(sourceFile: ts.SourceFile) { +export function updateSourceFile( + sourceFile: ts.SourceFile +): UpdateSourceFileAction { return { type: UPDATE_SOURCEFILE, payload: { sourceFile } }; } -export function clearSourceFile() { +export function clearSourceFile(): ClearSourceFileAction { return { - type: CLEAR_SOURCEFILE, + type: CLEAR_SOURCEFILE + }; +} + +export function highlightNode(node: ts.Node): HighlightNodeAction { + return { + type: HIGHLIGHT_NODE, + payload: { node } + }; +} + +export function clearHighlightedNodes(): ClearHighlightedNodesAction { + return { + type: CLEAR_HIGHLIGHTED_NODES }; } diff --git a/lib/src/store/sourceFiles/reducer.ts b/lib/src/store/sourceFiles/reducer.ts index ea6491c..598423b 100644 --- a/lib/src/store/sourceFiles/reducer.ts +++ b/lib/src/store/sourceFiles/reducer.ts @@ -2,17 +2,24 @@ import { INIT_SOURCEFILE, UPDATE_SOURCEFILE, CLEAR_SOURCEFILE, + HIGHLIGHT_NODE, + CLEAR_HIGHLIGHTED_NODES, SourceFilesState, - SourceFilesActionTypes + HighlightedNodesState, + SourceFilesActionTypes, + HighlightNodeActionTypes } from "./types"; -const initialState: SourceFilesState = { +const initialSourceFileState: SourceFilesState = { inititalSourceFile: undefined, currentSourceFile: undefined }; +const initialHighlightedNodesState: HighlightedNodesState = { + nodes: [] +}; export function sourceFilesReducer( - state = initialState, + state = initialSourceFileState, action: SourceFilesActionTypes ): SourceFilesState { switch (action.type) { @@ -28,7 +35,24 @@ export function sourceFilesReducer( currentSourceFile: action.payload.sourceFile }; case CLEAR_SOURCEFILE: - return initialState; + return initialSourceFileState; + default: + return state; + } +} + +export function highlightedNodesReducer( + state = initialHighlightedNodesState, + action: HighlightNodeActionTypes +): HighlightedNodesState { + switch (action.type) { + case HIGHLIGHT_NODE: + return { + ...state, + nodes: [...state.nodes, action.payload.node] + }; + case CLEAR_HIGHLIGHTED_NODES: + return initialHighlightedNodesState; default: return state; } diff --git a/lib/src/store/sourceFiles/types.ts b/lib/src/store/sourceFiles/types.ts index ae75d4e..4e0c764 100644 --- a/lib/src/store/sourceFiles/types.ts +++ b/lib/src/store/sourceFiles/types.ts @@ -6,26 +6,46 @@ export interface SourceFilesState { currentSourceFile?: ts.SourceFile; } +// slice for highlighted nodes +export interface HighlightedNodesState { + nodes: ts.Node[]; +} + // Describing the different ACTION NAMES available export const INIT_SOURCEFILE = "INIT_SOURCEFILE"; export const UPDATE_SOURCEFILE = "UPDATE_SOURCEFILE"; export const CLEAR_SOURCEFILE = "CLEAR_SOURCEFILE"; +export const HIGHLIGHT_NODE = "HIGHLIGHT_NODE"; +export const CLEAR_HIGHLIGHTED_NODES = "CLEAR_HIGHLIGHTED_NODES"; -interface InitSourceFileAction { +export interface InitSourceFileAction { type: typeof INIT_SOURCEFILE; payload: { sourceFile: ts.SourceFile }; } -interface UpdateSourceFileAction { +export interface UpdateSourceFileAction { type: typeof UPDATE_SOURCEFILE; payload: { sourceFile: ts.SourceFile }; } -interface ClearSourceFileAction { +export interface ClearSourceFileAction { type: typeof CLEAR_SOURCEFILE; } +export interface HighlightNodeAction { + type: typeof HIGHLIGHT_NODE; + payload: { node: ts.Node }; +} + +export interface ClearHighlightedNodesAction { + type: typeof CLEAR_HIGHLIGHTED_NODES; +} + export type SourceFilesActionTypes = | InitSourceFileAction | UpdateSourceFileAction | ClearSourceFileAction; + +export type HighlightNodeActionTypes = + | HighlightNodeAction + | ClearHighlightedNodesAction;