Rust artifact graph (#5068)
* Start porting artifact graph creation to Rust * Add most of artifact graph creation * Add handling loft command from recent PR * Refactor artifact merge code so that it errors when a new artifact type is added * Add sweep subtype * Finish implementation of build artifact graph * Fix wasm.ts to use new combined generated ts-rs file * Fix Rust lints * Fix lints * Fix up replacement code * Add artifact graph to WASM outcome * Add artifact graph to simulation test output * Add new artifact graph output snapshots * Fix wall field and reduce unreachable code * Change field order for subtype * Change subtype to be determined from the request, like the TS * Fix plane sweep_id * Condense code * Change ID types to be properly optional * Change to favor the new ID, the same as TS * Fix to make error impossible * Rename artifact type tag values to match TS * Fix name of field on Cap * Update outputs * Change to use Rust source range * Update output snapshots * Add conversion to mermaid mind map and add to snapshot tests * Add new mermaid mind map output * Add flowchart * Remove raw artifact graph from tests * Remove JSON artifact graph output * Update output file with header * Update output after adding flowchart * Fix flowchart to not have duplicate edges, one in each direction * Fix not not output duplicate edges in flowcharts * Change flowchart edge style to be more obvious when a direction is missing * Update output after deduplication of edges * Fix not not skip sketch-on-face artifacts * Add docs * Fix edge iteration order to be stable * Update output after fixing order * Port TS artifactGraph.test.ts tests to simulation tests * Add grouping segments and solid2ds with their path * Update output flowcharts since grouping paths * Remove TS artifactGraph tests * Remove unused d3 dependencies * Fix to track loft ID on paths * Add command ID to error messages * Move artifact graph test code to a separate file since it's a large file * Reduce function visibility * Remove TS artifact graph code * Fix spelling error with serde * Add TODO for edge cut consumed ID * Add comment about mermaid edge rank * Fix mermaid flowchart edge cuts to appear as children of their edges * Update output since fixing flowchart order * Fix to always build the artifact graph even when there's a KCL error * Add artifact graph to error output * Change optional ID merge to match TS * Remove redundant SourceRange definition * Remove Rust-flavored default source range function * Add helper for source range creation * Update doc comment for the website * Update docs after doc comment change * Fix to save engine responses in execution cache * Remove unused import * Fix to not call WASM function before beforeAll callback is run * Remove more unused imports
This commit is contained in:
108
src/lang/wasm.ts
108
src/lang/wasm.ts
@ -44,17 +44,30 @@ import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
||||
import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||
import { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||
import { SourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||
import { getAllCurrentSettings } from 'lib/settings/settingsUtils'
|
||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||
import { KclErrorWithOutputs } from 'wasm-lib/kcl/bindings/KclErrorWithOutputs'
|
||||
import { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||
import { ArtifactCommand } from 'wasm-lib/kcl/bindings/ArtifactCommand'
|
||||
import { Artifact as RustArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
import { ArtifactId } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
import { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
import { ArtifactGraph as RustArtifactGraph } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
import { Artifact } from './std/artifactGraph'
|
||||
import { getNodePathFromSourceRange } from './queryAst'
|
||||
|
||||
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/ArtifactCommand'
|
||||
export type { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { ArtifactId } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Cap as CapArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { CodeRef } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { EdgeCut } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Path as PathArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Plane as PlaneArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Segment as SegmentArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Solid2d as Solid2dArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Sweep as SweepArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { SweepEdge } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Wall as WallArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||
@ -76,7 +89,7 @@ export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
|
||||
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
|
||||
export type { LiteralValue } from '../wasm-lib/kcl/bindings/LiteralValue'
|
||||
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
|
||||
export type { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||
export type { SourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||
|
||||
export type SyntaxType =
|
||||
| 'Program'
|
||||
@ -105,35 +118,36 @@ export type { Solid } from '../wasm-lib/kcl/bindings/Solid'
|
||||
export type { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
||||
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
||||
|
||||
/**
|
||||
* The first two items are the start and end points (byte offsets from the start of the file).
|
||||
* The third item is whether the source range belongs to the 'main' file, i.e., the file currently
|
||||
* being rendered/displayed in the editor (TODO we need to handle modules better in the frontend).
|
||||
*/
|
||||
export type SourceRange = [number, number, boolean]
|
||||
|
||||
/**
|
||||
* Convert a SourceRange as used inside the KCL interpreter into the above one for use in the
|
||||
* frontend (essentially we're eagerly checking whether the frontend should care about the SourceRange
|
||||
* so as not to expose details of the interpreter's current representation of module ids throughout
|
||||
* the frontend).
|
||||
*/
|
||||
export function sourceRangeFromRust(s: RustSourceRange): SourceRange {
|
||||
return [s[0], s[1], s[2] === 0]
|
||||
export function sourceRangeFromRust(s: SourceRange): SourceRange {
|
||||
return [s[0], s[1], s[2]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default SourceRange for testing or as a placeholder.
|
||||
*/
|
||||
export function defaultSourceRange(): SourceRange {
|
||||
return [0, 0, true]
|
||||
return [0, 0, 0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default RustSourceRange for testing or as a placeholder.
|
||||
* Create a SourceRange for the top-level module.
|
||||
*/
|
||||
export function defaultRustSourceRange(): RustSourceRange {
|
||||
return [0, 0, 0]
|
||||
export function topLevelRange(start: number, end: number): SourceRange {
|
||||
return [start, end, 0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this source range is from the file being executed. Returns
|
||||
* false if it's from a file that was imported.
|
||||
*/
|
||||
export function isTopLevelModule(range: SourceRange): boolean {
|
||||
return range[2] === 0
|
||||
}
|
||||
|
||||
export const wasmUrl = () => {
|
||||
@ -234,7 +248,8 @@ export const parse = (code: string | Error): ParseResult | Error => {
|
||||
parsed.msg,
|
||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||
[],
|
||||
[]
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -258,8 +273,9 @@ export const isPathToNodeNumber = (
|
||||
export interface ExecState {
|
||||
memory: ProgramMemory
|
||||
operations: Operation[]
|
||||
artifacts: { [key in ArtifactId]?: Artifact }
|
||||
artifacts: { [key in ArtifactId]?: RustArtifact }
|
||||
artifactCommands: ArtifactCommand[]
|
||||
artifactGraph: ArtifactGraph
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,18 +288,53 @@ export function emptyExecState(): ExecState {
|
||||
operations: [],
|
||||
artifacts: {},
|
||||
artifactCommands: [],
|
||||
artifactGraph: defaultArtifactGraph(),
|
||||
}
|
||||
}
|
||||
|
||||
function execStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
||||
function execStateFromRust(
|
||||
execOutcome: RustExecOutcome,
|
||||
program: Node<Program>
|
||||
): ExecState {
|
||||
const artifactGraph = rustArtifactGraphToMap(execOutcome.artifactGraph)
|
||||
// We haven't ported pathToNode logic to Rust yet, so we need to fill it in.
|
||||
for (const [id, artifact] of artifactGraph) {
|
||||
if (!artifact) continue
|
||||
if (!('codeRef' in artifact)) continue
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
program,
|
||||
sourceRangeFromRust(artifact.codeRef.range)
|
||||
)
|
||||
artifact.codeRef.pathToNode = pathToNode
|
||||
}
|
||||
|
||||
return {
|
||||
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
||||
operations: execOutcome.operations,
|
||||
artifacts: execOutcome.artifacts,
|
||||
artifactCommands: execOutcome.artifactCommands,
|
||||
artifactGraph,
|
||||
}
|
||||
}
|
||||
|
||||
export type ArtifactGraph = Map<ArtifactId, Artifact>
|
||||
|
||||
function rustArtifactGraphToMap(
|
||||
rustArtifactGraph: RustArtifactGraph
|
||||
): ArtifactGraph {
|
||||
const map = new Map<ArtifactId, Artifact>()
|
||||
for (const [id, artifact] of Object.entries(rustArtifactGraph.map)) {
|
||||
if (!artifact) continue
|
||||
map.set(id, artifact)
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
export function defaultArtifactGraph(): ArtifactGraph {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
interface Memory {
|
||||
[key: string]: KclValue | undefined
|
||||
}
|
||||
@ -543,7 +594,7 @@ export const executor = async (
|
||||
engineCommandManager,
|
||||
fileSystemManager
|
||||
)
|
||||
return execStateFromRust(execOutcome)
|
||||
return execStateFromRust(execOutcome, node)
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
|
||||
@ -552,7 +603,8 @@ export const executor = async (
|
||||
parsed.error.msg,
|
||||
sourceRangeFromRust(parsed.error.sourceRanges[0]),
|
||||
parsed.operations,
|
||||
parsed.artifactCommands
|
||||
parsed.artifactCommands,
|
||||
rustArtifactGraphToMap(parsed.artifactGraph)
|
||||
)
|
||||
|
||||
return Promise.reject(kclError)
|
||||
@ -613,7 +665,8 @@ export const modifyAstForSketch = async (
|
||||
parsed.msg,
|
||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||
[],
|
||||
[]
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
@ -683,7 +736,8 @@ export function programMemoryInit(): ProgramMemory | Error {
|
||||
parsed.msg,
|
||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||
[],
|
||||
[]
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user