diff --git a/docs/kcl/std.json b/docs/kcl/std.json index 059740d4ac..8bd1e7dcbb 100644 --- a/docs/kcl/std.json +++ b/docs/kcl/std.json @@ -25,7 +25,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = abs(-4)" + ] }, { "name": "acos", @@ -53,7 +56,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = acos(0.5)" + ] }, { "name": "angleToMatchLengthX", @@ -1050,7 +1056,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line({ to: [1, 3.82], tag: 'seg01' }, %)\n |> angledLineToX([\n -angleToMatchLengthX('seg01', 10, %),\n 5\n ], %)\n |> close(%)" + ] }, { "name": "angleToMatchLengthY", @@ -2047,7 +2056,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line({ to: [1, 3.82], tag: 'seg01' }, %)\n |> angledLineToX([\n -angleToMatchLengthY('seg01', 10, %),\n 5\n ], %)\n |> close(%)" + ] }, { "name": "angledLine", @@ -4024,7 +4036,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> angledLine({ angle: 45, length: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> line([0, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)" + ] }, { "name": "angledLineOfXLength", @@ -6001,7 +6016,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> angledLineOfXLength({ angle: 45, length: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> line([0, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)" + ] }, { "name": "angledLineOfYLength", @@ -7978,7 +7996,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('YZ')\n |> startProfileAt([0, 0], %)\n |> angledLineOfYLength({ angle: 45, length: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> line([0, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)\n |> fillet({ radius: 2, tags: [\"edge1\"] }, %)" + ] }, { "name": "angledLineThatIntersects", @@ -9945,7 +9966,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> lineTo({ to: [2, 2], tag: \"yo\" }, %)\n |> lineTo([3, 1], %)\n |> angledLineThatIntersects({\n angle: 180,\n intersectTag: 'yo',\n offset: 12,\n tag: \"yo2\"\n }, %)\n |> line([4, 0], %)\n |> close(%, \"yo3\")\n |> extrude(10, %)" + ] }, { "name": "angledLineToX", @@ -11922,7 +11946,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> angledLineToX({ angle: 45, to: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> line([0, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)\n |> fillet({ radius: 2, tags: [\"edge1\"] }, %)" + ] }, { "name": "angledLineToY", @@ -13899,7 +13926,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> angledLineToY({ angle: 45, to: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> line([0, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)" + ] }, { "name": "arc", @@ -15915,7 +15945,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('-YZ')\n |> startProfileAt([0, 0], %)\n |> arc({\n angle_start: 0,\n angle_end: 360,\n radius: 10,\n tag: \"edge1\"\n }, %)\n |> extrude(10, %)" + ] }, { "name": "asin", @@ -15943,7 +15976,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = asin(0.5)" + ] }, { "name": "atan", @@ -15971,7 +16007,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = atan(1.0)" + ] }, { "name": "bezierCurve", @@ -17954,7 +17993,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> bezierCurve({\n to: [10, 10],\n control1: [5, 0],\n control2: [5, 10],\n tag: \"edge1\"\n }, %)\n |> close(%)\n |> extrude(10, %)" + ] }, { "name": "ceil", @@ -17982,7 +18024,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = ceil(4.5)" + ] }, { "name": "circle", @@ -19284,7 +19329,8 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [] }, { "name": "close", @@ -21225,7 +21271,11 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line([10, 0], %)\n |> close(%)", + "startSketchOn('YZ')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line([10, 0], %)\n |> close(%, \"edge1\")" + ] }, { "name": "cos", @@ -21253,7 +21303,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const anotherVar = cos(2 * pi())" + ] }, { "name": "e", @@ -21271,7 +21324,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = e()" + ] }, { "name": "extrude", @@ -23007,7 +23063,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line([0, -10], %)\n |> close(%)\n |> extrude(5, %)" + ] }, { "name": "fillet", @@ -24567,7 +24626,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line({ to: [0, 10], tag: \"thing\" }, %)\n |> line([10, 0], %)\n |> line({ to: [0, -10], tag: \"thing2\" }, %)\n |> close(%)\n |> extrude(10, %)\n |> fillet({ radius: 2, tags: [\"thing\", \"thing2\"] }, %)" + ] }, { "name": "floor", @@ -24595,7 +24657,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = floor(4.5)" + ] }, { "name": "getExtrudeWallTransform", @@ -25425,7 +25490,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line({ to: [0, -10], tag: \"surface\" }, %)\n |> close(%)\n |> extrude(5, %)\n\nconst transform = getExtrudeWallTransform('surface', box)" + ] }, { "name": "getNextAdjacentEdge", @@ -26208,7 +26276,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line({ to: [0, 10], tag: \"thing\" }, %)\n |> line({ to: [10, 0], tag: \"thing1\" }, %)\n |> line({ to: [0, -10], tag: \"thing2\" }, %)\n |> close(%)\n |> extrude(10, %)\n |> fillet({\n radius: 2,\n tags: [getNextAdjacentEdge(\"thing\", %)]\n }, %)" + ] }, { "name": "getOppositeEdge", @@ -26991,7 +27062,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line({ to: [0, 10], tag: \"thing\" }, %)\n |> line([10, 0], %)\n |> line({ to: [0, -10], tag: \"thing2\" }, %)\n |> close(%)\n |> extrude(10, %)\n |> fillet({\n radius: 2,\n tags: [\"thing\", getOppositeEdge(\"thing\", %)]\n }, %)" + ] }, { "name": "getPreviousAdjacentEdge", @@ -27774,7 +27848,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line({ to: [0, 10], tag: \"thing\" }, %)\n |> line({ to: [10, 0], tag: \"thing1\" }, %)\n |> line({ to: [0, -10], tag: \"thing2\" }, %)\n |> close(%)\n |> extrude(10, %)\n |> fillet({\n radius: 2,\n tags: [getPreviousAdjacentEdge(\"thing2\", %)]\n }, %)" + ] }, { "name": "hole", @@ -31652,7 +31729,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const square = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line([0, -10], %)\n |> close(%)\n |> hole(circle([2, 2], .5, startSketchOn('XY')), %)\n |> hole(circle([2, 8], .5, startSketchOn('XY')), %)\n |> extrude(2, %)" + ] }, { "name": "import", @@ -32302,7 +32382,14 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const model = import(\"thing.obj\")", + "const model = import(\"cube.obj\", { type: \"obj\", units: \"m\" })", + "const model = import(\"my_model.gltf\")", + "const model = import(\"my_model.sldprt\")", + "const model = import(\"my_model.step\")" + ] }, { "name": "lastSegX", @@ -33282,7 +33369,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn(\"YZ\")\n |> startProfileAt([0, 0], %)\n |> line({ to: [5, 0], tag: \"thing\" }, %)\n |> line([5, 5], %)\n |> line([0, lastSegX(%)], %)\n |> close(%)\n |> extrude(5, %)" + ] }, { "name": "lastSegY", @@ -34262,7 +34352,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn(\"YZ\")\n |> startProfileAt([0, 0], %)\n |> line({ to: [5, 0], tag: \"thing\" }, %)\n |> line([5, 5], %)\n |> line([0, lastSegY(%)], %)\n |> close(%)\n |> extrude(5, %)" + ] }, { "name": "legAngX", @@ -34299,7 +34392,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "legAngX(5, 3)" + ] }, { "name": "legAngY", @@ -34336,7 +34432,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "legAngY(5, 3)" + ] }, { "name": "legLen", @@ -34373,7 +34472,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "legLen(5, 3)" + ] }, { "name": "line", @@ -36349,7 +36451,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('-XY')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line({ to: [20, 10], tag: \"edge1\" }, %)\n |> close(%, \"edge2\")\n |> extrude(10, %)" + ] }, { "name": "lineTo", @@ -38325,7 +38430,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "fn rectShape = (pos, w, l) => {\n const rr = startSketchOn('YZ')\n |> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)\n |> lineTo({\n to: [pos[0] + w / 2, pos[1] - (l / 2)],\n tag: \"edge1\"\n }, %)\n |> lineTo({\n to: [pos[0] + w / 2, pos[1] + l / 2],\n tag: \"edge2\"\n }, %)\n |> lineTo({\n to: [pos[0] - (w / 2), pos[1] + l / 2],\n tag: \"edge3\"\n }, %)\n |> close(%, \"edge4\")\n return rr\n}\n\n// Create the mounting plate extrusion, holes, and fillets\nconst part = rectShape([0, 0], 20, 20)" + ] }, { "name": "ln", @@ -38353,7 +38461,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = ln(4)" + ] }, { "name": "log", @@ -38390,7 +38501,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = log(4, 2)" + ] }, { "name": "log10", @@ -38418,7 +38532,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = log10(4)" + ] }, { "name": "log2", @@ -38446,7 +38563,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = log2(4)" + ] }, { "name": "max", @@ -38477,7 +38597,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = max(4, 5, 6)" + ] }, { "name": "min", @@ -38508,7 +38631,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = min(4, 5, 6)" + ] }, { "name": "patternCircular2d", @@ -40485,7 +40611,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part = startSketchOn('XY')\n |> circle([0, 0], 2, %)\n |> patternCircular2d({\n center: [20, 20],\n repetitions: 12,\n arcDegrees: 210,\n rotateDuplicates: true\n }, %)" + ] }, { "name": "patternCircular3d", @@ -42063,7 +42192,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 1], %)\n |> line([1, 0], %)\n |> line([0, -1], %)\n |> close(%)\n |> extrude(1, %)\n |> patternCircular3d({\n axis: [1, 1, 0],\n center: [10, 0, 10],\n repetitions: 10,\n arcDegrees: 360,\n rotateDuplicates: true\n }, %)" + ] }, { "name": "patternLinear2d", @@ -44035,7 +44167,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part = startSketchOn('XY')\n |> circle([0, 0], 2, %)\n |> patternLinear2d({\n axis: [0, 1],\n repetitions: 12,\n distance: 2\n }, %)" + ] }, { "name": "patternLinear3d", @@ -45597,7 +45732,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 1], %)\n |> line([1, 0], %)\n |> line([0, -1], %)\n |> close(%)\n |> extrude(1, %)\n |> patternLinear3d({\n axis: [1, 0, 1],\n repetitions: 3,\n distance: 6\n }, %)" + ] }, { "name": "pi", @@ -45615,7 +45753,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = pi() * 3.0" + ] }, { "name": "pow", @@ -45652,7 +45793,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = pow(4, 2)" + ] }, { "name": "segAng", @@ -46640,7 +46784,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const part001 = startSketchOn('XY')\n |> startProfileAt([4.83, 12.56], %)\n |> line([15.1, 2.48], %)\n |> line({ to: [3.15, -9.85], tag: 'seg01' }, %)\n |> line([-15.17, -4.1], %)\n |> angledLine([segAng('seg01', %), 12.35], %)\n |> line([-13.02, 10.03], %)\n |> close(%)\n |> extrude(4, %)" + ] }, { "name": "segEndX", @@ -47628,7 +47775,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn(\"YZ\")\n |> startProfileAt([0, 0], %)\n |> line({ to: [5, 0], tag: \"thing\" }, %)\n |> line([5, 5], %)\n |> line([segEndX(\"thing\", %), 5], %)\n |> close(%)\n |> extrude(5, %)" + ] }, { "name": "segEndY", @@ -48616,7 +48766,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn(\"YZ\")\n |> startProfileAt([0, 0], %)\n |> line({ to: [5, 0], tag: \"thing\" }, %)\n |> line([5, 5], %)\n |> line([segEndY(\"thing\", %), 5], %)\n |> close(%)\n |> extrude(5, %)" + ] }, { "name": "segLen", @@ -49604,7 +49757,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn(\"YZ\")\n |> startProfileAt([0, 0], %)\n |> line({ to: [5, 0], tag: \"thing\" }, %)\n |> line([5, 5], %)\n |> line([0, segLen(\"thing\", %)], %)\n |> close(%)\n |> extrude(5, %)" + ] }, { "name": "sin", @@ -49632,7 +49788,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = sin(2 * pi())" + ] }, { "name": "sqrt", @@ -49660,7 +49819,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = sqrt(4)" + ] }, { "name": "startProfileAt", @@ -50975,7 +51137,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)" + ] }, { "name": "startSketchAt", @@ -51990,7 +52155,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchAt([0, 0])\n |> line([10, 10], %)" + ] }, { "name": "startSketchOn", @@ -53254,7 +53422,11 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line({ to: [20, 10], tag: \"edge1\" }, %)\n |> close(%, \"edge2\")", + "fn cube = (pos, scale) => {\n const sg = startSketchOn('XY')\n |> startProfileAt(pos, %)\n |> line([0, scale], %)\n |> line([scale, 0], %)\n |> line([0, -scale], %)\n |> close(%)\n |> extrude(scale, %)\n\n return sg\n}\n\nconst box = cube([0, 0], 20)\n\nconst part001 = startSketchOn(box, \"start\")\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line({ to: [20, 10], tag: \"edge1\" }, %)\n |> close(%)\n |> extrude(20, %)" + ] }, { "name": "tan", @@ -53282,7 +53454,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = tan(2 * pi())" + ] }, { "name": "tangentialArc", @@ -55277,7 +55452,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('-YZ')\n |> startProfileAt([0, 0], %)\n |> line({ to: [10, 10], tag: \"edge0\" }, %)\n |> tangentialArc({ radius: 10, offset: 90, tag: \"edge1\" }, %)\n |> close(%)\n |> extrude(10, %)" + ] }, { "name": "tangentialArcTo", @@ -57232,7 +57410,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('-YZ')\n |> startProfileAt([0, 0], %)\n |> line({ to: [10, 10], tag: \"edge0\" }, %)\n |> tangentialArcTo([10, 0], %)\n |> close(%)" + ] }, { "name": "tau", @@ -57250,10 +57431,13 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = tau()" + ] }, { - "name": "to_degrees", + "name": "toDegrees", "summary": "Converts a number from radians to degrees.", "description": "", "tags": [], @@ -57278,10 +57462,13 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = toDegrees(2 * pi())" + ] }, { - "name": "to_radians", + "name": "toRadians", "summary": "Converts a number from degrees to radians.", "description": "", "tags": [], @@ -57306,7 +57493,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "const myVar = toRadians(180)" + ] }, { "name": "xLine", @@ -59272,7 +59462,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('YZ')\n |> startProfileAt([0, 0], %)\n |> xLine(10, %)\n |> line([10, 10], %)\n |> close(%, \"edge1\")\n |> extrude(10, %)" + ] }, { "name": "xLineTo", @@ -61238,7 +61431,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> xLineTo({ to: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)" + ] }, { "name": "yLine", @@ -63204,7 +63400,10 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> yLine(10, %)\n |> line([10, 10], %)\n |> close(%, \"edge1\")\n |> extrude(10, %)" + ] }, { "name": "yLineTo", @@ -65170,6 +65369,9 @@ "required": true }, "unpublished": false, - "deprecated": false + "deprecated": false, + "examples": [ + "startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> yLineTo({ to: 10, tag: \"edge1\" }, %)\n |> line([10, 10], %)\n |> close(%, \"edge2\")\n |> extrude(10, %)\n |> fillet({ radius: 2, tags: [\"edge2\"] }, %)" + ] } ] \ No newline at end of file diff --git a/docs/kcl/std.md b/docs/kcl/std.md index fc3a9423c9..2498b934c4 100644 --- a/docs/kcl/std.md +++ b/docs/kcl/std.md @@ -65,8 +65,8 @@ * [`tangentialArc`](#tangentialArc) * [`tangentialArcTo`](#tangentialArcTo) * [`tau`](#tau) - * [`to_degrees`](#to_degrees) - * [`to_radians`](#to_radians) + * [`toDegrees`](#toDegrees) + * [`toRadians`](#toRadians) * [`xLine`](#xLine) * [`xLineTo`](#xLineTo) * [`yLine`](#yLine) @@ -85,6 +85,12 @@ Computes the absolute value of a number. abs(num: number) -> number ``` +#### Examples + +```kcl +const myVar = abs(-4) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -105,6 +111,12 @@ Computes the arccosine of a number (in radians). acos(num: number) -> number ``` +#### Examples + +```kcl +const myVar = acos(0.5) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -125,6 +137,19 @@ Returns the angle to match the given length for x. angleToMatchLengthX(segment_name: string, to: number, sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line({ to: [1, 3.82], tag: 'seg01' }, %) + |> angledLineToX([ + -angleToMatchLengthX('seg01', 10, %), + 5 + ], %) + |> close(%) +``` + #### Arguments * `segment_name`: `string` (REQUIRED) @@ -310,6 +335,19 @@ Returns the angle to match the given length for y. angleToMatchLengthY(segment_name: string, to: number, sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line({ to: [1, 3.82], tag: 'seg01' }, %) + |> angledLineToX([ + -angleToMatchLengthY('seg01', 10, %), + 5 + ], %) + |> close(%) +``` + #### Arguments * `segment_name`: `string` (REQUIRED) @@ -495,6 +533,18 @@ Draw an angled line. angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> angledLine({ angle: 45, length: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> line([0, 10], %) + |> close(%, "edge2") + |> extrude(10, %) +``` + #### Arguments * `data`: `AngledLineData` - Data to draw an angled line. (REQUIRED) @@ -853,6 +903,18 @@ Draw an angled line of a given x length. angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XZ') + |> startProfileAt([0, 0], %) + |> angledLineOfXLength({ angle: 45, length: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> line([0, 10], %) + |> close(%, "edge2") + |> extrude(10, %) +``` + #### Arguments * `data`: `AngledLineData` - Data to draw an angled line. (REQUIRED) @@ -1211,6 +1273,19 @@ Draw an angled line of a given y length. angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('YZ') + |> startProfileAt([0, 0], %) + |> angledLineOfYLength({ angle: 45, length: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> line([0, 10], %) + |> close(%, "edge2") + |> extrude(10, %) + |> fillet({ radius: 2, tags: ["edge1"] }, %) +``` + #### Arguments * `data`: `AngledLineData` - Data to draw an angled line. (REQUIRED) @@ -1569,6 +1644,24 @@ Draw an angled line that intersects with a given line. angledLineThatIntersects(data: AngledLineThatIntersectsData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> lineTo({ to: [2, 2], tag: "yo" }, %) + |> lineTo([3, 1], %) + |> angledLineThatIntersects({ + angle: 180, + intersectTag: 'yo', + offset: 12, + tag: "yo2" + }, %) + |> line([4, 0], %) + |> close(%, "yo3") + |> extrude(10, %) +``` + #### Arguments * `data`: `AngledLineThatIntersectsData` - Data for drawing an angled line that intersects with a given line. (REQUIRED) @@ -1928,6 +2021,19 @@ Draw an angled line to a given x coordinate. angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> angledLineToX({ angle: 45, to: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> line([0, 10], %) + |> close(%, "edge2") + |> extrude(10, %) + |> fillet({ radius: 2, tags: ["edge1"] }, %) +``` + #### Arguments * `data`: `AngledLineToData` - Data to draw an angled line to a point. (REQUIRED) @@ -2286,6 +2392,18 @@ Draw an angled line to a given y coordinate. angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> angledLineToY({ angle: 45, to: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> line([0, 10], %) + |> close(%, "edge2") + |> extrude(10, %) +``` + #### Arguments * `data`: `AngledLineToData` - Data to draw an angled line to a point. (REQUIRED) @@ -2644,6 +2762,20 @@ Draw an arc. arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('-YZ') + |> startProfileAt([0, 0], %) + |> arc({ + angle_start: 0, + angle_end: 360, + radius: 10, + tag: "edge1" + }, %) + |> extrude(10, %) +``` + #### Arguments * `data`: `ArcData` - Data to draw an arc. (REQUIRED) @@ -3013,6 +3145,12 @@ Computes the arcsine of a number (in radians). asin(num: number) -> number ``` +#### Examples + +```kcl +const myVar = asin(0.5) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -3033,6 +3171,12 @@ Computes the arctangent of a number (in radians). atan(num: number) -> number ``` +#### Examples + +```kcl +const myVar = atan(1.0) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -3053,6 +3197,21 @@ Draw a bezier curve. bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> bezierCurve({ + to: [10, 10], + control1: [5, 0], + control2: [5, 10], + tag: "edge1" + }, %) + |> close(%) + |> extrude(10, %) +``` + #### Arguments * `data`: `BezierData` - Data to draw a bezier curve. (REQUIRED) @@ -3412,6 +3571,12 @@ Computes the smallest integer greater than or equal to a number. ceil(num: number) -> number ``` +#### Examples + +```kcl +const myVar = ceil(4.5) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -3678,6 +3843,24 @@ Close the current sketch. close(sketch_group: SketchGroup, tag?: String) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XZ') + |> startProfileAt([0, 0], %) + |> line([10, 10], %) + |> line([10, 0], %) + |> close(%) +``` + +```kcl +startSketchOn('YZ') + |> startProfileAt([0, 0], %) + |> line([10, 10], %) + |> line([10, 0], %) + |> close(%, "edge1") +``` + #### Arguments * `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED) @@ -4025,6 +4208,12 @@ Computes the sine of a number (in radians). cos(num: number) -> number ``` +#### Examples + +```kcl +const anotherVar = cos(2 * pi()) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -4045,6 +4234,12 @@ Return the value of Euler’s number `e`. e() -> number ``` +#### Examples + +```kcl +const myVar = e() +``` + #### Arguments @@ -4064,6 +4259,18 @@ Extrudes by a given amount. extrude(length: number, sketch_group: SketchGroup) -> ExtrudeGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([0, 10], %) + |> line([10, 0], %) + |> line([0, -10], %) + |> close(%) + |> extrude(5, %) +``` + #### Arguments * `length`: `number` (REQUIRED) @@ -4378,6 +4585,19 @@ Create fillets on tagged paths. fillet(data: FilletData, extrude_group: ExtrudeGroup) -> ExtrudeGroup ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line({ to: [0, 10], tag: "thing" }, %) + |> line([10, 0], %) + |> line({ to: [0, -10], tag: "thing2" }, %) + |> close(%) + |> extrude(10, %) + |> fillet({ radius: 2, tags: ["thing", "thing2"] }, %) +``` + #### Arguments * `data`: `FilletData` - Data for fillets. (REQUIRED) @@ -4668,6 +4888,12 @@ Computes the largest integer less than or equal to a number. floor(num: number) -> number ``` +#### Examples + +```kcl +const myVar = floor(4.5) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -4688,6 +4914,20 @@ Returns the extrude wall transform. getExtrudeWallTransform(surface_name: string, extrude_group: ExtrudeGroup) -> ExtrudeTransform ``` +#### Examples + +```kcl +const box = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([0, 10], %) + |> line([10, 0], %) + |> line({ to: [0, -10], tag: "surface" }, %) + |> close(%) + |> extrude(5, %) + +const transform = getExtrudeWallTransform('surface', box) +``` + #### Arguments * `surface_name`: `string` (REQUIRED) @@ -4845,6 +5085,22 @@ Get the next adjacent edge to the edge given. getNextAdjacentEdge(tag: String, extrude_group: ExtrudeGroup) -> Uuid ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line({ to: [0, 10], tag: "thing" }, %) + |> line({ to: [10, 0], tag: "thing1" }, %) + |> line({ to: [0, -10], tag: "thing2" }, %) + |> close(%) + |> extrude(10, %) + |> fillet({ + radius: 2, + tags: [getNextAdjacentEdge("thing", %)] + }, %) +``` + #### Arguments * `tag`: `String` (REQUIRED) @@ -4996,6 +5252,22 @@ Get the opposite edge to the edge given. getOppositeEdge(tag: String, extrude_group: ExtrudeGroup) -> Uuid ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line({ to: [0, 10], tag: "thing" }, %) + |> line([10, 0], %) + |> line({ to: [0, -10], tag: "thing2" }, %) + |> close(%) + |> extrude(10, %) + |> fillet({ + radius: 2, + tags: ["thing", getOppositeEdge("thing", %)] + }, %) +``` + #### Arguments * `tag`: `String` (REQUIRED) @@ -5147,6 +5419,22 @@ Get the previous adjacent edge to the edge given. getPreviousAdjacentEdge(tag: String, extrude_group: ExtrudeGroup) -> Uuid ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line({ to: [0, 10], tag: "thing" }, %) + |> line({ to: [10, 0], tag: "thing1" }, %) + |> line({ to: [0, -10], tag: "thing2" }, %) + |> close(%) + |> extrude(10, %) + |> fillet({ + radius: 2, + tags: [getPreviousAdjacentEdge("thing2", %)] + }, %) +``` + #### Arguments * `tag`: `String` (REQUIRED) @@ -5298,6 +5586,20 @@ Use a sketch to cut a hole in another sketch. hole(hole_sketch_group: SketchGroupSet, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +const square = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([0, 10], %) + |> line([10, 0], %) + |> line([0, -10], %) + |> close(%) + |> hole(circle([2, 2], .5, startSketchOn('XY')), %) + |> hole(circle([2, 8], .5, startSketchOn('XY')), %) + |> extrude(2, %) +``` + #### Arguments * `hole_sketch_group`: `SketchGroupSet` - A sketch group or a group of sketch groups. (REQUIRED) @@ -5813,6 +6115,28 @@ Import paths are relative to the current project directory. This only works in t import(file_path: String, options?: ImportFormat) -> ImportedGeometry ``` +#### Examples + +```kcl +const model = import("thing.obj") +``` + +```kcl +const model = import("cube.obj", { type: "obj", units: "m" }) +``` + +```kcl +const model = import("my_model.gltf") +``` + +```kcl +const model = import("my_model.sldprt") +``` + +```kcl +const model = import("my_model.step") +``` + #### Arguments * `file_path`: `String` (REQUIRED) @@ -5922,6 +6246,18 @@ Returns the last segment of x. lastSegX(sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +startSketchOn("YZ") + |> startProfileAt([0, 0], %) + |> line({ to: [5, 0], tag: "thing" }, %) + |> line([5, 5], %) + |> line([0, lastSegX(%)], %) + |> close(%) + |> extrude(5, %) +``` + #### Arguments * `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED) @@ -6105,6 +6441,18 @@ Returns the last segment of y. lastSegY(sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +startSketchOn("YZ") + |> startProfileAt([0, 0], %) + |> line({ to: [5, 0], tag: "thing" }, %) + |> line([5, 5], %) + |> line([0, lastSegY(%)], %) + |> close(%) + |> extrude(5, %) +``` + #### Arguments * `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED) @@ -6288,6 +6636,12 @@ Returns the angle of the given leg for x. legAngX(hypotenuse: number, leg: number) -> number ``` +#### Examples + +```kcl +legAngX(5, 3) +``` + #### Arguments * `hypotenuse`: `number` (REQUIRED) @@ -6309,6 +6663,12 @@ Returns the angle of the given leg for y. legAngY(hypotenuse: number, leg: number) -> number ``` +#### Examples + +```kcl +legAngY(5, 3) +``` + #### Arguments * `hypotenuse`: `number` (REQUIRED) @@ -6330,6 +6690,12 @@ Returns the length of the given leg. legLen(hypotenuse: number, leg: number) -> number ``` +#### Examples + +```kcl +legLen(5, 3) +``` + #### Arguments * `hypotenuse`: `number` (REQUIRED) @@ -6351,6 +6717,17 @@ Draw a line. line(data: LineData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('-XY') + |> startProfileAt([0, 0], %) + |> line([10, 10], %) + |> line({ to: [20, 10], tag: "edge1" }, %) + |> close(%, "edge2") + |> extrude(10, %) +``` + #### Arguments * `data`: `LineData` - Data to draw a line. (REQUIRED) @@ -6707,6 +7084,32 @@ Draw a line to a point. lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +fn rectShape = (pos, w, l) => { + const rr = startSketchOn('YZ') + |> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %) + |> lineTo({ + to: [pos[0] + w / 2, pos[1] - (l / 2)], + tag: "edge1" + }, %) + |> lineTo({ + to: [pos[0] + w / 2, pos[1] + l / 2], + tag: "edge2" + }, %) + |> lineTo({ + to: [pos[0] - (w / 2), pos[1] + l / 2], + tag: "edge3" + }, %) + |> close(%, "edge4") + return rr +} + +// Create the mounting plate extrusion, holes, and fillets +const part = rectShape([0, 0], 20, 20) +``` + #### Arguments * `data`: `LineToData` - Data to draw a line to a point. (REQUIRED) @@ -7063,6 +7466,12 @@ Computes the natural logarithm of the number. ln(num: number) -> number ``` +#### Examples + +```kcl +const myVar = ln(4) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -7083,6 +7492,12 @@ The result might not be correctly rounded owing to implementation details; `log2 log(num: number, base: number) -> number ``` +#### Examples + +```kcl +const myVar = log(4, 2) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -7104,6 +7519,12 @@ Computes the base 10 logarithm of the number. log10(num: number) -> number ``` +#### Examples + +```kcl +const myVar = log10(4) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -7124,6 +7545,12 @@ Computes the base 2 logarithm of the number. log2(num: number) -> number ``` +#### Examples + +```kcl +const myVar = log2(4) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -7144,6 +7571,12 @@ Computes the maximum of the given arguments. max(args: [number]) -> number ``` +#### Examples + +```kcl +const myVar = max(4, 5, 6) +``` + #### Arguments * `args`: `[number]` (REQUIRED) @@ -7164,6 +7597,12 @@ Computes the minimum of the given arguments. min(args: [number]) -> number ``` +#### Examples + +```kcl +const myVar = min(4, 5, 6) +``` + #### Arguments * `args`: `[number]` (REQUIRED) @@ -7184,6 +7623,19 @@ A circular pattern on a 2D sketch. patternCircular2d(data: CircularPattern2dData, sketch_group: SketchGroup) -> [SketchGroup] ``` +#### Examples + +```kcl +const part = startSketchOn('XY') + |> circle([0, 0], 2, %) + |> patternCircular2d({ + center: [20, 20], + repetitions: 12, + arcDegrees: 210, + rotateDuplicates: true + }, %) +``` + #### Arguments * `data`: `CircularPattern2dData` - Data for a circular pattern on a 2D sketch. (REQUIRED) @@ -7380,6 +7832,25 @@ A circular pattern on a 3D model. patternCircular3d(data: CircularPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup] ``` +#### Examples + +```kcl +const part = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([0, 1], %) + |> line([1, 0], %) + |> line([0, -1], %) + |> close(%) + |> extrude(1, %) + |> patternCircular3d({ + axis: [1, 1, 0], + center: [10, 0, 10], + repetitions: 10, + arcDegrees: 360, + rotateDuplicates: true + }, %) +``` + #### Arguments * `data`: `CircularPattern3dData` - Data for a circular pattern on a 3D model. (REQUIRED) @@ -7545,6 +8016,18 @@ A linear pattern on a 2D sketch. patternLinear2d(data: LinearPattern2dData, sketch_group: SketchGroup) -> [SketchGroup] ``` +#### Examples + +```kcl +const part = startSketchOn('XY') + |> circle([0, 0], 2, %) + |> patternLinear2d({ + axis: [0, 1], + repetitions: 12, + distance: 2 + }, %) +``` + #### Arguments * `data`: `LinearPattern2dData` - Data for a linear pattern on a 2D sketch. (REQUIRED) @@ -7739,6 +8222,23 @@ A linear pattern on a 3D model. patternLinear3d(data: LinearPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup] ``` +#### Examples + +```kcl +const part = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([0, 1], %) + |> line([1, 0], %) + |> line([0, -1], %) + |> close(%) + |> extrude(1, %) + |> patternLinear3d({ + axis: [1, 0, 1], + repetitions: 3, + distance: 6 + }, %) +``` + #### Arguments * `data`: `LinearPattern3dData` - Data for a linear pattern on a 3D model. (REQUIRED) @@ -7900,6 +8400,12 @@ Return the value of `pi`. Archimedes’ constant (π). pi() -> number ``` +#### Examples + +```kcl +const myVar = pi() * 3.0 +``` + #### Arguments @@ -7919,6 +8425,12 @@ Computes the number to a power. pow(num: number, pow: number) -> number ``` +#### Examples + +```kcl +const myVar = pow(4, 2) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -7940,6 +8452,20 @@ Returns the angle of the segment. segAng(segment_name: string, sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +const part001 = startSketchOn('XY') + |> startProfileAt([4.83, 12.56], %) + |> line([15.1, 2.48], %) + |> line({ to: [3.15, -9.85], tag: 'seg01' }, %) + |> line([-15.17, -4.1], %) + |> angledLine([segAng('seg01', %), 12.35], %) + |> line([-13.02, 10.03], %) + |> close(%) + |> extrude(4, %) +``` + #### Arguments * `segment_name`: `string` (REQUIRED) @@ -8124,6 +8650,18 @@ Returns the segment end of x. segEndX(segment_name: string, sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +startSketchOn("YZ") + |> startProfileAt([0, 0], %) + |> line({ to: [5, 0], tag: "thing" }, %) + |> line([5, 5], %) + |> line([segEndX("thing", %), 5], %) + |> close(%) + |> extrude(5, %) +``` + #### Arguments * `segment_name`: `string` (REQUIRED) @@ -8308,6 +8846,18 @@ Returns the segment end of y. segEndY(segment_name: string, sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +startSketchOn("YZ") + |> startProfileAt([0, 0], %) + |> line({ to: [5, 0], tag: "thing" }, %) + |> line([5, 5], %) + |> line([segEndY("thing", %), 5], %) + |> close(%) + |> extrude(5, %) +``` + #### Arguments * `segment_name`: `string` (REQUIRED) @@ -8492,6 +9042,18 @@ Returns the length of the segment. segLen(segment_name: string, sketch_group: SketchGroup) -> number ``` +#### Examples + +```kcl +startSketchOn("YZ") + |> startProfileAt([0, 0], %) + |> line({ to: [5, 0], tag: "thing" }, %) + |> line([5, 5], %) + |> line([0, segLen("thing", %)], %) + |> close(%) + |> extrude(5, %) +``` + #### Arguments * `segment_name`: `string` (REQUIRED) @@ -8676,6 +9238,12 @@ Computes the sine of a number (in radians). sin(num: number) -> number ``` +#### Examples + +```kcl +const myVar = sin(2 * pi()) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -8696,6 +9264,12 @@ Computes the square root of a number. sqrt(num: number) -> number ``` +#### Examples + +```kcl +const myVar = sqrt(4) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -8716,6 +9290,14 @@ Start a profile at a given point. startProfileAt(data: LineData, sketch_surface: SketchSurface) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([10, 10], %) +``` + #### Arguments * `data`: `LineData` - Data to draw a line. (REQUIRED) @@ -8969,6 +9551,13 @@ Start a sketch at a given point on the 'XY' plane. startSketchAt(data: LineData) -> SketchGroup ``` +#### Examples + +```kcl +startSketchAt([0, 0]) + |> line([10, 10], %) +``` + #### Arguments * `data`: `LineData` - Data to draw a line. (REQUIRED) @@ -9161,6 +9750,39 @@ Start a sketch on a specific plane or face. startSketchOn(data: SketchData, tag?: SketchOnFaceTag) -> SketchSurface ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([10, 10], %) + |> line({ to: [20, 10], tag: "edge1" }, %) + |> close(%, "edge2") +``` + +```kcl +fn cube = (pos, scale) => { + const sg = startSketchOn('XY') + |> startProfileAt(pos, %) + |> line([0, scale], %) + |> line([scale, 0], %) + |> line([0, -scale], %) + |> close(%) + |> extrude(scale, %) + + return sg +} + +const box = cube([0, 0], 20) + +const part001 = startSketchOn(box, "start") + |> startProfileAt([0, 0], %) + |> line([10, 10], %) + |> line({ to: [20, 10], tag: "edge1" }, %) + |> close(%) + |> extrude(20, %) +``` + #### Arguments * `data`: `SketchData` - Data for start sketch on. You can start a sketch on a plane or an extrude group. (REQUIRED) @@ -9410,6 +10032,12 @@ Computes the tangent of a number (in radians). tan(num: number) -> number ``` +#### Examples + +```kcl +const myVar = tan(2 * pi()) +``` + #### Arguments * `num`: `number` (REQUIRED) @@ -9430,6 +10058,17 @@ Draw an arc. tangentialArc(data: TangentialArcData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('-YZ') + |> startProfileAt([0, 0], %) + |> line({ to: [10, 10], tag: "edge0" }, %) + |> tangentialArc({ radius: 10, offset: 90, tag: "edge1" }, %) + |> close(%) + |> extrude(10, %) +``` + #### Arguments * `data`: `TangentialArcData` - Data to draw a tangential arc. (REQUIRED) @@ -9792,6 +10431,16 @@ Draw an arc. tangentialArcTo(to: [number], sketch_group: SketchGroup, tag?: String) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('-YZ') + |> startProfileAt([0, 0], %) + |> line({ to: [10, 10], tag: "edge0" }, %) + |> tangentialArcTo([10, 0], %) + |> close(%) +``` + #### Arguments * `to`: `[number]` (REQUIRED) @@ -10140,6 +10789,12 @@ Return the value of `tau`. The full circle constant (τ). Equal to 2π. tau() -> number ``` +#### Examples + +```kcl +const myVar = tau() +``` + #### Arguments @@ -10149,14 +10804,20 @@ tau() -> number -### to_degrees +### toDegrees Converts a number from radians to degrees. ``` -to_degrees(num: number) -> number +toDegrees(num: number) -> number +``` + +#### Examples + +```kcl +const myVar = toDegrees(2 * pi()) ``` #### Arguments @@ -10169,14 +10830,20 @@ to_degrees(num: number) -> number -### to_radians +### toRadians Converts a number from degrees to radians. ``` -to_radians(num: number) -> number +toRadians(num: number) -> number +``` + +#### Examples + +```kcl +const myVar = toRadians(180) ``` #### Arguments @@ -10199,6 +10866,17 @@ Draw a line on the x-axis. xLine(data: AxisLineData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('YZ') + |> startProfileAt([0, 0], %) + |> xLine(10, %) + |> line([10, 10], %) + |> close(%, "edge1") + |> extrude(10, %) +``` + #### Arguments * `data`: `AxisLineData` - Data to draw a line on an axis. (REQUIRED) @@ -10555,6 +11233,17 @@ Draw a line to a point on the x-axis. xLineTo(data: AxisLineToData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> xLineTo({ to: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> close(%, "edge2") + |> extrude(10, %) +``` + #### Arguments * `data`: `AxisLineToData` - Data to draw a line to a point on an axis. (REQUIRED) @@ -10911,6 +11600,17 @@ Draw a line on the y-axis. yLine(data: AxisLineData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> yLine(10, %) + |> line([10, 10], %) + |> close(%, "edge1") + |> extrude(10, %) +``` + #### Arguments * `data`: `AxisLineData` - Data to draw a line on an axis. (REQUIRED) @@ -11267,6 +11967,18 @@ Draw a line to a point on the y-axis. yLineTo(data: AxisLineToData, sketch_group: SketchGroup) -> SketchGroup ``` +#### Examples + +```kcl +startSketchOn('XZ') + |> startProfileAt([0, 0], %) + |> yLineTo({ to: 10, tag: "edge1" }, %) + |> line([10, 10], %) + |> close(%, "edge2") + |> extrude(10, %) + |> fillet({ radius: 2, tags: ["edge2"] }, %) +``` + #### Arguments * `data`: `AxisLineToData` - Data to draw a line to a point on an axis. (REQUIRED) diff --git a/src/wasm-lib/Cargo.lock b/src/wasm-lib/Cargo.lock index ce9d5a1a49..970c58143f 100644 --- a/src/wasm-lib/Cargo.lock +++ b/src/wasm-lib/Cargo.lock @@ -947,8 +947,9 @@ dependencies = [ [[package]] name = "derive-docs" -version = "0.1.10" +version = "0.1.11" dependencies = [ + "Inflector", "convert_case", "expectorate", "once_cell", @@ -962,22 +963,6 @@ dependencies = [ "syn 2.0.52", ] -[[package]] -name = "derive-docs" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc85a0d10f808387cd56147b520be7efd94b8b198b1453f987b133cd2a90b7e" -dependencies = [ - "convert_case", - "once_cell", - "proc-macro2", - "quote", - "regex", - "serde", - "serde_tokenstream", - "syn 2.0.52", -] - [[package]] name = "diesel_derives" version = "2.1.2" @@ -1919,7 +1904,7 @@ dependencies = [ "criterion", "dashmap", "databake", - "derive-docs 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "derive-docs", "expectorate", "futures", "gltf-json", diff --git a/src/wasm-lib/derive-docs/Cargo.toml b/src/wasm-lib/derive-docs/Cargo.toml index af12ada2aa..676dacc067 100644 --- a/src/wasm-lib/derive-docs/Cargo.toml +++ b/src/wasm-lib/derive-docs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "derive-docs" description = "A tool for generating documentation from Rust derive macros" -version = "0.1.10" +version = "0.1.11" edition = "2021" license = "MIT" repository = "https://github.com/KittyCAD/modeling-app" @@ -12,6 +12,7 @@ rust-version = "1.73" proc-macro = true [dependencies] +Inflector = "0.11.4" convert_case = "0.6.0" once_cell = "1.19.0" proc-macro2 = "1" diff --git a/src/wasm-lib/derive-docs/src/lib.rs b/src/wasm-lib/derive-docs/src/lib.rs index 554b448302..efde2d95fd 100644 --- a/src/wasm-lib/derive-docs/src/lib.rs +++ b/src/wasm-lib/derive-docs/src/lib.rs @@ -2,7 +2,12 @@ // automated enforcement. #![allow(clippy::style)] +#[cfg(test)] +mod tests; +mod unbox; + use convert_case::Casing; +use inflector::Inflector; use once_cell::sync::Lazy; use quote::{format_ident, quote, quote_spanned, ToTokens}; use regex::Regex; @@ -12,6 +17,7 @@ use syn::{ parse::{Parse, ParseStream}, Attribute, Signature, Visibility, }; +use unbox::unbox; #[derive(Deserialize, Debug)] struct StdlibMetadata { @@ -101,6 +107,21 @@ fn do_stdlib_inner( } let name = metadata.name; + + // Fail if the name is not camel case. + let whitelist = [ + "patternLinear3d", + "patternLinear2d", + "patternCircular3d", + "patternCircular2d", + ]; + if !name.is_camel_case() && !whitelist.contains(&name.as_str()) { + errors.push(Error::new_spanned( + &ast.sig.ident, + format!("stdlib function names must be in camel case: `{}`", name), + )); + } + let name_ident = format_ident!("{}", name.to_case(convert_case::Case::UpperCamel)); let name_str = name.to_string(); @@ -110,16 +131,16 @@ fn do_stdlib_inner( let boxed_fn_name_ident = format_ident!("boxed_{}", fn_name_str); let _visibility = &ast.vis; - let (summary_text, description_text) = extract_doc_from_attrs(&ast.attrs); + let doc_info = extract_doc_from_attrs(&ast.attrs); let comment_text = { let mut buf = String::new(); buf.push_str("Std lib function: "); buf.push_str(&name_str); - if let Some(s) = &summary_text { + if let Some(s) = &doc_info.summary { buf.push_str("\n"); buf.push_str(&s); } - if let Some(s) = &description_text { + if let Some(s) = &doc_info.description { buf.push_str("\n"); buf.push_str(&s); } @@ -129,17 +150,60 @@ fn do_stdlib_inner( #[doc = #comment_text] }; - let summary = if let Some(summary) = summary_text { + let summary = if let Some(summary) = doc_info.summary { quote! { #summary } } else { quote! { "" } }; - let description = if let Some(description) = description_text { + let description = if let Some(description) = doc_info.description { quote! { #description } } else { quote! { "" } }; + let cb = doc_info.code_blocks.clone(); + let code_blocks = if !cb.is_empty() { + quote! { + let code_blocks = vec![#(#cb),*]; + code_blocks.iter().map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }).collect::>() + } + } else { + errors.push(Error::new_spanned( + &ast.sig, + "stdlib functions must have at least one code block", + )); + + quote! { vec![] } + }; + + // Make sure the function name is in all the code blocks. + for code_block in doc_info.code_blocks.iter() { + if !code_block.contains(&name) { + errors.push(Error::new_spanned( + &ast.sig, + format!( + "stdlib functions must have the function name `{}` in the code block", + name + ), + )); + } + } + + let test_code_blocks = doc_info + .code_blocks + .iter() + .enumerate() + .map(|(index, code_block)| generate_code_block_test(&fn_name_str, code_block, index)) + .collect::>(); + let tags = metadata .tags .iter() @@ -242,7 +306,7 @@ fn do_stdlib_inner( let mut args = args.iter(); let ok = args.next().unwrap(); if let syn::GenericArgument::Type(ty) = ok { - let ty = unbox(unbox_vec(ty.clone())); + let ty = unbox(ty.clone()); quote! { #ty } } else { quote! { () } @@ -254,7 +318,7 @@ fn do_stdlib_inner( quote! { () } } } else { - let ty = unbox(unbox_vec(*ty.clone())); + let ty = unbox(*ty.clone()); quote! { #ty } } } else { @@ -295,9 +359,16 @@ fn do_stdlib_inner( pub(crate) const #name_ident: #name_ident = #name_ident {}; }; + let test_mod_name = format_ident!("test_examples_{}", fn_name_str); + // The final TokenStream returned will have a few components that reference // `#name_ident`, the name of the function to which this macro was applied... let stream = quote! { + #[cfg(test)] + mod #test_mod_name { + #(#test_code_blocks)* + } + // ... a struct type called `#name_ident` that has no members #[allow(non_camel_case_types, missing_docs)] #description_doc_comment @@ -359,6 +430,10 @@ fn do_stdlib_inner( #deprecated } + fn examples(&self) -> Vec { + #code_blocks + } + fn std_lib_fn(&self) -> crate::std::StdFn { #boxed_fn_name_ident } @@ -394,10 +469,18 @@ fn get_crate(var: Option) -> proc_macro2::TokenStream { quote!(crate::docs) } -fn extract_doc_from_attrs(attrs: &[syn::Attribute]) -> (Option, Option) { +#[derive(Debug)] +struct DocInfo { + pub summary: Option, + pub description: Option, + pub code_blocks: Vec, +} + +fn extract_doc_from_attrs(attrs: &[syn::Attribute]) -> DocInfo { let doc = syn::Ident::new("doc", proc_macro2::Span::call_site()); + let mut code_blocks: Vec = Vec::new(); - let mut lines = attrs.iter().flat_map(|attr| { + let raw_lines = attrs.iter().flat_map(|attr| { if let syn::Meta::NameValue(nv) = &attr.meta { if nv.path.is_ident(&doc) { if let syn::Expr::Lit(syn::ExprLit { @@ -411,6 +494,53 @@ fn extract_doc_from_attrs(attrs: &[syn::Attribute]) -> (Option, Option = None; + let mut parsed_lines = Vec::new(); + for line in raw_lines { + if line.starts_with("```") { + if let Some(ref inner_code_block) = code_block { + code_blocks.push(inner_code_block.trim().to_string()); + code_block = None; + } else { + code_block = Some(String::new()); + } + + continue; + } + if let Some(ref mut code_block) = code_block { + code_block.push_str(&line); + code_block.push('\n'); + } else { + parsed_lines.push(line); + } + } + + // Parse code blocks that start with a tab or a space. + let mut lines = Vec::new(); + for line in parsed_lines { + if line.starts_with(" ") || line.starts_with('\t') { + if let Some(ref mut code_block) = code_block { + code_block.push_str(&line.trim_start_matches(" ").trim_start_matches('\t')); + code_block.push('\n'); + } else { + code_block = Some(format!("{}\n", line)); + } + } else { + if let Some(ref inner_code_block) = code_block { + code_blocks.push(inner_code_block.trim().to_string()); + code_block = None; + } + lines.push(line); + } + } + + let mut lines = lines.into_iter(); + + if let Some(code_block) = code_block { + code_blocks.push(code_block.trim().to_string()); + } + // Skip initial blank lines; they make for excessively terse summaries. let summary = loop { match lines.next() { @@ -426,7 +556,7 @@ fn extract_doc_from_attrs(attrs: &[syn::Attribute]) -> (Option, Option (None, None), (summary, None) => (summary, None), (Some(summary), Some(first)) => ( @@ -449,6 +579,12 @@ fn extract_doc_from_attrs(attrs: &[syn::Attribute]) -> (Option, Option Vec { .map(|(idx, s)| { // Rust-style comments are intrinsically single-line. We don't want // to trim away formatting such as an initial '*'. + // We also don't want to trim away a tab character, which is + // used to denote a code block. Code blocks can also be denoted + // by four spaces, but we don't want to trim those away either. + // We only want to trim a single space character from the start of + // a line, and only if it's the first character. + let new = s + .chars() + .enumerate() + .flat_map(|(idx, c)| { + if idx == 0 { + if c == ' ' { + return None; + } + } + Some(c) + }) + .collect::() + .trim_end() + .to_string(); + let s = new.as_str(); + if idx == 0 { - s.trim_start().trim_end() + s } else { - let trimmed = s.trim_start().trim_end(); - trimmed - .strip_prefix("* ") - .unwrap_or_else(|| trimmed.strip_prefix('*').unwrap_or(trimmed)) + s.strip_prefix("* ").unwrap_or_else(|| s.strip_prefix('*').unwrap_or(s)) } + .to_string() }) - .map(ToString::to_string) .collect() } @@ -575,319 +729,59 @@ fn parse_array_type(type_name: &str) -> Option<(&str, usize)> { Some((inner_type.as_str(), length)) } -// Unbox a syn::Type that is boxed to the inner object. -fn unbox(t: syn::Type) -> syn::Type { - match t { - syn::Type::Path(syn::TypePath { ref path, .. }) => { - let path = &path.segments; - if path.len() == 1 { - let seg = &path[0]; - if seg.ident == "Box" { - if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }) = - &seg.arguments - { - if args.len() == 1 { - let mut args = args.iter(); - let ok = args.next().unwrap(); - if let syn::GenericArgument::Type(ty) = ok { - return ty.clone(); - } - } - } - } - } - } - _ => { - return t; - } - } - t -} +// For each kcl code block, we want to generate a test that checks that the +// code block is valid kcl code and compiles and executes. +fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> proc_macro2::TokenStream { + let test_name = format_ident!("serial_test_example_{}{}", fn_name, index); -// For a Vec> return Vec. -// For a Vec return Vec. -// For a Box return T. -fn unbox_vec(t: syn::Type) -> syn::Type { - match t { - syn::Type::Path(syn::TypePath { ref path, .. }) => { - let path = &path.segments; - if path.len() == 1 { - let seg = &path[0]; - if seg.ident == "Vec" { - if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }) = - &seg.arguments - { - if args.len() == 1 { - let mut args = args.iter(); - let ok = args.next().unwrap(); - if let syn::GenericArgument::Type(ty) = ok { - let unboxed = unbox(ty.clone()); - // Wrap it back in a vec. - let wrapped = syn::Type::Path(syn::TypePath { - qself: None, - path: syn::Path { - leading_colon: None, - segments: { - let mut segments = syn::punctuated::Punctuated::new(); - segments.push_value(syn::PathSegment { - ident: syn::Ident::new("Vec", proc_macro2::Span::call_site()), - arguments: syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { - colon2_token: None, - lt_token: syn::token::Lt::default(), - args: { - let mut args = syn::punctuated::Punctuated::new(); - args.push_value(syn::GenericArgument::Type(unboxed)); - args - }, - gt_token: syn::token::Gt::default(), - }, - ), - }); - segments - }, - }, - }); - return wrapped; - } - } - } - } - } - } - _ => { - return t; - } - } - t -} - -#[cfg(test)] -mod tests { - - use quote::quote; - - use super::*; - - #[test] - fn test_get_inner_array_type() { - for (expected, input) in [ - (Some(("f64", 2)), "[f64;2]"), - (Some(("String", 2)), "[String; 2]"), - (Some(("Option", 12)), "[Option;12]"), - (Some(("Option", 12)), "[Option; 12]"), - ] { - let actual = parse_array_type(input); - assert_eq!(actual, expected); - } - } - - #[test] - fn test_stdlib_line_to() { - let (item, errors) = do_stdlib( - quote! { - name = "lineTo", - }, - quote! { - fn inner_line_to( - data: LineToData, - sketch_group: SketchGroup, - args: &Args, - ) -> Result { - Ok(()) - } - }, - ) - .unwrap(); - let _expected = quote! {}; - - assert!(errors.is_empty()); - expectorate::assert_contents("tests/lineTo.gen", &openapitor::types::get_text_fmt(&item).unwrap()); - } - - #[test] - fn test_stdlib_min() { - let (item, errors) = do_stdlib( - quote! { - name = "min", - }, - quote! { - fn inner_min( - /// The args to do shit to. - args: Vec - ) -> f64 { - let mut min = std::f64::MAX; - for arg in args.iter() { - if *arg < min { - min = *arg; - } - } - - min - } - }, - ) - .unwrap(); - let _expected = quote! {}; - - assert!(errors.is_empty()); - expectorate::assert_contents("tests/min.gen", &openapitor::types::get_text_fmt(&item).unwrap()); - } - - #[test] - fn test_stdlib_show() { - let (item, errors) = do_stdlib( - quote! { - name = "show", - }, - quote! { - fn inner_show( - /// The args to do shit to. - _args: Vec - ) { - } - }, - ) - .unwrap(); - let _expected = quote! {}; - - assert!(errors.is_empty()); - expectorate::assert_contents("tests/show.gen", &openapitor::types::get_text_fmt(&item).unwrap()); - } - - #[test] - fn test_stdlib_box() { - let (item, errors) = do_stdlib( - quote! { - name = "show", - }, - quote! { - fn inner_show( - /// The args to do shit to. - args: Box - ) -> Box { - args - } - }, - ) - .unwrap(); - let _expected = quote! {}; - - assert!(errors.is_empty()); - expectorate::assert_contents("tests/box.gen", &openapitor::types::get_text_fmt(&item).unwrap()); - } - - #[test] - fn test_stdlib_option() { - let (item, errors) = do_stdlib( - quote! { - name = "show", - }, - quote! { - fn inner_show( - /// The args to do shit to. - args: Option - ) -> Result> { - args - } - }, - ) - .unwrap(); - - assert!(errors.is_empty()); - expectorate::assert_contents("tests/option.gen", &openapitor::types::get_text_fmt(&item).unwrap()); - } - - #[test] - fn test_stdlib_array() { - let (item, errors) = do_stdlib( - quote! { - name = "show", - }, - quote! { - fn inner_show( - /// The args to do shit to. - args: [f64; 2] - ) -> Result> { - args - } - }, - ) - .unwrap(); - - assert!(errors.is_empty()); - expectorate::assert_contents("tests/array.gen", &openapitor::types::get_text_fmt(&item).unwrap()); - } - - #[test] - fn test_stdlib_option_input_format() { - let (item, errors) = do_stdlib( - quote! { - name = "import", - }, - quote! { - fn inner_import( - /// The args to do shit to. - args: Option - ) -> Result> { - args - } - }, - ) - .unwrap(); - - assert!(errors.is_empty()); - expectorate::assert_contents( - "tests/option_input_format.gen", - &openapitor::types::get_text_fmt(&item).unwrap(), - ); - } + // TODO: We ignore import for now, because the files don't exist and we just want + // to show easy imports. + let ignored = if fn_name == "import" { + quote! { #[ignore] } + } else { + quote! {} + }; - #[test] - fn test_stdlib_return_vec_sketch_group() { - let (item, errors) = do_stdlib( - quote! { - name = "import", - }, - quote! { - fn inner_import( - /// The args to do shit to. - args: Option - ) -> Result> { - args - } - }, - ) - .unwrap(); - - assert!(errors.is_empty()); - expectorate::assert_contents( - "tests/return_vec_sketch_group.gen", - &openapitor::types::get_text_fmt(&item).unwrap(), - ); - } + quote! { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #ignored + async fn #test_name() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + // For file conversions we need this to be long. + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + // For file conversions we need this to be long. + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + + // Create the client. + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await.unwrap(); + + let tokens = crate::token::lexer(#code_block); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new(crate::engine::conn::EngineConnection::new(ws).await.unwrap())), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; - #[test] - fn test_stdlib_return_vec_box_sketch_group() { - let (item, errors) = do_stdlib( - quote! { - name = "import", - }, - quote! { - fn inner_import( - /// The args to do shit to. - args: Option - ) -> Result>> { - args - } - }, - ) - .unwrap(); - - assert!(errors.is_empty()); - expectorate::assert_contents( - "tests/return_vec_box_sketch_group.gen", - &openapitor::types::get_text_fmt(&item).unwrap(), - ); + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx).await.unwrap(); + } } } diff --git a/src/wasm-lib/derive-docs/src/tests.rs b/src/wasm-lib/derive-docs/src/tests.rs new file mode 100644 index 0000000000..fe348acc80 --- /dev/null +++ b/src/wasm-lib/derive-docs/src/tests.rs @@ -0,0 +1,461 @@ +use quote::quote; + +use crate::{do_stdlib, parse_array_type}; + +#[test] +fn test_get_inner_array_type() { + for (expected, input) in [ + (Some(("f64", 2)), "[f64;2]"), + (Some(("String", 2)), "[String; 2]"), + (Some(("Option", 12)), "[Option;12]"), + (Some(("Option", 12)), "[Option; 12]"), + ] { + let actual = parse_array_type(input); + assert_eq!(actual, expected); + } +} + +#[test] +fn test_stdlib_line_to() { + let (item, errors) = do_stdlib( + quote! { + name = "lineTo", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// lineTo + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// lineTo + /// ``` + fn inner_line_to( + data: LineToData, + sketch_group: SketchGroup, + args: &Args, + ) -> Result { + Ok(()) + } + }, + ) + .unwrap(); + let _expected = quote! {}; + + assert!(errors.is_empty()); + expectorate::assert_contents("tests/lineTo.gen", &openapitor::types::get_text_fmt(&item).unwrap()); +} + +#[test] +fn test_stdlib_min() { + let (item, errors) = do_stdlib( + quote! { + name = "min", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// min + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// min + /// ``` + fn inner_min( + /// The args to do shit to. + args: Vec + ) -> f64 { + let mut min = std::f64::MAX; + for arg in args.iter() { + if *arg < min { + min = *arg; + } + } + + min + } + }, + ) + .unwrap(); + let _expected = quote! {}; + + assert!(errors.is_empty()); + expectorate::assert_contents("tests/min.gen", &openapitor::types::get_text_fmt(&item).unwrap()); +} + +#[test] +fn test_stdlib_show() { + let (item, errors) = do_stdlib( + quote! { + name = "show", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// show + fn inner_show( + /// The args to do shit to. + _args: Vec + ) { + } + }, + ) + .unwrap(); + let _expected = quote! {}; + + assert!(errors.is_empty()); + expectorate::assert_contents("tests/show.gen", &openapitor::types::get_text_fmt(&item).unwrap()); +} + +#[test] +fn test_stdlib_box() { + let (item, errors) = do_stdlib( + quote! { + name = "show", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// show + fn inner_show( + /// The args to do shit to. + args: Box + ) -> Box { + args + } + }, + ) + .unwrap(); + let _expected = quote! {}; + + assert!(errors.is_empty()); + expectorate::assert_contents("tests/box.gen", &openapitor::types::get_text_fmt(&item).unwrap()); +} + +#[test] +fn test_stdlib_option() { + let (item, errors) = do_stdlib( + quote! { + name = "show", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// show + fn inner_show( + /// The args to do shit to. + args: Option + ) -> Result> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents("tests/option.gen", &openapitor::types::get_text_fmt(&item).unwrap()); +} + +#[test] +fn test_stdlib_array() { + let (item, errors) = do_stdlib( + quote! { + name = "show", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// show + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// show + /// ``` + fn inner_show( + /// The args to do shit to. + args: [f64; 2] + ) -> Result> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents("tests/array.gen", &openapitor::types::get_text_fmt(&item).unwrap()); +} + +#[test] +fn test_stdlib_option_input_format() { + let (item, errors) = do_stdlib( + quote! { + name = "import", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// import + fn inner_import( + /// The args to do shit to. + args: Option + ) -> Result> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents( + "tests/option_input_format.gen", + &openapitor::types::get_text_fmt(&item).unwrap(), + ); +} + +#[test] +fn test_stdlib_return_vec_sketch_group() { + let (item, errors) = do_stdlib( + quote! { + name = "import", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// import + fn inner_import( + /// The args to do shit to. + args: Option + ) -> Result> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents( + "tests/return_vec_sketch_group.gen", + &openapitor::types::get_text_fmt(&item).unwrap(), + ); +} + +#[test] +fn test_stdlib_return_vec_box_sketch_group() { + let (item, errors) = do_stdlib( + quote! { + name = "import", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// import + fn inner_import( + /// The args to do shit to. + args: Option + ) -> Result>> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents( + "tests/return_vec_box_sketch_group.gen", + &openapitor::types::get_text_fmt(&item).unwrap(), + ); +} + +#[test] +fn test_stdlib_doc_comment_with_code() { + let (item, errors) = do_stdlib( + quote! { + name = "myFunc", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// myFunc + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// myFunc + /// ``` + fn inner_my_func( + /// The args to do shit to. + args: Option + ) -> Result>> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents( + "tests/doc_comment_with_code.gen", + &openapitor::types::get_text_fmt(&item).unwrap(), + ); +} + +#[test] +fn test_stdlib_doc_comment_with_code_on_ignored_function() { + let (item, errors) = do_stdlib( + quote! { + name = "import", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// import + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// import + /// ``` + fn inner_import( + /// The args to do shit to. + args: Option + ) -> Result>> { + args + } + }, + ) + .unwrap(); + + assert!(errors.is_empty()); + expectorate::assert_contents( + "tests/doc_comment_with_code_on_ignored_function.gen", + &openapitor::types::get_text_fmt(&item).unwrap(), + ); +} + +#[test] +fn test_stdlib_fail_non_camel_case() { + let (_, errors) = do_stdlib( + quote! { + name = "import_thing", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// ``` + fn inner_import_thing( + /// The args to do shit to. + args: Option + ) -> Result>> { + args + } + }, + ) + .unwrap(); + + assert!(!errors.is_empty()); + assert_eq!( + errors[1].to_string(), + "stdlib function names must be in camel case: `import_thing`" + ); +} + +#[test] +fn test_stdlib_fail_no_code_block() { + let (_, errors) = do_stdlib( + quote! { + name = "import", + }, + quote! { + fn inner_import( + /// The args to do shit to. + args: Option + ) -> Result>> { + args + } + }, + ) + .unwrap(); + + assert!(!errors.is_empty()); + assert_eq!( + errors[1].to_string(), + "stdlib functions must have at least one code block" + ); +} + +#[test] +fn test_stdlib_fail_name_not_in_code_block() { + let (_, errors) = do_stdlib( + quote! { + name = "import", + }, + quote! { + /// This is some function. + /// It does shit. + /// + /// This is code. + /// It does other shit. + /// + /// ``` + /// This is another code block. + /// yes sirrr. + /// ``` + fn inner_import( + /// The args to do shit to. + args: Option + ) -> Result>> { + args + } + }, + ) + .unwrap(); + + assert!(!errors.is_empty()); + assert_eq!( + errors[1].to_string(), + "stdlib functions must have the function name `import` in the code block" + ); +} diff --git a/src/wasm-lib/derive-docs/src/unbox.rs b/src/wasm-lib/derive-docs/src/unbox.rs new file mode 100644 index 0000000000..eceb8316de --- /dev/null +++ b/src/wasm-lib/derive-docs/src/unbox.rs @@ -0,0 +1,91 @@ +// Unbox a Vec> to Vec. +// Unbox a Box to T. +pub(crate) fn unbox(t: syn::Type) -> syn::Type { + unbox_inner(unbox_vec(t)) +} + +// Unbox a syn::Type that is boxed to the inner object. +fn unbox_inner(t: syn::Type) -> syn::Type { + match t { + syn::Type::Path(syn::TypePath { ref path, .. }) => { + let path = &path.segments; + if path.len() == 1 { + let seg = &path[0]; + if seg.ident == "Box" { + if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }) = + &seg.arguments + { + if args.len() == 1 { + let mut args = args.iter(); + let ok = args.next().unwrap(); + if let syn::GenericArgument::Type(ty) = ok { + return ty.clone(); + } + } + } + } + } + } + _ => { + return t; + } + } + t +} + +// For a Vec> return Vec. +// For a Vec return Vec. +fn unbox_vec(t: syn::Type) -> syn::Type { + match t { + syn::Type::Path(syn::TypePath { ref path, .. }) => { + let path = &path.segments; + if path.len() == 1 { + let seg = &path[0]; + if seg.ident == "Vec" { + if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }) = + &seg.arguments + { + if args.len() == 1 { + let mut args = args.iter(); + let ok = args.next().unwrap(); + if let syn::GenericArgument::Type(ty) = ok { + let unboxed = unbox(ty.clone()); + // Wrap it back in a vec. + let wrapped = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::Path { + leading_colon: None, + segments: { + let mut segments = syn::punctuated::Punctuated::new(); + segments.push_value(syn::PathSegment { + ident: syn::Ident::new("Vec", proc_macro2::Span::call_site()), + arguments: syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { + colon2_token: None, + lt_token: syn::token::Lt::default(), + args: { + let mut args = syn::punctuated::Punctuated::new(); + args.push_value(syn::GenericArgument::Type(unboxed)); + args + }, + gt_token: syn::token::Gt::default(), + }, + ), + }); + segments + }, + }, + }); + return wrapped; + } + } + } + } + } + } + _ => { + return t; + } + } + t +} diff --git a/src/wasm-lib/derive-docs/tests/array.gen b/src/wasm-lib/derive-docs/tests/array.gen index a06729cf6a..0fe3d1de7e 100644 --- a/src/wasm-lib/derive-docs/tests/array.gen +++ b/src/wasm-lib/derive-docs/tests/array.gen @@ -1,11 +1,94 @@ +#[cfg(test)] +mod test_examples_show { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_show0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_show1() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Show {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] pub(crate) const Show: Show = Show {}; fn boxed_show( args: crate::std::Args, @@ -25,11 +108,11 @@ impl crate::docs::StdLibFn for Show { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +151,24 @@ impl crate::docs::StdLibFn for Show { false } + fn examples(&self) -> Vec { + let code_blocks = vec![ + "This is another code block.\nyes sirrr.\nshow", + "This is code.\nIt does other shit.\nshow", + ]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_show } @@ -77,6 +178,18 @@ impl crate::docs::StdLibFn for Show { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" show"] +#[doc = r""] +#[doc = r" ```"] +#[doc = r" This is another code block."] +#[doc = r" yes sirrr."] +#[doc = r" show"] +#[doc = r" ```"] fn inner_show(#[doc = r" The args to do shit to."] args: [f64; 2]) -> Result> { args } diff --git a/src/wasm-lib/derive-docs/tests/box.gen b/src/wasm-lib/derive-docs/tests/box.gen index 84f1f92624..ccdcbbdc3c 100644 --- a/src/wasm-lib/derive-docs/tests/box.gen +++ b/src/wasm-lib/derive-docs/tests/box.gen @@ -1,11 +1,54 @@ +#[cfg(test)] +mod test_examples_show { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_show0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Show {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] pub(crate) const Show: Show = Show {}; fn boxed_show( args: crate::std::Args, @@ -25,11 +68,11 @@ impl crate::docs::StdLibFn for Show { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +111,21 @@ impl crate::docs::StdLibFn for Show { false } + fn examples(&self) -> Vec { + let code_blocks = vec!["This is code.\nIt does other shit.\nshow"]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_show } @@ -77,6 +135,12 @@ impl crate::docs::StdLibFn for Show { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" show"] fn inner_show(#[doc = r" The args to do shit to."] args: Box) -> Box { args } diff --git a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen new file mode 100644 index 0000000000..6e2c7f8de0 --- /dev/null +++ b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen @@ -0,0 +1,197 @@ +#[cfg(test)] +mod test_examples_my_func { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_my_func0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_my_func1() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + +#[allow(non_camel_case_types, missing_docs)] +#[doc = "Std lib function: myFunc\nThis is some function.\nIt does shit."] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] +#[ts(export)] +pub(crate) struct MyFunc {} + +#[allow(non_upper_case_globals, missing_docs)] +#[doc = "Std lib function: myFunc\nThis is some function.\nIt does shit."] +pub(crate) const MyFunc: MyFunc = MyFunc {}; +fn boxed_my_func( + args: crate::std::Args, +) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = anyhow::Result, + >, + >, +> { + Box::pin(my_func(args)) +} + +impl crate::docs::StdLibFn for MyFunc { + fn name(&self) -> String { + "myFunc".to_string() + } + + fn summary(&self) -> String { + "This is some function.".to_string() + } + + fn description(&self) -> String { + "It does shit.".to_string() + } + + fn tags(&self) -> Vec { + vec![] + } + + fn args(&self) -> Vec { + let mut settings = schemars::gen::SchemaSettings::openapi3(); + settings.inline_subschemas = true; + let mut generator = schemars::gen::SchemaGenerator::new(settings); + vec![crate::docs::StdLibFnArg { + name: "args".to_string(), + type_: "kittycad::types::InputFormat".to_string(), + schema: >::json_schema(&mut generator), + required: false, + }] + } + + fn return_value(&self) -> Option { + let mut settings = schemars::gen::SchemaSettings::openapi3(); + settings.inline_subschemas = true; + let mut generator = schemars::gen::SchemaGenerator::new(settings); + Some(crate::docs::StdLibFnArg { + name: "".to_string(), + type_: "[SketchGroup]".to_string(), + schema: >::json_schema(&mut generator), + required: true, + }) + } + + fn unpublished(&self) -> bool { + false + } + + fn deprecated(&self) -> bool { + false + } + + fn examples(&self) -> Vec { + let code_blocks = vec![ + "This is another code block.\nyes sirrr.\nmyFunc", + "This is code.\nIt does other shit.\nmyFunc", + ]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + + fn std_lib_fn(&self) -> crate::std::StdFn { + boxed_my_func + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" myFunc"] +#[doc = r""] +#[doc = r" ```"] +#[doc = r" This is another code block."] +#[doc = r" yes sirrr."] +#[doc = r" myFunc"] +#[doc = r" ```"] +fn inner_my_func( + #[doc = r" The args to do shit to."] args: Option, +) -> Result>> { + args +} diff --git a/src/wasm-lib/derive-docs/tests/doc_comment_with_code_on_ignored_function.gen b/src/wasm-lib/derive-docs/tests/doc_comment_with_code_on_ignored_function.gen new file mode 100644 index 0000000000..e3d00b5e03 --- /dev/null +++ b/src/wasm-lib/derive-docs/tests/doc_comment_with_code_on_ignored_function.gen @@ -0,0 +1,199 @@ +#[cfg(test)] +mod test_examples_import { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #[ignore] + async fn serial_test_example_import0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nimport"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #[ignore] + async fn serial_test_example_import1() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + +#[allow(non_camel_case_types, missing_docs)] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] +#[ts(export)] +pub(crate) struct Import {} + +#[allow(non_upper_case_globals, missing_docs)] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] +pub(crate) const Import: Import = Import {}; +fn boxed_import( + args: crate::std::Args, +) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = anyhow::Result, + >, + >, +> { + Box::pin(import(args)) +} + +impl crate::docs::StdLibFn for Import { + fn name(&self) -> String { + "import".to_string() + } + + fn summary(&self) -> String { + "This is some function.".to_string() + } + + fn description(&self) -> String { + "It does shit.".to_string() + } + + fn tags(&self) -> Vec { + vec![] + } + + fn args(&self) -> Vec { + let mut settings = schemars::gen::SchemaSettings::openapi3(); + settings.inline_subschemas = true; + let mut generator = schemars::gen::SchemaGenerator::new(settings); + vec![crate::docs::StdLibFnArg { + name: "args".to_string(), + type_: "kittycad::types::InputFormat".to_string(), + schema: >::json_schema(&mut generator), + required: false, + }] + } + + fn return_value(&self) -> Option { + let mut settings = schemars::gen::SchemaSettings::openapi3(); + settings.inline_subschemas = true; + let mut generator = schemars::gen::SchemaGenerator::new(settings); + Some(crate::docs::StdLibFnArg { + name: "".to_string(), + type_: "[SketchGroup]".to_string(), + schema: >::json_schema(&mut generator), + required: true, + }) + } + + fn unpublished(&self) -> bool { + false + } + + fn deprecated(&self) -> bool { + false + } + + fn examples(&self) -> Vec { + let code_blocks = vec![ + "This is another code block.\nyes sirrr.\nimport", + "This is code.\nIt does other shit.\nimport", + ]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + + fn std_lib_fn(&self) -> crate::std::StdFn { + boxed_import + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" import"] +#[doc = r""] +#[doc = r" ```"] +#[doc = r" This is another code block."] +#[doc = r" yes sirrr."] +#[doc = r" import"] +#[doc = r" ```"] +fn inner_import( + #[doc = r" The args to do shit to."] args: Option, +) -> Result>> { + args +} diff --git a/src/wasm-lib/derive-docs/tests/lineTo.gen b/src/wasm-lib/derive-docs/tests/lineTo.gen index ffc78c18c7..7746131e9e 100644 --- a/src/wasm-lib/derive-docs/tests/lineTo.gen +++ b/src/wasm-lib/derive-docs/tests/lineTo.gen @@ -1,11 +1,94 @@ +#[cfg(test)] +mod test_examples_line_to { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_line_to0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_line_to1() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: lineTo"] +#[doc = "Std lib function: lineTo\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct LineTo {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: lineTo"] +#[doc = "Std lib function: lineTo\nThis is some function.\nIt does shit."] pub(crate) const LineTo: LineTo = LineTo {}; fn boxed_line_to( args: crate::std::Args, @@ -25,11 +108,11 @@ impl crate::docs::StdLibFn for LineTo { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -76,6 +159,24 @@ impl crate::docs::StdLibFn for LineTo { false } + fn examples(&self) -> Vec { + let code_blocks = vec![ + "This is another code block.\nyes sirrr.\nlineTo", + "This is code.\nIt does other shit.\nlineTo", + ]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_line_to } @@ -85,6 +186,18 @@ impl crate::docs::StdLibFn for LineTo { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" lineTo"] +#[doc = r""] +#[doc = r" ```"] +#[doc = r" This is another code block."] +#[doc = r" yes sirrr."] +#[doc = r" lineTo"] +#[doc = r" ```"] fn inner_line_to( data: LineToData, sketch_group: SketchGroup, diff --git a/src/wasm-lib/derive-docs/tests/min.gen b/src/wasm-lib/derive-docs/tests/min.gen index 3bfac9c7b9..94e7396137 100644 --- a/src/wasm-lib/derive-docs/tests/min.gen +++ b/src/wasm-lib/derive-docs/tests/min.gen @@ -1,11 +1,94 @@ +#[cfg(test)] +mod test_examples_min { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_min0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_min1() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: min"] +#[doc = "Std lib function: min\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Min {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: min"] +#[doc = "Std lib function: min\nThis is some function.\nIt does shit."] pub(crate) const Min: Min = Min {}; fn boxed_min( args: crate::std::Args, @@ -25,11 +108,11 @@ impl crate::docs::StdLibFn for Min { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +151,24 @@ impl crate::docs::StdLibFn for Min { false } + fn examples(&self) -> Vec { + let code_blocks = vec![ + "This is another code block.\nyes sirrr.\nmin", + "This is code.\nIt does other shit.\nmin", + ]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_min } @@ -77,6 +178,18 @@ impl crate::docs::StdLibFn for Min { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" min"] +#[doc = r""] +#[doc = r" ```"] +#[doc = r" This is another code block."] +#[doc = r" yes sirrr."] +#[doc = r" min"] +#[doc = r" ```"] fn inner_min(#[doc = r" The args to do shit to."] args: Vec) -> f64 { let mut min = std::f64::MAX; for arg in args.iter() { diff --git a/src/wasm-lib/derive-docs/tests/option.gen b/src/wasm-lib/derive-docs/tests/option.gen index dce8d619d6..618b66110f 100644 --- a/src/wasm-lib/derive-docs/tests/option.gen +++ b/src/wasm-lib/derive-docs/tests/option.gen @@ -1,11 +1,54 @@ +#[cfg(test)] +mod test_examples_show { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_show0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Show {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] pub(crate) const Show: Show = Show {}; fn boxed_show( args: crate::std::Args, @@ -25,11 +68,11 @@ impl crate::docs::StdLibFn for Show { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +111,21 @@ impl crate::docs::StdLibFn for Show { false } + fn examples(&self) -> Vec { + let code_blocks = vec!["This is code.\nIt does other shit.\nshow"]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_show } @@ -77,6 +135,12 @@ impl crate::docs::StdLibFn for Show { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" show"] fn inner_show(#[doc = r" The args to do shit to."] args: Option) -> Result> { args } diff --git a/src/wasm-lib/derive-docs/tests/option_input_format.gen b/src/wasm-lib/derive-docs/tests/option_input_format.gen index a15f62310d..a542aee021 100644 --- a/src/wasm-lib/derive-docs/tests/option_input_format.gen +++ b/src/wasm-lib/derive-docs/tests/option_input_format.gen @@ -1,11 +1,55 @@ +#[cfg(test)] +mod test_examples_import { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #[ignore] + async fn serial_test_example_import0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: import"] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Import {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: import"] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] pub(crate) const Import: Import = Import {}; fn boxed_import( args: crate::std::Args, @@ -25,11 +69,11 @@ impl crate::docs::StdLibFn for Import { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +112,21 @@ impl crate::docs::StdLibFn for Import { false } + fn examples(&self) -> Vec { + let code_blocks = vec!["This is code.\nIt does other shit.\nimport"]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_import } @@ -77,6 +136,12 @@ impl crate::docs::StdLibFn for Import { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" import"] fn inner_import( #[doc = r" The args to do shit to."] args: Option, ) -> Result> { diff --git a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen index f2e72e8786..1c465cffcc 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen @@ -1,11 +1,55 @@ +#[cfg(test)] +mod test_examples_import { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #[ignore] + async fn serial_test_example_import0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: import"] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Import {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: import"] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] pub(crate) const Import: Import = Import {}; fn boxed_import( args: crate::std::Args, @@ -25,11 +69,11 @@ impl crate::docs::StdLibFn for Import { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +112,21 @@ impl crate::docs::StdLibFn for Import { false } + fn examples(&self) -> Vec { + let code_blocks = vec!["This is code.\nIt does other shit.\nimport"]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_import } @@ -77,6 +136,12 @@ impl crate::docs::StdLibFn for Import { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" import"] fn inner_import( #[doc = r" The args to do shit to."] args: Option, ) -> Result>> { diff --git a/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen b/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen index 0c937f24f2..51e6d83d57 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen @@ -1,11 +1,55 @@ +#[cfg(test)] +mod test_examples_import { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #[ignore] + async fn serial_test_example_import0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: import"] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Import {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: import"] +#[doc = "Std lib function: import\nThis is some function.\nIt does shit."] pub(crate) const Import: Import = Import {}; fn boxed_import( args: crate::std::Args, @@ -25,11 +69,11 @@ impl crate::docs::StdLibFn for Import { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +112,21 @@ impl crate::docs::StdLibFn for Import { false } + fn examples(&self) -> Vec { + let code_blocks = vec!["This is code.\nIt does other shit.\nimport"]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_import } @@ -77,6 +136,12 @@ impl crate::docs::StdLibFn for Import { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" import"] fn inner_import( #[doc = r" The args to do shit to."] args: Option, ) -> Result> { diff --git a/src/wasm-lib/derive-docs/tests/show.gen b/src/wasm-lib/derive-docs/tests/show.gen index 71c54d7b90..821a168cc0 100644 --- a/src/wasm-lib/derive-docs/tests/show.gen +++ b/src/wasm-lib/derive-docs/tests/show.gen @@ -1,11 +1,54 @@ +#[cfg(test)] +mod test_examples_show { + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn serial_test_example_show0() { + let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),); + let http_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)); + let ws_client = reqwest::Client::builder() + .user_agent(user_agent) + .timeout(std::time::Duration::from_secs(600)) + .connect_timeout(std::time::Duration::from_secs(60)) + .connection_verbose(true) + .tcp_keepalive(std::time::Duration::from_secs(600)) + .http1_only(); + let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set"); + let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client); + let ws = client + .modeling() + .commands_ws(None, None, None, None, None, Some(false)) + .await + .unwrap(); + let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut mem: crate::executor::ProgramMemory = Default::default(); + let ctx = crate::executor::ExecutorContext { + engine: std::sync::Arc::new(Box::new( + crate::engine::conn::EngineConnection::new(ws) + .await + .unwrap(), + )), + fs: crate::fs::FileManager::new(), + stdlib: std::sync::Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; + crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) + .await + .unwrap(); + } +} + #[allow(non_camel_case_types, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)] #[ts(export)] pub(crate) struct Show {} #[allow(non_upper_case_globals, missing_docs)] -#[doc = "Std lib function: show"] +#[doc = "Std lib function: show\nThis is some function.\nIt does shit."] pub(crate) const Show: Show = Show {}; fn boxed_show( args: crate::std::Args, @@ -25,11 +68,11 @@ impl crate::docs::StdLibFn for Show { } fn summary(&self) -> String { - "".to_string() + "This is some function.".to_string() } fn description(&self) -> String { - "".to_string() + "It does shit.".to_string() } fn tags(&self) -> Vec { @@ -68,6 +111,21 @@ impl crate::docs::StdLibFn for Show { false } + fn examples(&self) -> Vec { + let code_blocks = vec!["This is code.\nIt does other shit.\nshow"]; + code_blocks + .iter() + .map(|cb| { + let tokens = crate::token::lexer(cb); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast().unwrap(); + let mut options: crate::ast::types::FormatOptions = Default::default(); + options.insert_final_newline = false; + program.recast(&options, 0) + }) + .collect::>() + } + fn std_lib_fn(&self) -> crate::std::StdFn { boxed_show } @@ -77,4 +135,10 @@ impl crate::docs::StdLibFn for Show { } } +#[doc = r" This is some function."] +#[doc = r" It does shit."] +#[doc = r""] +#[doc = r" This is code."] +#[doc = r" It does other shit."] +#[doc = r" show"] fn inner_show(#[doc = r" The args to do shit to."] _args: Vec) {} diff --git a/src/wasm-lib/kcl/Cargo.toml b/src/wasm-lib/kcl/Cargo.toml index 1d612170b4..2b0cb695ab 100644 --- a/src/wasm-lib/kcl/Cargo.toml +++ b/src/wasm-lib/kcl/Cargo.toml @@ -18,8 +18,8 @@ chrono = "0.4.35" clap = { version = "4.5.2", features = ["cargo", "derive", "env", "unicode"], optional = true } dashmap = "5.5.3" databake = { version = "0.1.7", features = ["derive"] } -derive-docs = { version = "0.1.10" } -#derive-docs = { path = "../derive-docs" } +#derive-docs = { version = "0.1.11" } +derive-docs = { path = "../derive-docs" } futures = { version = "0.3.30" } gltf-json = "1.4.0" kittycad = { workspace = true } diff --git a/src/wasm-lib/kcl/src/ast/modify.rs b/src/wasm-lib/kcl/src/ast/modify.rs index e699e13565..98d155dc9b 100644 --- a/src/wasm-lib/kcl/src/ast/modify.rs +++ b/src/wasm-lib/kcl/src/ast/modify.rs @@ -1,12 +1,13 @@ +use std::sync::Arc; + use kittycad::types::{ModelingCmd, Point3D}; -use super::types::ConstraintLevel; use crate::{ ast::types::{ - ArrayExpression, CallExpression, FormatOptions, Literal, PipeExpression, PipeSubstitution, Program, - VariableDeclarator, + ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, PipeExpression, PipeSubstitution, + Program, VariableDeclarator, }, - engine::{EngineConnection, EngineManager}, + engine::EngineManager, errors::{KclError, KclErrorDetails}, executor::{Point2d, SourceRange}, }; @@ -27,7 +28,7 @@ const EPSILON: f64 = 0.015625; // or 2^-6 /// Update the AST to reflect the new state of the program after something like /// a move or a new line. pub async fn modify_ast_for_sketch( - engine: &mut EngineConnection, + engine: &Arc>, program: &mut Program, // The name of the sketch. sketch_name: &str, diff --git a/src/wasm-lib/kcl/src/docs.rs b/src/wasm-lib/kcl/src/docs.rs index 99b046cec3..43c126e249 100644 --- a/src/wasm-lib/kcl/src/docs.rs +++ b/src/wasm-lib/kcl/src/docs.rs @@ -30,6 +30,9 @@ pub struct StdLibFnData { pub unpublished: bool, /// If the function is deprecated. pub deprecated: bool, + /// Code examples. + /// These are tested and we know they compile and execute. + pub examples: Vec, } /// This struct defines a single argument to a stdlib function. @@ -105,6 +108,9 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync { /// If the function is deprecated. fn deprecated(&self) -> bool; + /// Any example code blocks. + fn examples(&self) -> Vec; + /// The function itself. fn std_lib_fn(&self) -> crate::std::StdFn; @@ -122,6 +128,7 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync { return_value: self.return_value(), unpublished: self.unpublished(), deprecated: self.deprecated(), + examples: self.examples(), }) } @@ -560,7 +567,7 @@ mod tests { #[test] fn test_deserialize_function() { - let some_function_string = r#"{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}}"#; + let some_function_string = r#"{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false, "examples": []}}"#; let some_function: crate::ast::types::Function = serde_json::from_str(some_function_string).unwrap(); assert_eq!( diff --git a/src/wasm-lib/kcl/src/engine/mod.rs b/src/wasm-lib/kcl/src/engine/mod.rs index 7faa785558..a5d1cee81e 100644 --- a/src/wasm-lib/kcl/src/engine/mod.rs +++ b/src/wasm-lib/kcl/src/engine/mod.rs @@ -1,38 +1,15 @@ //! Functions for managing engine communications. #[cfg(not(target_arch = "wasm32"))] -#[cfg(not(test))] #[cfg(feature = "engine")] pub mod conn; -#[cfg(not(target_arch = "wasm32"))] -#[cfg(not(test))] -#[cfg(feature = "engine")] -pub use conn::EngineConnection; - +pub mod conn_mock; #[cfg(target_arch = "wasm32")] -#[cfg(not(test))] #[cfg(feature = "engine")] pub mod conn_wasm; -#[cfg(target_arch = "wasm32")] -#[cfg(not(test))] -#[cfg(feature = "engine")] -pub use conn_wasm::EngineConnection; - -#[cfg(test)] -pub mod conn_mock; -#[cfg(test)] -pub use conn_mock::EngineConnection; - -#[cfg(not(feature = "engine"))] -#[cfg(not(test))] -pub mod conn_mock; -use anyhow::Result; -#[cfg(not(feature = "engine"))] -#[cfg(not(test))] -pub use conn_mock::EngineConnection; #[async_trait::async_trait] -pub trait EngineManager: Clone { +pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static { /// Send a modeling command and wait for the response message. async fn send_modeling_cmd( &self, diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 98bfc86f92..edc2a32946 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -13,7 +13,7 @@ use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange}; use crate::{ ast::types::{BodyItem, FunctionExpression, KclNone, Value}, - engine::{EngineConnection, EngineManager}, + engine::EngineManager, errors::{KclError, KclErrorDetails}, fs::FileManager, std::{FunctionKind, StdLib}, @@ -977,30 +977,19 @@ impl Default for PipeInfo { /// The executor context. #[derive(Debug, Clone)] pub struct ExecutorContext { - pub engine: EngineConnection, + pub engine: Arc>, pub fs: FileManager, pub stdlib: Arc, pub units: kittycad::types::UnitLength, } impl ExecutorContext { - /// Create a new default executor context. - #[cfg(test)] - pub async fn new(units: kittycad::types::UnitLength) -> Result { - Ok(Self { - engine: EngineConnection::new().await?, - fs: FileManager::new(), - stdlib: Arc::new(StdLib::new()), - units, - }) - } - /// Create a new default executor context. #[cfg(not(test))] #[cfg(not(target_arch = "wasm32"))] pub async fn new(ws: reqwest::Upgraded, units: kittycad::types::UnitLength) -> Result { Ok(Self { - engine: EngineConnection::new(ws).await?, + engine: Arc::new(Box::new(crate::engine::conn::EngineConnection::new(ws).await?)), fs: FileManager::new(), stdlib: Arc::new(StdLib::new()), units, @@ -1290,6 +1279,8 @@ fn assign_args_to_params( #[cfg(test)] mod tests { + use std::sync::Arc; + use pretty_assertions::assert_eq; use super::*; @@ -1300,7 +1291,12 @@ mod tests { let parser = crate::parser::Parser::new(tokens); let program = parser.ast()?; let mut mem: ProgramMemory = Default::default(); - let ctx = ExecutorContext::new(kittycad::types::UnitLength::Mm).await?; + let ctx = ExecutorContext { + engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)), + fs: crate::fs::FileManager::new(), + stdlib: Arc::new(crate::std::StdLib::new()), + units: kittycad::types::UnitLength::Mm, + }; let memory = execute(program, &mut mem, BodyType::Root, &ctx).await?; Ok(memory) diff --git a/src/wasm-lib/kcl/src/lsp/copilot/mod.rs b/src/wasm-lib/kcl/src/lsp/copilot/mod.rs index 2c69536946..c7cbe7b7ea 100644 --- a/src/wasm-lib/kcl/src/lsp/copilot/mod.rs +++ b/src/wasm-lib/kcl/src/lsp/copilot/mod.rs @@ -24,13 +24,12 @@ use tower_lsp::{ LanguageServer, }; +use self::types::{CopilotAcceptCompletionParams, CopilotCompletionTelemetry, CopilotRejectCompletionParams}; use crate::lsp::{ backend::Backend as _, copilot::types::{CopilotCompletionResponse, CopilotEditorInfo, CopilotLspCompletionParams, DocParams}, }; -use self::types::{CopilotAcceptCompletionParams, CopilotCompletionTelemetry, CopilotRejectCompletionParams}; - #[derive(Deserialize, Serialize, Debug)] pub struct Success { success: bool, diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index 70bf0a2126..eb798efb3d 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -20,6 +20,16 @@ pub async fn extrude(args: Args) -> Result { } /// Extrudes by a given amount. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line([0, 10], %) +/// |> line([10, 0], %) +/// |> line([0, -10], %) +/// |> close(%) +/// |> extrude(5, %) +/// ``` #[stdlib { name = "extrude" }] @@ -180,6 +190,18 @@ pub async fn get_extrude_wall_transform(args: Args) -> Result startProfileAt([0, 0], %) +/// |> line([0, 10], %) +/// |> line([10, 0], %) +/// |> line({to: [0, -10], tag: "surface"}, %) +/// |> close(%) +/// |> extrude(5, %) +/// +/// const transform = getExtrudeWallTransform('surface', box) +/// ``` #[stdlib { name = "getExtrudeWallTransform" }] diff --git a/src/wasm-lib/kcl/src/std/fillet.rs b/src/wasm-lib/kcl/src/std/fillet.rs index 4934c69ebf..9ee3a3ace0 100644 --- a/src/wasm-lib/kcl/src/std/fillet.rs +++ b/src/wasm-lib/kcl/src/std/fillet.rs @@ -44,6 +44,17 @@ pub async fn fillet(args: Args) -> Result { } /// Create fillets on tagged paths. +/// +/// ```no_run +/// const part001 = startSketchOn('XY') +/// |> startProfileAt([0,0], %) +/// |> line({to: [0, 10], tag: "thing"}, %) +/// |> line([10, 0], %) +/// |> line({to: [0, -10], tag: "thing2"}, %) +/// |> close(%) +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: ["thing", "thing2"]}, %) +/// ``` #[stdlib { name = "fillet", }] @@ -115,6 +126,17 @@ pub async fn get_opposite_edge(args: Args) -> Result { } /// Get the opposite edge to the edge given. +/// +/// ```no_run +/// const part001 = startSketchOn('XY') +/// |> startProfileAt([0,0], %) +/// |> line({to: [0, 10], tag: "thing"}, %) +/// |> line([10, 0], %) +/// |> line({to: [0, -10], tag: "thing2"}, %) +/// |> close(%) +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: ["thing", getOppositeEdge("thing", %)]}, %) +/// ``` #[stdlib { name = "getOppositeEdge", }] @@ -173,6 +195,17 @@ pub async fn get_next_adjacent_edge(args: Args) -> Result } /// Get the next adjacent edge to the edge given. +/// +/// ```no_run +/// const part001 = startSketchOn('XY') +/// |> startProfileAt([0,0], %) +/// |> line({to: [0, 10], tag: "thing"}, %) +/// |> line({to: [10, 0], tag: "thing1"}, %) +/// |> line({to: [0, -10], tag: "thing2"}, %) +/// |> close(%) +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: [getNextAdjacentEdge("thing", %)]}, %) +/// ``` #[stdlib { name = "getNextAdjacentEdge", }] @@ -240,6 +273,17 @@ pub async fn get_previous_adjacent_edge(args: Args) -> Result startProfileAt([0,0], %) +/// |> line({to: [0, 10], tag: "thing"}, %) +/// |> line({to: [10, 0], tag: "thing1"}, %) +/// |> line({to: [0, -10], tag: "thing2"}, %) +/// |> close(%) +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: [getPreviousAdjacentEdge("thing2", %)]}, %) +/// ``` #[stdlib { name = "getPreviousAdjacentEdge", }] diff --git a/src/wasm-lib/kcl/src/std/import.rs b/src/wasm-lib/kcl/src/std/import.rs index 39f151d175..e80c997b8c 100644 --- a/src/wasm-lib/kcl/src/std/import.rs +++ b/src/wasm-lib/kcl/src/std/import.rs @@ -127,6 +127,26 @@ pub async fn import(args: Args) -> Result { /// /// Import paths are relative to the current project directory. This only works in the desktop app /// not in browser. +/// +/// ```no_run +/// const model = import("thing.obj") +/// ``` +/// +/// ```no_run +/// const model = import("cube.obj", {type: "obj", units: "m"}) +/// ``` +/// +/// ```no_run +/// const model = import("my_model.gltf") +/// ``` +/// +/// ```no_run +/// const model = import("my_model.sldprt") +/// ``` +/// +/// ```no_run +/// const model = import("my_model.step") +/// ``` #[stdlib { name = "import", }] diff --git a/src/wasm-lib/kcl/src/std/math.rs b/src/wasm-lib/kcl/src/std/math.rs index 91bec37313..1b24513635 100644 --- a/src/wasm-lib/kcl/src/std/math.rs +++ b/src/wasm-lib/kcl/src/std/math.rs @@ -19,6 +19,10 @@ pub async fn cos(args: Args) -> Result { } /// Computes the sine of a number (in radians). +/// +/// ```no_run +/// const anotherVar = cos(2*pi()) +/// ``` #[stdlib { name = "cos", }] @@ -35,6 +39,10 @@ pub async fn sin(args: Args) -> Result { } /// Computes the sine of a number (in radians). +/// +/// ```no_run +/// const myVar = sin(2*pi()) +/// ``` #[stdlib { name = "sin", }] @@ -51,6 +59,10 @@ pub async fn tan(args: Args) -> Result { } /// Computes the tangent of a number (in radians). +/// +/// ```no_run +/// const myVar = tan(2*pi()) +/// ``` #[stdlib { name = "tan", }] @@ -66,6 +78,10 @@ pub async fn pi(args: Args) -> Result { } /// Return the value of `pi`. Archimedes’ constant (π). +/// +/// ```no_run +/// const myVar = pi() * 3.0 +/// ``` #[stdlib { name = "pi", }] @@ -82,6 +98,10 @@ pub async fn sqrt(args: Args) -> Result { } /// Computes the square root of a number. +/// +/// ```no_run +/// const myVar = sqrt(4) +/// ``` #[stdlib { name = "sqrt", }] @@ -98,6 +118,10 @@ pub async fn abs(args: Args) -> Result { } /// Computes the absolute value of a number. +/// +/// ```no_run +/// const myVar = abs(-4) +/// ``` #[stdlib { name = "abs", }] @@ -114,6 +138,10 @@ pub async fn floor(args: Args) -> Result { } /// Computes the largest integer less than or equal to a number. +/// +/// ```no_run +/// const myVar = floor(4.5) +/// ``` #[stdlib { name = "floor", }] @@ -130,6 +158,10 @@ pub async fn ceil(args: Args) -> Result { } /// Computes the smallest integer greater than or equal to a number. +/// +/// ```no_run +/// const myVar = ceil(4.5) +/// ``` #[stdlib { name = "ceil", }] @@ -146,6 +178,10 @@ pub async fn min(args: Args) -> Result { } /// Computes the minimum of the given arguments. +/// +/// ```no_run +/// const myVar = min(4, 5, 6) +/// ``` #[stdlib { name = "min", }] @@ -169,6 +205,10 @@ pub async fn max(args: Args) -> Result { } /// Computes the maximum of the given arguments. +/// +/// ```no_run +/// const myVar = max(4, 5, 6) +/// ``` #[stdlib { name = "max", }] @@ -206,6 +246,10 @@ pub async fn pow(args: Args) -> Result { } /// Computes the number to a power. +/// +/// ```no_run +/// const myVar = pow(4, 2) +/// ``` #[stdlib { name = "pow", }] @@ -222,6 +266,10 @@ pub async fn acos(args: Args) -> Result { } /// Computes the arccosine of a number (in radians). +/// +/// ```no_run +/// const myVar = acos(0.5) +/// ``` #[stdlib { name = "acos", }] @@ -238,6 +286,10 @@ pub async fn asin(args: Args) -> Result { } /// Computes the arcsine of a number (in radians). +/// +/// ```no_run +/// const myVar = asin(0.5) +/// ``` #[stdlib { name = "asin", }] @@ -254,6 +306,10 @@ pub async fn atan(args: Args) -> Result { } /// Computes the arctangent of a number (in radians). +/// +/// ```no_run +/// const myVar = atan(1.0) +/// ``` #[stdlib { name = "atan", }] @@ -291,6 +347,10 @@ pub async fn log(args: Args) -> Result { /// The result might not be correctly rounded owing to implementation /// details; `log2()` can produce more accurate results for base 2, /// and `log10()` can produce more accurate results for base 10. +/// +/// ```no_run +/// const myVar = log(4, 2) +/// ``` #[stdlib { name = "log", }] @@ -307,6 +367,10 @@ pub async fn log2(args: Args) -> Result { } /// Computes the base 2 logarithm of the number. +/// +/// ```no_run +/// const myVar = log2(4) +/// ``` #[stdlib { name = "log2", }] @@ -323,6 +387,10 @@ pub async fn log10(args: Args) -> Result { } /// Computes the base 10 logarithm of the number. +/// +/// ```no_run +/// const myVar = log10(4) +/// ``` #[stdlib { name = "log10", }] @@ -339,6 +407,10 @@ pub async fn ln(args: Args) -> Result { } /// Computes the natural logarithm of the number. +/// +/// ```no_run +/// const myVar = ln(4) +/// ``` #[stdlib { name = "ln", }] @@ -354,6 +426,10 @@ pub async fn e(args: Args) -> Result { } /// Return the value of Euler’s number `e`. +/// +/// ```no_run +/// const myVar = e() +/// ``` #[stdlib { name = "e", }] @@ -369,6 +445,10 @@ pub async fn tau(args: Args) -> Result { } /// Return the value of `tau`. The full circle constant (τ). Equal to 2π. +/// +/// ```no_run +/// const myVar = tau() +/// ``` #[stdlib { name = "tau", }] @@ -377,33 +457,41 @@ fn inner_tau() -> Result { } /// Converts a number from degrees to radians. -pub async fn deg_to_rad(args: Args) -> Result { +pub async fn to_radians(args: Args) -> Result { let num = args.get_number()?; - let result = inner_deg_to_rad(num)?; + let result = inner_to_radians(num)?; args.make_user_val_from_f64(result) } /// Converts a number from degrees to radians. +/// +/// ```no_run +/// const myVar = toRadians(180) +/// ``` #[stdlib { - name = "to_radians", + name = "toRadians", }] -fn inner_deg_to_rad(num: f64) -> Result { +fn inner_to_radians(num: f64) -> Result { Ok(num.to_radians()) } /// Converts a number from radians to degrees. -pub async fn rad_to_deg(args: Args) -> Result { +pub async fn to_degrees(args: Args) -> Result { let num = args.get_number()?; - let result = inner_rad_to_deg(num)?; + let result = inner_to_degrees(num)?; args.make_user_val_from_f64(result) } /// Converts a number from radians to degrees. +/// +/// ```no_run +/// const myVar = toDegrees(2 * pi()) +/// ``` #[stdlib { - name = "to_degrees", + name = "toDegrees", }] -fn inner_rad_to_deg(num: f64) -> Result { +fn inner_to_degrees(num: f64) -> Result { Ok(num.to_degrees()) } diff --git a/src/wasm-lib/kcl/src/std/mod.rs b/src/wasm-lib/kcl/src/std/mod.rs index 1a21fa07e4..849e584b01 100644 --- a/src/wasm-lib/kcl/src/std/mod.rs +++ b/src/wasm-lib/kcl/src/std/mod.rs @@ -24,7 +24,6 @@ use serde::{Deserialize, Serialize}; use crate::{ ast::types::parse_json_number_as_f64, docs::StdLibFn, - engine::EngineManager, errors::{KclError, KclErrorDetails}, executor::{ ExecutorContext, ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SketchGroupSet, SketchSurface, SourceRange, @@ -760,6 +759,10 @@ pub async fn leg_length(args: Args) -> Result { } /// Returns the length of the given leg. +/// +/// ```no_run +/// legLen(5, 3) +/// ``` #[stdlib { name = "legLen", }] @@ -775,6 +778,10 @@ pub async fn leg_angle_x(args: Args) -> Result { } /// Returns the angle of the given leg for x. +/// +/// ```no_run +/// legAngX(5, 3) +/// ``` #[stdlib { name = "legAngX", }] @@ -790,6 +797,10 @@ pub async fn leg_angle_y(args: Args) -> Result { } /// Returns the angle of the given leg for y. +/// +/// ```no_run +/// legAngY(5, 3) +/// ``` #[stdlib { name = "legAngY", }] @@ -868,6 +879,16 @@ mod tests { fn_docs.push_str(&signature); fn_docs.push_str("\n```\n\n"); + if !internal_fn.examples().is_empty() { + fn_docs.push_str("#### Examples\n\n"); + + for example in internal_fn.examples() { + fn_docs.push_str("```kcl\n"); + fn_docs.push_str(&example); + fn_docs.push_str("\n```\n\n"); + } + } + fn_docs.push_str("#### Arguments\n\n"); for arg in internal_fn.args() { let (format, should_be_indented) = arg.get_type_string().unwrap(); diff --git a/src/wasm-lib/kcl/src/std/patterns.rs b/src/wasm-lib/kcl/src/std/patterns.rs index 2bf5b30313..1c213e4295 100644 --- a/src/wasm-lib/kcl/src/std/patterns.rs +++ b/src/wasm-lib/kcl/src/std/patterns.rs @@ -88,6 +88,12 @@ pub async fn pattern_linear_2d(args: Args) -> Result { } /// A linear pattern on a 2D sketch. +/// +/// ```no_run +/// const part = startSketchOn('XY') +/// |> circle([0,0], 2, %) +/// |> patternLinear2d({axis: [0,1], repetitions: 12, distance: 2}, %) +/// ``` #[stdlib { name = "patternLinear2d", }] @@ -131,6 +137,17 @@ pub async fn pattern_linear_3d(args: Args) -> Result { } /// A linear pattern on a 3D model. +/// +/// ```no_run +/// const part = startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line([0,1], %) +/// |> line([1, 0], %) +/// |> line([0, -1], %) +/// |> close(%) +/// |> extrude(1, %) +/// |> patternLinear3d({axis: [1, 0, 1], repetitions: 3, distance: 6}, %) +/// ``` #[stdlib { name = "patternLinear3d", }] @@ -296,6 +313,12 @@ pub async fn pattern_circular_2d(args: Args) -> Result { } /// A circular pattern on a 2D sketch. +/// +/// ```no_run +/// const part = startSketchOn('XY') +/// |> circle([0,0], 2, %) +/// |> patternCircular2d({center: [20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %) +/// ``` #[stdlib { name = "patternCircular2d", }] @@ -330,6 +353,17 @@ pub async fn pattern_circular_3d(args: Args) -> Result { } /// A circular pattern on a 3D model. +/// +/// ```no_run +/// const part = startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line([0,1], %) +/// |> line([1, 0], %) +/// |> line([0, -1], %) +/// |> close(%) +/// |> extrude(1, %) +/// |> patternCircular3d({axis: [1,1,0], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %) +/// ``` #[stdlib { name = "patternCircular3d", }] diff --git a/src/wasm-lib/kcl/src/std/segment.rs b/src/wasm-lib/kcl/src/std/segment.rs index f2bdc002a5..abf260525c 100644 --- a/src/wasm-lib/kcl/src/std/segment.rs +++ b/src/wasm-lib/kcl/src/std/segment.rs @@ -20,6 +20,16 @@ pub async fn segment_end_x(args: Args) -> Result { } /// Returns the segment end of x. +/// +/// ```no_run +/// startSketchOn("YZ") +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [5, 0], tag: "thing" }, %) +/// |> line([5, 5], %) +/// |> line([segEndX("thing", %), 5], %) +/// |> close(%) +/// |> extrude(5, %) +/// ``` #[stdlib { name = "segEndX", }] @@ -46,6 +56,16 @@ pub async fn segment_end_y(args: Args) -> Result { } /// Returns the segment end of y. +/// +/// ```no_run +/// startSketchOn("YZ") +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [5, 0], tag: "thing" }, %) +/// |> line([5, 5], %) +/// |> line([segEndY("thing", %), 5], %) +/// |> close(%) +/// |> extrude(5, %) +/// ``` #[stdlib { name = "segEndY", }] @@ -72,6 +92,16 @@ pub async fn last_segment_x(args: Args) -> Result { } /// Returns the last segment of x. +/// +/// ```no_run +/// startSketchOn("YZ") +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [5, 0], tag: "thing" }, %) +/// |> line([5, 5], %) +/// |> line([0, lastSegX(%)], %) +/// |> close(%) +/// |> extrude(5, %) +/// ``` #[stdlib { name = "lastSegX", }] @@ -102,6 +132,16 @@ pub async fn last_segment_y(args: Args) -> Result { } /// Returns the last segment of y. +/// +/// ```no_run +/// startSketchOn("YZ") +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [5, 0], tag: "thing" }, %) +/// |> line([5, 5], %) +/// |> line([0, lastSegY(%)], %) +/// |> close(%) +/// |> extrude(5, %) +/// ``` #[stdlib { name = "lastSegY", }] @@ -131,6 +171,16 @@ pub async fn segment_length(args: Args) -> Result { } /// Returns the length of the segment. +/// +/// ```no_run +/// startSketchOn("YZ") +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [5, 0], tag: "thing" }, %) +/// |> line([5, 5], %) +/// |> line([0, segLen("thing", %)], %) +/// |> close(%) +/// |> extrude(5, %) +/// ``` #[stdlib { name = "segLen", }] @@ -160,6 +210,18 @@ pub async fn segment_angle(args: Args) -> Result { } /// Returns the angle of the segment. +/// +/// ```no_run +/// const part001 = startSketchOn('XY') +/// |> startProfileAt([4.83, 12.56], %) +/// |> line([15.1, 2.48], %) +/// |> line({ to: [3.15, -9.85], tag: 'seg01' }, %) +/// |> line([-15.17, -4.1], %) +/// |> angledLine([segAng('seg01', %), 12.35], %) +/// |> line([-13.02, 10.03], %) +/// |> close(%) +/// |> extrude(4, %) +/// ``` #[stdlib { name = "segAng", }] @@ -188,6 +250,17 @@ pub async fn angle_to_match_length_x(args: Args) -> Result } /// Returns the angle to match the given length for x. +/// +/// ```no_run +/// const part001 = startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [1, 3.82], tag: 'seg01' }, %) +/// |> angledLineToX([ +/// -angleToMatchLengthX('seg01', 10, %), +/// 5 +/// ], %) +/// |> close(%) +/// ``` #[stdlib { name = "angleToMatchLengthX", }] @@ -243,6 +316,17 @@ pub async fn angle_to_match_length_y(args: Args) -> Result } /// Returns the angle to match the given length for y. +/// +/// ```no_run +/// const part001 = startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line({ to: [1, 3.82], tag: 'seg01' }, %) +/// |> angledLineToX([ +/// -angleToMatchLengthY('seg01', 10, %), +/// 5 +/// ], %) +/// |> close(%) +/// ``` #[stdlib { name = "angleToMatchLengthY", }] diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs index 6bd3768537..524de0afa0 100644 --- a/src/wasm-lib/kcl/src/std/shapes.rs +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -128,6 +128,10 @@ impl StdLibFn for Circle { false } + fn examples(&self) -> Vec { + vec![] + } + fn std_lib_fn(&self) -> crate::std::StdFn { todo!() } diff --git a/src/wasm-lib/kcl/src/std/sketch.rs b/src/wasm-lib/kcl/src/std/sketch.rs index 43ab034021..757b129b01 100644 --- a/src/wasm-lib/kcl/src/std/sketch.rs +++ b/src/wasm-lib/kcl/src/std/sketch.rs @@ -48,6 +48,30 @@ pub async fn line_to(args: Args) -> Result { } /// Draw a line to a point. +/// +/// ```no_run +/// fn rectShape = (pos, w, l) => { +/// const rr = startSketchOn('YZ') +/// |> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %) +/// |> lineTo({ +/// to: [pos[0] + w / 2, pos[1] - (l / 2)], +/// tag: "edge1" +/// }, %) +/// |> lineTo({ +/// to: [pos[0] + w / 2, pos[1] + l / 2], +/// tag: "edge2" +/// }, %) +/// |> lineTo({ +/// to: [pos[0] - (w / 2), pos[1] + l / 2], +/// tag: "edge3" +/// }, %) +/// |> close(%, "edge4") +/// return rr +/// } +/// +/// // Create the mounting plate extrusion, holes, and fillets +/// const part = rectShape([0, 0], 20, 20) +/// ``` #[stdlib { name = "lineTo", }] @@ -127,6 +151,18 @@ pub async fn x_line_to(args: Args) -> Result { } /// Draw a line to a point on the x-axis. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> xLineTo({ +/// to: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "xLineTo", }] @@ -156,6 +192,19 @@ pub async fn y_line_to(args: Args) -> Result { } /// Draw a line to a point on the y-axis. +/// +/// ```no_run +/// startSketchOn('XZ') +/// |> startProfileAt([0, 0], %) +/// |> yLineTo({ +/// to: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: ["edge2"]}, %) +/// ``` #[stdlib { name = "yLineTo", }] @@ -200,6 +249,15 @@ pub async fn line(args: Args) -> Result { } /// Draw a line. +/// +/// ```no_run +/// startSketchOn('-XY') +/// |> startProfileAt([0, 0], %) +/// |> line([10, 10], %) +/// |> line({to: [20, 10], tag: "edge1"}, %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "line", }] @@ -278,6 +336,15 @@ pub async fn x_line(args: Args) -> Result { } /// Draw a line on the x-axis. +/// +/// ```no_run +/// startSketchOn('YZ') +/// |> startProfileAt([0, 0], %) +/// |> xLine(10, %) +/// |> line([10, 10], %) +/// |> close(%, "edge1") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "xLine", }] @@ -304,6 +371,15 @@ pub async fn y_line(args: Args) -> Result { } /// Draw a line on the y-axis. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> yLine(10, %) +/// |> line([10, 10], %) +/// |> close(%, "edge1") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "yLine", }] @@ -358,6 +434,20 @@ pub async fn angled_line(args: Args) -> Result { } /// Draw an angled line. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> angledLine({ +/// angle: 45, +/// length: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> line([0, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "angledLine", }] @@ -429,6 +519,20 @@ pub async fn angled_line_of_x_length(args: Args) -> Result } /// Draw an angled line of a given x length. +/// +/// ```no_run +/// startSketchOn('XZ') +/// |> startProfileAt([0, 0], %) +/// |> angledLineOfXLength({ +/// angle: 45, +/// length: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> line([0, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "angledLineOfXLength", }] @@ -486,6 +590,21 @@ pub async fn angled_line_to_x(args: Args) -> Result { } /// Draw an angled line to a given x coordinate. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> angledLineToX({ +/// angle: 45, +/// to: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> line([0, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: ["edge1"]}, %) +/// ``` #[stdlib { name = "angledLineToX", }] @@ -518,6 +637,21 @@ pub async fn angled_line_of_y_length(args: Args) -> Result } /// Draw an angled line of a given y length. +/// +/// ```no_run +/// startSketchOn('YZ') +/// |> startProfileAt([0, 0], %) +/// |> angledLineOfYLength({ +/// angle: 45, +/// length: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> line([0, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// |> fillet({radius: 2, tags: ["edge1"]}, %) +/// ``` #[stdlib { name = "angledLineOfYLength", }] @@ -547,6 +681,20 @@ pub async fn angled_line_to_y(args: Args) -> Result { } /// Draw an angled line to a given y coordinate. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> angledLineToY({ +/// angle: 45, +/// to: 10, +/// tag: "edge1" +/// }, %) +/// |> line([10, 10], %) +/// |> line([0, 10], %) +/// |> close(%, "edge2") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "angledLineToY", }] @@ -593,6 +741,22 @@ pub async fn angled_line_that_intersects(args: Args) -> Result startProfileAt([0, 0], %) +/// |> lineTo({to:[2, 2], tag: "yo"}, %) +/// |> lineTo([3, 1], %) +/// |> angledLineThatIntersects({ +/// angle: 180, +/// intersectTag: 'yo', +/// offset: 12, +/// tag: "yo2" +/// }, %) +/// |> line([4, 0], %) +/// |> close(%, "yo3") +/// |> extrude(10, %) +/// ``` #[stdlib { name = "angledLineThatIntersects", }] @@ -641,6 +805,11 @@ pub async fn start_sketch_at(args: Args) -> Result { } /// Start a sketch at a given point on the 'XY' plane. +/// +/// ```no_run +/// startSketchAt([0, 0]) +/// |> line([10, 10], %) +/// ``` #[stdlib { name = "startSketchAt", }] @@ -785,6 +954,37 @@ pub async fn start_sketch_on(args: Args) -> Result { } /// Start a sketch on a specific plane or face. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line([10, 10], %) +/// |> line({to: [20, 10], tag: "edge1"}, %) +/// |> close(%, "edge2") +/// ``` +/// +/// ```no_run +/// fn cube = (pos, scale) => { +/// const sg = startSketchOn('XY') +/// |> startProfileAt(pos, %) +/// |> line([0, scale], %) +/// |> line([scale, 0], %) +/// |> line([0, -scale], %) +/// |> close(%) +/// |> extrude(scale, %) +/// +/// return sg +/// } +/// +/// const box = cube([0,0], 20) +/// +/// const part001 = startSketchOn(box, "start") +/// |> startProfileAt([0, 0], %) +/// |> line([10, 10], %) +/// |> line({to: [20, 10], tag: "edge1"}, %) +/// |> close(%) +/// |> extrude(20, %) +/// ``` #[stdlib { name = "startSketchOn", }] @@ -1011,6 +1211,12 @@ pub async fn start_profile_at(args: Args) -> Result { } /// Start a profile at a given point. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line([10, 10], %) +/// ``` #[stdlib { name = "startProfileAt", }] @@ -1081,6 +1287,22 @@ pub async fn close(args: Args) -> Result { } /// Close the current sketch. +/// +/// ```no_run +/// startSketchOn('XZ') +/// |> startProfileAt([0, 0], %) +/// |> line([10, 10], %) +/// |> line([10, 0], %) +/// |> close(%) +/// ``` +/// +/// ```no_run +/// startSketchOn('YZ') +/// |> startProfileAt([0, 0], %) +/// |> line([10, 10], %) +/// |> line([10, 0], %) +/// |> close(%, "edge1") +/// ``` #[stdlib { name = "close", }] @@ -1165,6 +1387,18 @@ pub async fn arc(args: Args) -> Result { } /// Draw an arc. +/// +/// ```no_run +/// startSketchOn('-YZ') +/// |> startProfileAt([0, 0], %) +/// |> arc({ +/// angle_start: 0, +/// angle_end: 360, +/// radius: 10, +/// tag: "edge1" +/// }, %) +/// |> extrude(10, %) +/// ``` #[stdlib { name = "arc", }] @@ -1259,6 +1493,19 @@ pub async fn tangential_arc(args: Args) -> Result { } /// Draw an arc. +/// +/// ```no_run +/// startSketchOn('-YZ') +/// |> startProfileAt([0, 0], %) +/// |> line({to: [10, 10], tag: "edge0"}, %) +/// |> tangentialArc({ +/// radius: 10, +/// offset: 90, +/// tag: "edge1" +/// }, %) +/// |> close(%) +/// |> extrude(10, %) +/// ``` #[stdlib { name = "tangentialArc", }] @@ -1373,6 +1620,14 @@ pub async fn tangential_arc_to(args: Args) -> Result { } /// Draw an arc. +/// +/// ```no_run +/// startSketchOn('-YZ') +/// |> startProfileAt([0, 0], %) +/// |> line({to: [10, 10], tag: "edge0"}, %) +/// |> tangentialArcTo([10, 0], %) +/// |> close(%) +/// ``` #[stdlib { name = "tangentialArcTo", }] @@ -1445,6 +1700,19 @@ pub async fn bezier_curve(args: Args) -> Result { } /// Draw a bezier curve. +/// +/// ```no_run +/// startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> bezierCurve({ +/// to: [10, 10], +/// control1: [5, 0], +/// control2: [5, 10], +/// tag: "edge1" +/// }, %) +/// |> close(%) +/// |> extrude(10, %) +/// ``` #[stdlib { name = "bezierCurve", }] @@ -1514,6 +1782,18 @@ pub async fn hole(args: Args) -> Result { } /// Use a sketch to cut a hole in another sketch. +/// +/// ```no_run +/// const square = startSketchOn('XY') +/// |> startProfileAt([0, 0], %) +/// |> line([0, 10], %) +/// |> line([10, 0], %) +/// |> line([0, -10], %) +/// |> close(%) +/// |> hole(circle([2, 2], .5, startSketchOn('XY')), %) +/// |> hole(circle([2, 8], .5, startSketchOn('XY')), %) +/// |> extrude(2, %) +/// ``` #[stdlib { name = "hole", }] diff --git a/src/wasm-lib/src/wasm.rs b/src/wasm-lib/src/wasm.rs index f1fc5aac56..f099c12433 100644 --- a/src/wasm-lib/src/wasm.rs +++ b/src/wasm-lib/src/wasm.rs @@ -7,6 +7,7 @@ use std::{ use futures::stream::TryStreamExt; use gloo_utils::format::JsValueSerdeExt; +use kcl_lib::engine::EngineManager; use tower_lsp::{LspService, Server}; use wasm_bindgen::prelude::*; @@ -27,12 +28,12 @@ pub async fn execute_wasm( let mut mem: kcl_lib::executor::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?; let units = kittycad::types::UnitLength::from_str(units).map_err(|e| e.to_string())?; - let engine = kcl_lib::engine::EngineConnection::new(engine_manager) + let engine = kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager) .await .map_err(|e| format!("{:?}", e))?; let fs = kcl_lib::fs::FileManager::new(fs_manager); let ctx = ExecutorContext { - engine, + engine: Arc::new(Box::new(engine)), fs, stdlib: std::sync::Arc::new(kcl_lib::std::StdLib::new()), units, @@ -62,12 +63,14 @@ pub async fn modify_ast_for_sketch_wasm( let plane: kcl_lib::executor::PlaneType = serde_json::from_str(plane_type).map_err(|e| e.to_string())?; - let mut engine = kcl_lib::engine::EngineConnection::new(manager) - .await - .map_err(|e| format!("{:?}", e))?; + let engine: Arc> = Arc::new(Box::new( + kcl_lib::engine::conn_wasm::EngineConnection::new(manager) + .await + .map_err(|e| format!("{:?}", e))?, + )); let _ = kcl_lib::ast::modify::modify_ast_for_sketch( - &mut engine, + &engine, &mut program, sketch_name, plane, diff --git a/src/wasm-lib/tests/executor/main.rs b/src/wasm-lib/tests/executor/main.rs index 6dea4b5767..1fb6bda8db 100644 --- a/src/wasm-lib/tests/executor/main.rs +++ b/src/wasm-lib/tests/executor/main.rs @@ -1,5 +1,4 @@ use anyhow::Result; -use kcl_lib::engine::EngineManager; /// Executes a kcl program and takes a snapshot of the result. /// This returns the bytes of the snapshot. diff --git a/src/wasm-lib/tests/modify/main.rs b/src/wasm-lib/tests/modify/main.rs index 49054ebe94..cd2dc8a8ad 100644 --- a/src/wasm-lib/tests/modify/main.rs +++ b/src/wasm-lib/tests/modify/main.rs @@ -1,7 +1,6 @@ use anyhow::Result; use kcl_lib::{ ast::{modify::modify_ast_for_sketch, types::Program}, - engine::EngineManager, executor::{ExecutorContext, MemoryItem, PlaneType, SourceRange}, }; use kittycad::types::{ModelingCmd, Point3D}; @@ -105,9 +104,9 @@ async fn serial_test_modify_sketch_part001() { name ); - let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -130,9 +129,9 @@ async fn serial_test_modify_sketch_part002() { name ); - let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -157,9 +156,9 @@ async fn serial_test_modify_close_sketch() { name ); - let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -183,9 +182,9 @@ async fn serial_test_modify_line_to_close_sketch() { name ); - let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -220,9 +219,9 @@ const {} = startSketchOn("XY") name ); - let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let result = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id).await; + let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id).await; assert!(result.is_err()); assert_eq!( @@ -245,9 +244,9 @@ async fn serial_test_modify_line_should_close_sketch() { name ); - let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) .await .unwrap();