Skip to content

Commit

Permalink
feature(editor) Scoped variables show destructured parameters.
Browse files Browse the repository at this point in the history
- Added `propertiesExposedByParam` utility function.
- Added `findContainingComponent` utility function.
- `getVariablesInScope` now finds the correct component and not just the first one.
- Fixed `applyPropsParamToPassedProps` to correctly handle destructured values
  and repeated field usages.
- `createComponentRendererComponent` includes values exposed by the props param
  when building `spiedVariablesInScope`.
  • Loading branch information
seanparsons committed Feb 12, 2024
1 parent da77530 commit f02c2cc
Show file tree
Hide file tree
Showing 6 changed files with 422 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
isUtopiaJSXComponent,
isSVGElement,
isJSXElement,
propertiesExposedByParam,
} from '../../../core/shared/element-template'
import { optionalMap } from '../../../core/shared/optional-utils'
import type {
Expand Down Expand Up @@ -130,6 +131,16 @@ export function createComponentRendererComponent(params: {
...appliedProps,
}

let spiedVariablesInScope: VariableData = {}
if (utopiaJsxComponent.param != null) {
for (const paramName of propertiesExposedByParam(utopiaJsxComponent.param)) {
spiedVariablesInScope[paramName] = {
spiedValue: scope[paramName],
insertionCeiling: instancePath,
}
}
}

let codeError: Error | null = null

// Protect against infinite recursion by taking the view that anything
Expand Down Expand Up @@ -161,8 +172,6 @@ export function createComponentRendererComponent(params: {
})
}

let definedWithinWithValues: MapLike<unknown> = {}

if (utopiaJsxComponent.arbitraryJSBlock != null) {
const lookupRenderer = createLookupRender(
rootElementPath,
Expand Down Expand Up @@ -194,12 +203,23 @@ export function createComponentRendererComponent(params: {
lookupRenderer,
)

definedWithinWithValues = runBlockUpdatingScope(
const definedWithinWithValues = runBlockUpdatingScope(
params.filePath,
mutableContext.requireResult,
utopiaJsxComponent.arbitraryJSBlock,
scope,
)

spiedVariablesInScope = {
...spiedVariablesInScope,
...objectMap(
(spiedValue) => ({
spiedValue: spiedValue,
insertionCeiling: null,
}),
definedWithinWithValues,
),
}
}

function buildComponentRenderResult(element: JSXElementChild): React.ReactElement {
Expand All @@ -208,14 +228,6 @@ export function createComponentRendererComponent(params: {
instancePath,
)

const spiedVariablesInScope: VariableData = objectMap(
(spiedValue) => ({
spiedValue: spiedValue,
insertionCeiling: null,
}),
definedWithinWithValues,
)

const renderedCoreElement = renderCoreElement(
element,
ownElementPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,39 +38,36 @@ export function applyPropsParamToPassedProps(
output[paramName] = getParamValue(value, boundParam.defaultExpression)
} else if (isDestructuredObject(boundParam)) {
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
const valueAsRecord: Record<string, unknown> = { ...value }
let remainingValues = { ...value } as Record<string, unknown>
let remainingKeys = Object.keys(remainingValues)
boundParam.parts.forEach((part) => {
for (const part of boundParam.parts) {
const { propertyName, param } = part
if (propertyName != null) {
// e.g. `{ prop: renamedProp }` or `{ prop: { /* further destructuring */ } }`
// Can't spread if we have a property name
const innerValue = remainingValues[propertyName]
applyBoundParamToOutput(innerValue, param.boundParam)
remainingKeys = remainingKeys.filter((k) => k !== propertyName)
delete remainingValues[propertyName]
} else {
if (propertyName == null) {
const { dotDotDotToken: spread, boundParam: innerBoundParam } = param
if (isRegularParam(innerBoundParam)) {
// e.g. `{ prop }` or `{ ...remainingProps }`
const { paramName } = innerBoundParam
if (spread) {
output[paramName] = remainingValues
remainingKeys = []
remainingValues = {}
} else {
output[paramName] = getParamValue(
remainingValues[paramName],
valueAsRecord[paramName],
innerBoundParam.defaultExpression,
)
remainingKeys = remainingKeys.filter((k) => k !== paramName)
delete remainingValues[paramName]
}
}
} else {
// e.g. `{ prop: renamedProp }` or `{ prop: { /* further destructuring */ } }`
// Can't spread if we have a property name
const innerValue = valueAsRecord[propertyName]
applyBoundParamToOutput(innerValue, param.boundParam)
delete remainingValues[propertyName]
// No other cases are legal
// TODO Should we throw? Users will already have a lint error
}
})
}
}
// TODO Throw, but what?
} else {
Expand Down
Loading

0 comments on commit f02c2cc

Please sign in to comment.