diff --git a/package.json b/package.json index 9d15d02a5e..4fa3f93c9e 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ "nearley@2.20.1": "patches/nearley@2.20.1.patch" }, "overrides": { - "@docusaurus/core>webpack-dev-server": "5.2.2" + "@docusaurus/core>webpack-dev-server": "5.2.2", + "@yarnpkg/shell>cross-spawn": "7.0.6" } } } diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index bd67d15c8b..a06b23061a 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -38,7 +38,7 @@ "nearley": "2.20.1", "talon-snippets": "1.3.0", "uuid": "11.1.0", - "zod": "3.25.76" + "zod": "4.0.2" }, "devDependencies": { "@types/js-yaml": "4.0.9", diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts index 6c6757df22..5a5ac10188 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts @@ -13,11 +13,7 @@ import type { MutableQueryCapture } from "./QueryCapture"; * captures to nodes, because that will be done dynamically each time we run a * query. */ -type OperandListSchemaType = z.ZodType< - SchemaOutputType[], - z.ZodTypeDef, - SchemaInputType[] ->; +type OperandListSchemaType = z.ZodType; // These two types are used to allow us to infer the schema type from the // operator type. For example, if we have a type `NotType` that extends diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts index 147c2d8668..4288eb22b4 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts @@ -4,44 +4,52 @@ import { operandToString } from "./predicateToString"; export function constructZodErrorMessages( inputOperands: PredicateStep[], - error: z.ZodError, + error: z.ZodError>, ): string[] { - return error.errors + return error.issues .filter( // If the user has supplied a capture instead of a string, or vice versa, - // we'll get two errors instead of one; we prefer to show the more helpful + // we'll get two issues instead of one; we prefer to show the more helpful // one. - (error) => + (issue) => !( - error.code === "invalid_type" && - error.path.length === 2 && - (error.path[1] === "name" || error.path[1] === "value") + issue.code === "invalid_type" && + issue.path.length === 2 && + (issue.path[1] === "name" || issue.path[1] === "value") ), ) - .map((error) => getErrorMessage(inputOperands, error)); + .map((issue) => getErrorMessage(inputOperands, issue)); } -function getErrorMessage(inputOperands: PredicateStep[], error: z.ZodIssue) { - if (error.path.length === 0) { - if (error.code === "too_small") { +function getErrorMessage( + inputOperands: PredicateStep[], + issue: z.core.$ZodIssue, +) { + if (issue.path.length === 0) { + if (issue.code === "too_small") { return "Too few arguments"; - } else if (error.code === "too_big") { + } else if (issue.code === "too_big") { return "Too many arguments"; } - return error.message; + return issue.message; + } + + const argIndex = issue.path[0] as number; + + if (argIndex >= inputOperands.length) { + return "Too few arguments"; } - let message = error.message; + let message = issue.message; - if (error.code === "invalid_literal" && error.path[1] === "type") { + if (issue.code === "invalid_value" && issue.path[1] === "type") { message = - error.expected === "capture" + issue.values[0] === "capture" ? "Capture names must be prefixed with @" : "Expected string, but received capture"; } - const argIndex = error.path[0] as number; const operandString = operandToString(inputOperands[argIndex]); return `Error on argument ${argIndex} (\`${operandString}\`): ${message}`; } diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts index 0067a6856c..ed67b10d40 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/parsePredicates.test.ts @@ -32,6 +32,26 @@ const predicates: QueryPredicate[][] = [ ], }, + // (#is-nth-child? @statement 1 2) + // Error: too many orgs + { + operator: "is-nth-child?", + operands: [ + { + type: "capture", + name: "statement", + }, + { + type: "string", + value: "1", + }, + { + type: "string", + value: "2", + }, + ], + }, + // (#not-parent-type? statement foo) // Error: capture names must be prefixed with @ { @@ -95,19 +115,24 @@ const expectedErrors = [ { patternIdx: 0, predicateIdx: 2, + error: "Too many arguments", + }, + { + patternIdx: 0, + predicateIdx: 3, error: "Error on argument 0 (`statement`): Capture names must be prefixed with @", }, { patternIdx: 0, - predicateIdx: 3, + predicateIdx: 4, error: "Error on argument 1 (`hello`): Expected an integer", }, { + patternIdx: 0, + predicateIdx: 5, error: "Error on argument 2 (`@foo`): Expected string, but received capture", - patternIdx: 0, - predicateIdx: 4, }, ]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f4e379b10e..856d62a2ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,7 @@ settings: overrides: '@docusaurus/core>webpack-dev-server': 5.2.2 + '@yarnpkg/shell>cross-spawn': 7.0.6 patchedDependencies: '@shikijs/core': @@ -124,7 +125,7 @@ importers: version: 30.0.4 ts-jest: specifier: 29.4.0 - version: 29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.4)(@jest/types@30.0.1)(babel-jest@30.0.4(@babel/core@7.28.0))(esbuild@0.25.6)(jest-util@30.0.2)(jest@30.0.4(@types/node@24.0.12)(ts-node@10.9.2(@types/node@24.0.12)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.4)(@jest/types@30.0.1)(babel-jest@30.0.4(@babel/core@7.28.0))(esbuild@0.25.6)(jest-util@30.0.2)(jest@30.0.4(@types/node@24.0.12))(typescript@5.8.3) typescript: specifier: 5.8.3 version: 5.8.3 @@ -306,8 +307,8 @@ importers: specifier: 11.1.0 version: 11.1.0 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.0.2 + version: 4.0.2 devDependencies: '@types/js-yaml': specifier: 4.0.9 @@ -5139,10 +5140,6 @@ packages: engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} hasBin: true - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -10769,8 +10766,8 @@ packages: youtube-video-element@1.6.0: resolution: {integrity: sha512-yu6aMaSyyp8ygqNNnf01xjJdy7AaWwEhLlJ/XRdFteLP/djM7i7JOrK0INgjvBnv88dAtiW9PKZ0OQQzGFeydw==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.0.2: + resolution: {integrity: sha512-X2niJNY54MGam4L6Kj0AxeedeDIi/E5QFW0On2faSX5J4/pfLk1tW+cRMIMoojnCavn/u5W/kX17e1CSGnKMxA==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -15377,7 +15374,7 @@ snapshots: '@yarnpkg/parsers': 3.0.3 chalk: 3.0.0 clipanion: 4.0.0-rc.4(typanion@3.14.0) - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 fast-glob: 3.3.3 micromatch: 4.0.8 tslib: 2.8.1 @@ -16310,12 +16307,6 @@ snapshots: dependencies: cross-spawn: 7.0.6 - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -22327,7 +22318,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.4)(@jest/types@30.0.1)(babel-jest@30.0.4(@babel/core@7.28.0))(esbuild@0.25.6)(jest-util@30.0.2)(jest@30.0.4(@types/node@24.0.12)(ts-node@10.9.2(@types/node@24.0.12)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.4)(@jest/types@30.0.1)(babel-jest@30.0.4(@babel/core@7.28.0))(esbuild@0.25.6)(jest-util@30.0.2)(jest@30.0.4(@types/node@24.0.12))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -23137,6 +23128,6 @@ snapshots: youtube-video-element@1.6.0: {} - zod@3.25.76: {} + zod@4.0.2: {} zwitch@2.0.4: {}